修改了通知相关的功能,能够正常通知了。
This commit is contained in:
@@ -8,6 +8,8 @@ import { EventDb, LocalEvent } from '../common/EventDb';
|
||||
import { AppSettings } from '../common/AppSettings';
|
||||
import { DavClient } from '../common/DavClient';
|
||||
import { SyncEngine } from '../common/SyncEngine';
|
||||
import { ReminderService } from '../common/ReminderService';
|
||||
import { LogUtil } from '../common/LogUtil';
|
||||
|
||||
/** 可选的日历本 */
|
||||
class BookChoice {
|
||||
@@ -32,7 +34,7 @@ struct EventEditPage {
|
||||
@State statusMsg: string = '';
|
||||
@State isExisting: boolean = false;
|
||||
@State repeatMode: string = 'none'; // none|daily|workday|weekly|monthly|yearly|custom
|
||||
@State reminderMin: number = 0; // 提前提醒分钟数,0 = 不提醒
|
||||
@State reminderList: number[] = []; // 提前提醒分钟数(多选),空 = 不提醒
|
||||
@State pickerShow: boolean = false; // 开始/结束时间选择底部弹层(日期+时间一次选完)
|
||||
@State pickerIsEnd: boolean = false;
|
||||
@State pickDate: Date = new Date();
|
||||
@@ -77,16 +79,18 @@ struct EventEditPage {
|
||||
this.endMs = loaded.endTime;
|
||||
this.chosenKey = loaded.calKey;
|
||||
this.repeatMode = loaded.rrule !== '' ? this.modeFromRrule(loaded.rrule) : 'none';
|
||||
this.reminderMin = loaded.reminder;
|
||||
this.reminderList = loaded.reminders.length > 0 ? loaded.reminders
|
||||
: (loaded.reminder > 0 ? [loaded.reminder] : []);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 新建:默认时间 = 所选日期 9:00-10:00
|
||||
// 新建:日期取所选日(未选则今天),时间取当前时刻;结束 = 开始 + 1 小时
|
||||
const base: number = AppStorage.get<number>('pendingEventDate') ?? Date.now();
|
||||
const dayStart = new Date(new Date(base).getFullYear(), new Date(base).getMonth(),
|
||||
new Date(base).getDate()).getTime();
|
||||
this.startMs = dayStart + 9 * 3600000;
|
||||
this.endMs = dayStart + 10 * 3600000;
|
||||
const now = new Date();
|
||||
this.startMs = dayStart + now.getHours() * 3600000 + now.getMinutes() * 60000;
|
||||
this.endMs = this.startMs + 3600000;
|
||||
if (choices.length > 0) {
|
||||
this.chosenKey = choices[0].calKey;
|
||||
}
|
||||
@@ -179,9 +183,9 @@ struct EventEditPage {
|
||||
// 全天日程的结束存为"当天 23:59:59.999"(排他日期前 1 毫秒)
|
||||
this.endMs = this.allDay ? picked + 86399999 : picked;
|
||||
} else {
|
||||
const dur: number = Math.max(0, this.endMs - this.startMs);
|
||||
// 设定开始时间后,结束时间自动跟随 = 开始 + 1 小时
|
||||
this.startMs = picked;
|
||||
this.endMs = this.allDay ? picked + 86399999 : picked + dur;
|
||||
this.endMs = this.allDay ? picked + 86399999 : picked + 3600000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,7 +250,8 @@ struct EventEditPage {
|
||||
e.rrule = this.rruleForMode(this.repeatMode);
|
||||
}
|
||||
e.recurring = e.rrule !== '';
|
||||
e.reminder = this.reminderMin;
|
||||
e.reminders = EventDb.normalizeReminders(this.reminderList);
|
||||
e.reminder = e.reminders.length > 0 ? e.reminders[0] : 0;
|
||||
if (isNew) {
|
||||
e.uid = `syncal-${Date.now()}-${Math.floor(Math.random() * 1000000)}`;
|
||||
e.remotePath = encodeURIComponent(e.uid) + '.ics';
|
||||
@@ -265,11 +270,18 @@ struct EventEditPage {
|
||||
} else {
|
||||
await SyncEngine.settleLocalEvents(context);
|
||||
}
|
||||
LogUtil.write(`本地保存日程「${e.title}」提醒=${e.reminders.join('/')}分钟 重复=${e.rrule === '' ? '否' : e.rrule}`);
|
||||
// 立即刷新提醒(不等下一轮同步),保证刚保存的提醒马上生效
|
||||
try {
|
||||
await ReminderService.refreshReminders(context as common.UIAbilityContext);
|
||||
} catch (err) {
|
||||
// 刷新失败不影响保存
|
||||
}
|
||||
this.getUIContext().getPromptAction().showToast({ message: '日程已保存' });
|
||||
router.back();
|
||||
} catch (err) {
|
||||
const ex = err as BusinessError;
|
||||
console.error(`保存日程失败: ${ex.message}`);
|
||||
LogUtil.write(`保存日程流程失败(数据已存本地待重试):${ex.message}`);
|
||||
this.statusMsg = `保存失败:${ex.message}(已保存到本地,稍后同步会重试)`;
|
||||
// 数据仍在本地且带 dirty 标记,不会丢
|
||||
router.back();
|
||||
@@ -302,6 +314,13 @@ struct EventEditPage {
|
||||
} else {
|
||||
await SyncEngine.settleLocalEvents(context);
|
||||
}
|
||||
LogUtil.write(`本地删除日程「${this.event?.title ?? ''}」`);
|
||||
// 立即刷新提醒(取消已发布但日程已删的提醒)
|
||||
try {
|
||||
await ReminderService.refreshReminders(context as common.UIAbilityContext);
|
||||
} catch (err) {
|
||||
// 刷新失败不影响删除
|
||||
}
|
||||
this.getUIContext().getPromptAction().showToast({ message: '日程已删除' });
|
||||
router.back();
|
||||
} catch (err) {
|
||||
@@ -407,7 +426,7 @@ struct EventEditPage {
|
||||
|
||||
// 提醒
|
||||
Column({ space: 8 }) {
|
||||
Text('提醒')
|
||||
Text('提醒(可多选)')
|
||||
.fontSize(14)
|
||||
.fontColor($r('app.color.text_secondary'))
|
||||
Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Start }) {
|
||||
@@ -541,7 +560,8 @@ struct EventEditPage {
|
||||
.width('100%')
|
||||
.padding({ left: 8, right: 8, top: 10, bottom: 10 })
|
||||
.onClick(() => {
|
||||
this.openPicker(isStart);
|
||||
// 注意:timeRow 的参数是 isStart,openPicker 的参数是 isEnd,语义相反必须取反
|
||||
this.openPicker(!isStart);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -560,18 +580,42 @@ struct EventEditPage {
|
||||
})
|
||||
}
|
||||
|
||||
/** 提醒 chip 是否选中:0 = 不提醒(列表为空时选中);其余按是否已选 */
|
||||
private chipOn(minutes: number): boolean {
|
||||
if (minutes === 0) {
|
||||
return this.reminderList.length === 0;
|
||||
}
|
||||
return this.reminderList.includes(minutes);
|
||||
}
|
||||
|
||||
/** 切换提醒:0 = 清空全部;其余多选切换 */
|
||||
private toggleReminder(minutes: number): void {
|
||||
if (minutes === 0) {
|
||||
this.reminderList = [];
|
||||
return;
|
||||
}
|
||||
const list: number[] = [...this.reminderList];
|
||||
const idx: number = list.indexOf(minutes);
|
||||
if (idx >= 0) {
|
||||
list.splice(idx, 1);
|
||||
} else {
|
||||
list.push(minutes);
|
||||
}
|
||||
this.reminderList = list;
|
||||
}
|
||||
|
||||
@Builder
|
||||
reminderChip(minutes: number) {
|
||||
Text(this.reminderLabel(minutes))
|
||||
.fontSize(12)
|
||||
.fontColor(this.reminderMin === minutes
|
||||
.fontColor(this.chipOn(minutes)
|
||||
? $r('app.color.button_text') : $r('app.color.text_primary'))
|
||||
.padding({ left: 10, right: 10, top: 6, bottom: 6 })
|
||||
.borderRadius(14)
|
||||
.margin({ right: 8, bottom: 8 })
|
||||
.backgroundColor(this.reminderMin === minutes ? $r('app.color.brand') : $r('app.color.chip_off_bg'))
|
||||
.backgroundColor(this.chipOn(minutes) ? $r('app.color.brand') : $r('app.color.chip_off_bg'))
|
||||
.onClick(() => {
|
||||
this.reminderMin = minutes;
|
||||
this.toggleReminder(minutes);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -181,12 +181,18 @@ struct Index {
|
||||
});
|
||||
}
|
||||
this.autoSyncTimer = setInterval((): void => {
|
||||
// 应用内提醒模式(代理提醒配额为 0 的降级):每 15 秒检查到点的提醒
|
||||
const tickCtx = this.getUIContext().getHostContext();
|
||||
if (tickCtx !== undefined && !this.syncing) {
|
||||
ReminderService.tickReminders(tickCtx as common.UIAbilityContext);
|
||||
}
|
||||
const minutes: number = AppStorage.get<number>('syncIntervalMinutes') ?? 1;
|
||||
if (Date.now() - this.lastSyncTime < minutes * 60000) {
|
||||
return; // 未到设置的同步间隔
|
||||
}
|
||||
if (!this.syncing && this.accounts.length > 0) {
|
||||
this.lastSyncTime = Date.now();
|
||||
LogUtil.write(`自动同步触发(间隔 ${minutes} 分钟,账号 ${this.accounts.length} 个)`);
|
||||
this.syncAll(false);
|
||||
}
|
||||
}, 15000);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// 设置页:系统日历模式(仅显示/备份到 CalDAV)+ 混合显示开关 + 自动同步间隔 + 后台同步
|
||||
import { router } from '@kit.ArkUI';
|
||||
import { common } from '@kit.AbilityKit';
|
||||
import { pasteboard } from '@kit.BasicServicesKit';
|
||||
import { notificationManager } from '@kit.NotificationKit';
|
||||
import { AppSettings } from '../common/AppSettings';
|
||||
import { BackgroundSyncService } from '../common/BackgroundSyncService';
|
||||
import { AccountStore, DavAccount } from '../common/AccountStore';
|
||||
@@ -29,6 +31,10 @@ struct SettingsPage {
|
||||
@State allBooks: BackupTarget[] = []; // 全部 DAV 日历本(只读标记管理用)
|
||||
@State manualKeys: string[] = []; // 手动标记只读的 calKey
|
||||
@State mutedKeys: string[] = []; // 提醒静音的 calKey
|
||||
@State showLogSheet: boolean = false; // 同步日志查看弹层
|
||||
@State logText: string = '';
|
||||
@State notifyEnabled: boolean = true; // 通知权限状态(提醒依赖)
|
||||
@State lastTestResult: string = '';
|
||||
private context?: common.Context;
|
||||
|
||||
aboutToAppear(): void {
|
||||
@@ -56,6 +62,13 @@ struct SettingsPage {
|
||||
AppSettings.getMutedReminderKeys(ctx).then((v: string[]): void => {
|
||||
this.mutedKeys = v;
|
||||
});
|
||||
notificationManager.isNotificationEnabled()
|
||||
.then((v: boolean): void => {
|
||||
this.notifyEnabled = v;
|
||||
})
|
||||
.catch((): void => {
|
||||
this.notifyEnabled = false;
|
||||
});
|
||||
this.loadBackupSettings();
|
||||
this.loadAllBooks();
|
||||
}
|
||||
@@ -274,6 +287,65 @@ struct SettingsPage {
|
||||
});
|
||||
}
|
||||
|
||||
/** 打开同步日志查看弹层 */
|
||||
private openLogSheet(): void {
|
||||
if (this.context === undefined) {
|
||||
return;
|
||||
}
|
||||
this.logText = LogUtil.readAll(this.context);
|
||||
this.showLogSheet = true;
|
||||
}
|
||||
|
||||
/** 复制全部日志到剪贴板(方便粘贴给我诊断) */
|
||||
private copyLog(): void {
|
||||
if (this.context === undefined) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const data: pasteboard.PasteData = pasteboard.createData(
|
||||
pasteboard.MIMETYPE_TEXT_PLAIN, LogUtil.readAll(this.context));
|
||||
pasteboard.getSystemPasteboard().setData(data)
|
||||
.then((): void => {
|
||||
this.getUIContext().getPromptAction().showToast({ message: '日志已复制到剪贴板' });
|
||||
})
|
||||
.catch((): void => {
|
||||
this.getUIContext().getPromptAction().showToast({ message: '复制失败' });
|
||||
});
|
||||
} catch (err) {
|
||||
this.getUIContext().getPromptAction().showToast({ message: '复制失败' });
|
||||
}
|
||||
}
|
||||
|
||||
private clearLog(): void {
|
||||
if (this.context === undefined) {
|
||||
return;
|
||||
}
|
||||
LogUtil.clear(this.context);
|
||||
this.logText = '(日志已清空)';
|
||||
this.getUIContext().getPromptAction().showToast({ message: '日志已清空' });
|
||||
}
|
||||
|
||||
/** 发布 1 分钟后的测试提醒,验证提醒链路 */
|
||||
private async testReminder(): Promise<void> {
|
||||
if (this.context === undefined) {
|
||||
return;
|
||||
}
|
||||
this.lastTestResult = await ReminderService.publishTestReminder(
|
||||
this.context as common.UIAbilityContext);
|
||||
}
|
||||
|
||||
/** 按当前数据立即重建提醒,显示发布条数 */
|
||||
private async refreshNow(): Promise<void> {
|
||||
if (this.context === undefined) {
|
||||
return;
|
||||
}
|
||||
const n: number = await ReminderService.refreshReminders(
|
||||
this.context as common.UIAbilityContext);
|
||||
this.getUIContext().getPromptAction().showToast({
|
||||
message: n > 0 ? `已发布 ${n} 个提醒` : '没有发布任何提醒(可能都过了提醒时间、被静音或通知未开启,详见日志)'
|
||||
});
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
// 顶部
|
||||
@@ -581,6 +653,85 @@ struct SettingsPage {
|
||||
.borderRadius(12)
|
||||
.backgroundColor($r('app.color.card_bg'))
|
||||
.border({ width: 1, color: $r('app.color.shadow_color') })
|
||||
// 提醒诊断:通知权限状态 + 测试提醒 + 立即刷新
|
||||
Column({ space: 8 }) {
|
||||
Row({ space: 10 }) {
|
||||
Column({ space: 2 }) {
|
||||
Row({ space: 6 }) {
|
||||
Text('提醒诊断')
|
||||
.fontSize(15)
|
||||
.fontWeight(FontWeight.Medium)
|
||||
.fontColor($r('app.color.text_primary'))
|
||||
Text(this.notifyEnabled ? '通知已开启' : '通知未开启!')
|
||||
.fontSize(11)
|
||||
.fontColor(this.notifyEnabled ? $r('app.color.success') : $r('app.color.error'))
|
||||
.padding({ left: 8, right: 8, top: 3, bottom: 3 })
|
||||
.borderRadius(10)
|
||||
.backgroundColor(this.notifyEnabled ? $r('app.color.success_bg') : $r('app.color.error_bg'))
|
||||
}
|
||||
Text(this.notifyEnabled
|
||||
? '点"测试提醒"验证链路:1 分钟后应收到通知'
|
||||
: '系统设置 → 通知管理 → 找到本应用 → 允许通知,日程提醒才能弹出')
|
||||
.fontSize(12)
|
||||
.fontColor($r('app.color.text_secondary'))
|
||||
}
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
.layoutWeight(1)
|
||||
}
|
||||
.width('100%')
|
||||
Row({ space: 10 }) {
|
||||
Button('测试提醒')
|
||||
.fontSize(13)
|
||||
.backgroundColor($r('app.color.brand'))
|
||||
.onClick(() => {
|
||||
this.testReminder();
|
||||
})
|
||||
Button('立即刷新提醒')
|
||||
.fontSize(13)
|
||||
.backgroundColor($r('app.color.text_secondary'))
|
||||
.onClick(() => {
|
||||
this.refreshNow();
|
||||
})
|
||||
}
|
||||
if (this.lastTestResult !== '') {
|
||||
Text(this.lastTestResult)
|
||||
.fontSize(12)
|
||||
.fontColor($r('app.color.text_secondary'))
|
||||
.width('100%')
|
||||
}
|
||||
}
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
.width('100%')
|
||||
.padding(14)
|
||||
.borderRadius(12)
|
||||
.backgroundColor($r('app.color.card_bg'))
|
||||
.border({ width: 1, color: $r('app.color.shadow_color') })
|
||||
|
||||
// 同步日志:查看/复制/清空(真机测试诊断用)
|
||||
Row({ space: 10 }) {
|
||||
Column({ space: 2 }) {
|
||||
Text('同步日志')
|
||||
.fontSize(15)
|
||||
.fontWeight(FontWeight.Medium)
|
||||
.fontColor($r('app.color.text_primary'))
|
||||
Text('记录每次同步/推送/提醒的详细过程,测试出问题时复制日志用于分析')
|
||||
.fontSize(12)
|
||||
.fontColor($r('app.color.text_secondary'))
|
||||
}
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
.layoutWeight(1)
|
||||
Button('查看')
|
||||
.fontSize(13)
|
||||
.backgroundColor($r('app.color.brand'))
|
||||
.onClick(() => {
|
||||
this.openLogSheet();
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.padding(14)
|
||||
.borderRadius(12)
|
||||
.backgroundColor($r('app.color.card_bg'))
|
||||
.border({ width: 1, color: $r('app.color.shadow_color') })
|
||||
}
|
||||
.width('100%')
|
||||
.padding({ left: 20, right: 20, top: 8, bottom: 20 })
|
||||
@@ -588,9 +739,53 @@ struct SettingsPage {
|
||||
.layoutWeight(1)
|
||||
.align(Alignment.Top)
|
||||
.scrollBar(BarState.Off)
|
||||
.bindSheet($$this.showLogSheet, this.logSheetBuilder(), {
|
||||
height: 560,
|
||||
dragBar: true,
|
||||
title: { title: '同步日志' }
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor($r('app.color.page_bg'))
|
||||
}
|
||||
|
||||
@Builder
|
||||
logSheetBuilder() {
|
||||
Column({ space: 10 }) {
|
||||
Row({ space: 10 }) {
|
||||
Button('复制全部')
|
||||
.fontSize(13)
|
||||
.backgroundColor($r('app.color.brand'))
|
||||
.onClick(() => {
|
||||
this.copyLog();
|
||||
})
|
||||
Button('清空')
|
||||
.fontSize(13)
|
||||
.backgroundColor($r('app.color.error'))
|
||||
.onClick(() => {
|
||||
this.clearLog();
|
||||
})
|
||||
Button('刷新')
|
||||
.fontSize(13)
|
||||
.backgroundColor($r('app.color.text_secondary'))
|
||||
.onClick(() => {
|
||||
this.openLogSheet();
|
||||
})
|
||||
}
|
||||
Scroll() {
|
||||
Text(this.logText === '' ? '(日志为空)' : this.logText)
|
||||
.fontSize(11)
|
||||
.fontColor($r('app.color.text_primary'))
|
||||
.width('100%')
|
||||
}
|
||||
.layoutWeight(1)
|
||||
.align(Alignment.Top)
|
||||
.scrollBar(BarState.On)
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.padding(16)
|
||||
.backgroundColor($r('app.color.page_bg'))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user