upgrade cal
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "chrono-snap",
|
||||
"private": true,
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@@ -667,7 +667,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono-snap"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "chrono-snap"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
description = "An app to record screens and events"
|
||||
authors = ["you"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -227,3 +227,37 @@ pub fn get_overdue_reminders_count(path: &str, date: &str, minute: i32) -> anyho
|
||||
let count: i32 = stmt.query_row(params![date, minute], |row| row.get(0))?;
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct DayStatus {
|
||||
pub date: String,
|
||||
pub has_overdue: bool,
|
||||
pub has_upcoming: bool,
|
||||
}
|
||||
|
||||
pub fn get_reminders_by_month(path: &str, year_month: &str, today: &str, now_minute: i32) -> anyhow::Result<Vec<DayStatus>> {
|
||||
let conn = Connection::open(path)?;
|
||||
// 查找该月份内有提醒的所有日期
|
||||
let mut stmt = conn.prepare("
|
||||
SELECT date,
|
||||
MAX(CASE WHEN is_completed = 0 AND (date < ?2 OR (date = ?2 AND minute < ?3)) THEN 1 ELSE 0 END) as has_overdue,
|
||||
MAX(CASE WHEN is_completed = 0 AND (date > ?2 OR (date = ?2 AND minute >= ?3)) THEN 1 ELSE 0 END) as has_upcoming
|
||||
FROM reminders
|
||||
WHERE date LIKE ?1
|
||||
GROUP BY date
|
||||
")?;
|
||||
|
||||
let rows = stmt.query_map(params![format!("{}%", year_month), today, now_minute], |row| {
|
||||
Ok(DayStatus {
|
||||
date: row.get(0)?,
|
||||
has_overdue: row.get::<_, i32>(1)? == 1,
|
||||
has_upcoming: row.get::<_, i32>(2)? == 1,
|
||||
})
|
||||
})?;
|
||||
|
||||
let mut results = Vec::new();
|
||||
for row in rows {
|
||||
results.push(row?);
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
@@ -107,6 +107,13 @@ pub fn get_overdue_reminders_count(state: tauri::State<'_, AppState>, date: Stri
|
||||
crate::db::get_overdue_reminders_count(path, &date, minute).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_reminders_by_month(state: tauri::State<'_, AppState>, year_month: String, today: String, now_minute: i32) -> Result<Vec<crate::db::DayStatus>, String> {
|
||||
let path = state.db_path.lock().unwrap();
|
||||
let path = path.as_ref().ok_or("Database path not set")?;
|
||||
crate::db::get_reminders_by_month(path, &year_month, &today, now_minute).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn update_interval(state: tauri::State<'_, AppState>, seconds: u64) {
|
||||
state.capture_interval_secs.store(seconds, Ordering::SeqCst);
|
||||
|
||||
@@ -37,6 +37,7 @@ pub fn run() {
|
||||
engine::delete_reminder,
|
||||
engine::toggle_reminder,
|
||||
engine::get_overdue_reminders_count,
|
||||
engine::get_reminders_by_month,
|
||||
engine::write_file
|
||||
])
|
||||
.setup(|app| {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "ChronoSnap",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"identifier": "top.volan.chrono-snap",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
@@ -12,7 +12,7 @@
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "瞬影 - 时间记录 v0.2.2",
|
||||
"title": "瞬影 - 时间记录 v0.2.3",
|
||||
"width": 1760,
|
||||
"height": 1100
|
||||
}
|
||||
|
||||
33
src/App.vue
33
src/App.vue
@@ -14,7 +14,8 @@ import {
|
||||
retainDays, captureInterval,
|
||||
TIME_OFFSET_MINUTES, TOTAL_MINUTES, getTagColor, getTagName, mainTags, getSubTags,
|
||||
logicalMinutesToTime, logicalMinutesFromTime, formatDuration,
|
||||
loadTags, loadEvents, loadReminders, toISODate, refreshBadgeCount
|
||||
loadTags, loadEvents, loadReminders, toISODate, refreshBadgeCount,
|
||||
calendarStatus, loadCalendarStatus, currentCalendarMonthStr
|
||||
} from "./store";
|
||||
import { DBEvent, TimelineItem } from "./types";
|
||||
|
||||
@@ -90,7 +91,7 @@ const checkReminders = async (currentMin: number) => {
|
||||
|
||||
for (const r of dueReminders) {
|
||||
if (hasPermission) {
|
||||
sendNotification({ title: '', body: r.content });
|
||||
sendNotification({ title: '瞬影 提醒', body: r.content });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,7 +109,7 @@ const updateCurrentMinute = () => {
|
||||
} else {
|
||||
currentLogicalMinute.value = -1;
|
||||
}
|
||||
// 无论是否看今天,每分钟都尝试刷新全局徽章
|
||||
// 无论是否看今天,每分钟都尝试刷新全局徽章和日历状态点
|
||||
refreshBadgeCount();
|
||||
};
|
||||
|
||||
@@ -126,6 +127,13 @@ const startMinuteTimer = () => {
|
||||
}, msUntilNextMinute);
|
||||
};
|
||||
|
||||
watch(calendarMonth, (newMonth) => {
|
||||
const y = newMonth.getFullYear();
|
||||
const m = String(newMonth.getMonth() + 1).padStart(2, '0');
|
||||
currentCalendarMonthStr.value = `${y}-${m}`;
|
||||
loadCalendarStatus(currentCalendarMonthStr.value);
|
||||
}, { immediate: true });
|
||||
|
||||
watch(currentDate, () => {
|
||||
updateCurrentMinute();
|
||||
});
|
||||
@@ -392,8 +400,23 @@ const togglePause = async () => {
|
||||
</div>
|
||||
<div class="grid grid-cols-7 gap-1 text-center mb-2"><div v-for="d in ['一','二','三','四','五','六','日']" :key="d" class="text-[10px] font-bold text-text-sec">{{d}}</div></div>
|
||||
<div class="grid grid-cols-7 gap-1">
|
||||
<div v-for="(date, i) in calendarDays" :key="i" class="aspect-square flex items-center justify-center">
|
||||
<button v-if="date" @click="selectCalendarDate(date)" class="w-8 h-8 rounded-full text-xs font-medium transition-all" :class="date.toLocaleDateString('sv') === currentDate ? 'bg-[#007AFF] text-white font-bold' : 'hover:bg-bg-input text-main'">{{ date.getDate() }}</button>
|
||||
<div v-for="(date, i) in calendarDays" :key="i" class="aspect-square flex flex-col items-center justify-center relative">
|
||||
<button v-if="date" @click="selectCalendarDate(date)"
|
||||
class="w-8 h-8 rounded-full text-xs font-medium transition-all flex items-center justify-center relative"
|
||||
:class="[
|
||||
date.toLocaleDateString('sv') === getLogicDateStr()
|
||||
? 'bg-[#007AFF] text-white font-black shadow-[0_4px_12px_rgba(0,122,255,0.4)] ' + (date.toLocaleDateString('sv') === currentDate ? 'ring-2 ring-offset-2 ring-[#007AFF]' : '')
|
||||
: (date.toLocaleDateString('sv') === currentDate
|
||||
? 'bg-bg-input text-[#007AFF] font-black ring-1 ring-border-main shadow-sm'
|
||||
: 'hover:bg-bg-input text-main')
|
||||
]">
|
||||
{{ date.getDate() }}
|
||||
</button>
|
||||
<!-- Status Dots -->
|
||||
<div v-if="date && calendarStatus[date.toLocaleDateString('sv')]" class="absolute -bottom-1.5 flex gap-0.5">
|
||||
<div v-if="calendarStatus[date.toLocaleDateString('sv')].has_overdue" class="w-1.5 h-1.5 rounded-full bg-[#FF3B30] border-[1.5px] border-bg-card shadow-sm"></div>
|
||||
<div v-if="calendarStatus[date.toLocaleDateString('sv')].has_upcoming" class="w-1.5 h-1.5 rounded-full bg-[#007AFF] border-[1.5px] border-bg-card shadow-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -52,6 +52,7 @@ export const lockedImage = ref<TimelineItem | null>(null);
|
||||
export const tags = ref<Tag[]>([]);
|
||||
export const dayEvents = ref<DBEvent[]>([]);
|
||||
export const reminders = ref<Reminder[]>([]);
|
||||
export const calendarStatus = ref<Record<string, { has_overdue: boolean, has_upcoming: boolean }>>({});
|
||||
export const timelineImages = ref<TimelineItem[]>([]);
|
||||
export const refreshSignal = ref(0); // Counter to trigger dashboard refreshes
|
||||
|
||||
@@ -114,6 +115,33 @@ export const loadEvents = async () => {
|
||||
export const loadReminders = async () => {
|
||||
reminders.value = await invoke("get_reminders", { date: currentDate.value });
|
||||
refreshBadgeCount();
|
||||
refreshCalendarStatus(); // 提醒更新时也刷新日历状态
|
||||
};
|
||||
|
||||
export const loadCalendarStatus = async (yearMonth: string) => {
|
||||
if (!dbPath.value) return;
|
||||
try {
|
||||
const { date, minute } = getRealNowLogicalTime();
|
||||
const statuses: any[] = await invoke("get_reminders_by_month", {
|
||||
yearMonth,
|
||||
today: date,
|
||||
nowMinute: minute
|
||||
});
|
||||
const map: Record<string, any> = {};
|
||||
statuses.forEach(s => { map[s.date] = s; });
|
||||
calendarStatus.value = map;
|
||||
} catch (e) {
|
||||
console.error("Failed to load calendar status:", e);
|
||||
}
|
||||
};
|
||||
|
||||
// 当前显示的日历月份,用于自动刷新状态
|
||||
export const currentCalendarMonthStr = ref("");
|
||||
|
||||
export const refreshCalendarStatus = () => {
|
||||
if (currentCalendarMonthStr.value) {
|
||||
loadCalendarStatus(currentCalendarMonthStr.value);
|
||||
}
|
||||
};
|
||||
|
||||
export const overdueCount = ref(0);
|
||||
|
||||
Reference in New Issue
Block a user