feat: Initial clean release of CHM
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
这个提交包含在:
@@ -0,0 +1,138 @@
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
import Alarm from '../models/Alarm';
|
||||
import { Notification } from '../models/Alarm';
|
||||
import { NoteEntity } from './database/types';
|
||||
|
||||
import Note from '../models/Note';
|
||||
|
||||
export interface AlarmServiceDriver {
|
||||
setService?(service: typeof AlarmService): void;
|
||||
setInAppNotificationHandler?(handler: unknown): void;
|
||||
clearNotification(id: number): Promise<void> | void;
|
||||
scheduleNotification(notification: Notification): Promise<void> | void;
|
||||
hasPersistentNotifications(): boolean;
|
||||
notificationIsSet(id: number): boolean;
|
||||
}
|
||||
|
||||
export default class AlarmService {
|
||||
|
||||
private static driver_: AlarmServiceDriver;
|
||||
private static logger_: Logger;
|
||||
// private static inAppNotificationHandler_:any;
|
||||
|
||||
public static setDriver(v: AlarmServiceDriver) {
|
||||
this.driver_ = v;
|
||||
|
||||
if (this.driver_.setService) this.driver_.setService(this);
|
||||
}
|
||||
|
||||
public static driver() {
|
||||
if (!this.driver_) throw new Error('AlarmService driver not set!');
|
||||
return this.driver_;
|
||||
}
|
||||
|
||||
public static setLogger(v: Logger) {
|
||||
this.logger_ = v;
|
||||
}
|
||||
|
||||
public static logger() {
|
||||
return this.logger_;
|
||||
}
|
||||
|
||||
public static setInAppNotificationHandler(v: unknown) {
|
||||
// this.inAppNotificationHandler_ = v;
|
||||
if (this.driver_.setInAppNotificationHandler) this.driver_.setInAppNotificationHandler(v);
|
||||
}
|
||||
|
||||
public static async garbageCollect() {
|
||||
this.logger().info('Garbage collecting alarms...');
|
||||
|
||||
// Delete alarms that have already been triggered
|
||||
await Alarm.deleteExpiredAlarms();
|
||||
|
||||
// Delete alarms that correspond to non-existent notes
|
||||
const alarmIds = await Alarm.alarmIdsWithoutNotes();
|
||||
for (let i = 0; i < alarmIds.length; i++) {
|
||||
this.logger().info(`Clearing notification for non-existing note. Alarm ${alarmIds[i]}`);
|
||||
await this.driver().clearNotification(alarmIds[i]);
|
||||
}
|
||||
await Alarm.batchDelete(alarmIds as unknown as string[], { sourceDescription: 'AlarmService/garbageCollect' });
|
||||
}
|
||||
|
||||
// When passing a note, make sure it has all the required properties
|
||||
// (better to pass a complete note or else just the ID)
|
||||
public static async updateNoteNotification(noteOrId: NoteEntity | string, isDeleted = false) {
|
||||
try {
|
||||
let note = null;
|
||||
let noteId = null;
|
||||
|
||||
if (typeof noteOrId === 'object') {
|
||||
note = noteOrId;
|
||||
noteId = note.id;
|
||||
} else {
|
||||
note = await Note.load(noteOrId);
|
||||
noteId = note ? note.id : null;
|
||||
}
|
||||
|
||||
if (!note && !isDeleted) return;
|
||||
|
||||
const driver = this.driver();
|
||||
|
||||
let alarm = noteId ? await Alarm.byNoteId(noteId) : null;
|
||||
let clearAlarm = false;
|
||||
|
||||
if (isDeleted || !Note.needAlarm(note) || (alarm && alarm.trigger_time !== note.todo_due)) {
|
||||
clearAlarm = !!alarm;
|
||||
}
|
||||
|
||||
if (!clearAlarm && alarm) {
|
||||
// Alarm already exists and set at the right time
|
||||
|
||||
// For persistent notifications (those that stay active after the app has been closed, like on mobile), if we have
|
||||
// an alarm object we can be sure that the notification has already been set, so there's nothing to do.
|
||||
// For non-persistent notifications however we need to check that the notification has been set because, for example,
|
||||
// if the app has just started the notifications need to be set again. so we do this below.
|
||||
if (!driver.hasPersistentNotifications() && !driver.notificationIsSet(alarm.id)) {
|
||||
const notification = await Alarm.makeNotification(alarm, note);
|
||||
this.logger().info(`Scheduling (non-persistent) notification for note ${note.id}`, notification);
|
||||
await driver.scheduleNotification(notification);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (clearAlarm) {
|
||||
this.logger().info(`Clearing notification for note ${noteId}`);
|
||||
await driver.clearNotification(alarm.id);
|
||||
await Alarm.delete(alarm.id, { sourceDescription: 'AlarmService/clearAlarm' });
|
||||
}
|
||||
|
||||
if (isDeleted || !Note.needAlarm(note)) return;
|
||||
|
||||
await Alarm.save({
|
||||
note_id: note.id,
|
||||
trigger_time: note.todo_due,
|
||||
});
|
||||
|
||||
// Reload alarm to get its ID
|
||||
alarm = await Alarm.byNoteId(note.id);
|
||||
|
||||
const notification = await Alarm.makeNotification(alarm, note);
|
||||
this.logger().info(`Scheduling notification for note ${note.id}`, notification);
|
||||
await driver.scheduleNotification(notification);
|
||||
} catch (error) {
|
||||
this.logger().error('Could not update notification', error);
|
||||
}
|
||||
}
|
||||
|
||||
public static async updateAllNotifications() {
|
||||
this.logger().info('Updating all notifications...');
|
||||
|
||||
await this.garbageCollect();
|
||||
|
||||
const dueNotes = await Note.dueNotes();
|
||||
for (let i = 0; i < dueNotes.length; i++) {
|
||||
await this.updateNoteNotification(dueNotes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户