✨ Add update notifications
This commit is contained in:
@@ -0,0 +1,64 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service represents the notifications service
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import * as application$0 from "../../application/models.js";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import * as $models from "./models.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RemoveBadge removes the badge label from the application icon.
|
||||||
|
*/
|
||||||
|
export function RemoveBadge(): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(2374916939) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServiceName returns the name of the service.
|
||||||
|
*/
|
||||||
|
export function ServiceName(): Promise<string> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(2428202016) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServiceShutdown is called when the service is unloaded.
|
||||||
|
*/
|
||||||
|
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(3893755233) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServiceStartup is called when the service is loaded.
|
||||||
|
*/
|
||||||
|
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(4078800764, options) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SetBadge sets the badge label on the application icon.
|
||||||
|
*/
|
||||||
|
export function SetBadge(label: string): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(784276339, label) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SetCustomBadge(label: string, options: $models.Options): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(3058653106, label, options) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
import * as BadgeService from "./badgeservice.js";
|
||||||
|
export {
|
||||||
|
BadgeService
|
||||||
|
};
|
||||||
|
|
||||||
|
export * from "./models.js";
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import {Create as $Create} from "@wailsio/runtime";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import * as color$0 from "../../../../../../../image/color/models.js";
|
||||||
|
|
||||||
|
export class Options {
|
||||||
|
"TextColour": color$0.RGBA;
|
||||||
|
"BackgroundColour": color$0.RGBA;
|
||||||
|
"FontName": string;
|
||||||
|
"FontSize": number;
|
||||||
|
"SmallFontSize": number;
|
||||||
|
|
||||||
|
/** Creates a new Options instance. */
|
||||||
|
constructor($$source: Partial<Options> = {}) {
|
||||||
|
if (!("TextColour" in $$source)) {
|
||||||
|
this["TextColour"] = (new color$0.RGBA());
|
||||||
|
}
|
||||||
|
if (!("BackgroundColour" in $$source)) {
|
||||||
|
this["BackgroundColour"] = (new color$0.RGBA());
|
||||||
|
}
|
||||||
|
if (!("FontName" in $$source)) {
|
||||||
|
this["FontName"] = "";
|
||||||
|
}
|
||||||
|
if (!("FontSize" in $$source)) {
|
||||||
|
this["FontSize"] = 0;
|
||||||
|
}
|
||||||
|
if (!("SmallFontSize" in $$source)) {
|
||||||
|
this["SmallFontSize"] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this, $$source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Options instance from a string or object.
|
||||||
|
*/
|
||||||
|
static createFrom($$source: any = {}): Options {
|
||||||
|
const $$createField0_0 = $$createType0;
|
||||||
|
const $$createField1_0 = $$createType0;
|
||||||
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
if ("TextColour" in $$parsedSource) {
|
||||||
|
$$parsedSource["TextColour"] = $$createField0_0($$parsedSource["TextColour"]);
|
||||||
|
}
|
||||||
|
if ("BackgroundColour" in $$parsedSource) {
|
||||||
|
$$parsedSource["BackgroundColour"] = $$createField1_0($$parsedSource["BackgroundColour"]);
|
||||||
|
}
|
||||||
|
return new Options($$parsedSource as Partial<Options>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private type creation functions
|
||||||
|
const $$createType0 = color$0.RGBA.createFrom;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
import * as NotificationService from "./notificationservice.js";
|
||||||
|
export {
|
||||||
|
NotificationService
|
||||||
|
};
|
||||||
|
|
||||||
|
export * from "./models.js";
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import {Create as $Create} from "@wailsio/runtime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NotificationAction represents an action button for a notification.
|
||||||
|
*/
|
||||||
|
export class NotificationAction {
|
||||||
|
"id"?: string;
|
||||||
|
"title"?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (macOS-specific)
|
||||||
|
*/
|
||||||
|
"destructive"?: boolean;
|
||||||
|
|
||||||
|
/** Creates a new NotificationAction instance. */
|
||||||
|
constructor($$source: Partial<NotificationAction> = {}) {
|
||||||
|
|
||||||
|
Object.assign(this, $$source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new NotificationAction instance from a string or object.
|
||||||
|
*/
|
||||||
|
static createFrom($$source: any = {}): NotificationAction {
|
||||||
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
return new NotificationAction($$parsedSource as Partial<NotificationAction>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NotificationCategory groups actions for notifications.
|
||||||
|
*/
|
||||||
|
export class NotificationCategory {
|
||||||
|
"id"?: string;
|
||||||
|
"actions"?: NotificationAction[];
|
||||||
|
"hasReplyField"?: boolean;
|
||||||
|
"replyPlaceholder"?: string;
|
||||||
|
"replyButtonTitle"?: string;
|
||||||
|
|
||||||
|
/** Creates a new NotificationCategory instance. */
|
||||||
|
constructor($$source: Partial<NotificationCategory> = {}) {
|
||||||
|
|
||||||
|
Object.assign(this, $$source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new NotificationCategory instance from a string or object.
|
||||||
|
*/
|
||||||
|
static createFrom($$source: any = {}): NotificationCategory {
|
||||||
|
const $$createField1_0 = $$createType1;
|
||||||
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
if ("actions" in $$parsedSource) {
|
||||||
|
$$parsedSource["actions"] = $$createField1_0($$parsedSource["actions"]);
|
||||||
|
}
|
||||||
|
return new NotificationCategory($$parsedSource as Partial<NotificationCategory>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NotificationOptions contains configuration for a notification
|
||||||
|
*/
|
||||||
|
export class NotificationOptions {
|
||||||
|
"id": string;
|
||||||
|
"title": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (macOS and Linux only)
|
||||||
|
*/
|
||||||
|
"subtitle"?: string;
|
||||||
|
"body"?: string;
|
||||||
|
"categoryId"?: string;
|
||||||
|
"data"?: { [_: string]: any };
|
||||||
|
|
||||||
|
/** Creates a new NotificationOptions instance. */
|
||||||
|
constructor($$source: Partial<NotificationOptions> = {}) {
|
||||||
|
if (!("id" in $$source)) {
|
||||||
|
this["id"] = "";
|
||||||
|
}
|
||||||
|
if (!("title" in $$source)) {
|
||||||
|
this["title"] = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this, $$source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new NotificationOptions instance from a string or object.
|
||||||
|
*/
|
||||||
|
static createFrom($$source: any = {}): NotificationOptions {
|
||||||
|
const $$createField5_0 = $$createType2;
|
||||||
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
if ("data" in $$parsedSource) {
|
||||||
|
$$parsedSource["data"] = $$createField5_0($$parsedSource["data"]);
|
||||||
|
}
|
||||||
|
return new NotificationOptions($$parsedSource as Partial<NotificationOptions>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private type creation functions
|
||||||
|
const $$createType0 = NotificationAction.createFrom;
|
||||||
|
const $$createType1 = $Create.Array($$createType0);
|
||||||
|
const $$createType2 = $Create.Map($Create.Any, $Create.Any);
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service represents the notifications service
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import * as application$0 from "../../application/models.js";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import * as $models from "./models.js";
|
||||||
|
|
||||||
|
export function CheckNotificationAuthorization(): Promise<boolean> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(2216952893) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OnNotificationResponse registers a callback function that will be called when
|
||||||
|
* a notification response is received from the user.
|
||||||
|
*/
|
||||||
|
export function OnNotificationResponse(callback: any): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(1642697808, callback) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RegisterNotificationCategory(category: $models.NotificationCategory): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(2917562919, category) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoveAllDeliveredNotifications(): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(3956282340) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoveAllPendingNotifications(): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(108821341) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoveDeliveredNotification(identifier: string): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(975691940, identifier) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoveNotification(identifier: string): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(3966653866, identifier) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoveNotificationCategory(categoryID: string): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(2032615554, categoryID) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemovePendingNotification(identifier: string): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(3729049703, identifier) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public methods that delegate to the implementation.
|
||||||
|
*/
|
||||||
|
export function RequestNotificationAuthorization(): Promise<boolean> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(3933442950) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SendNotification(options: $models.NotificationOptions): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(3968228732, options) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SendNotificationWithActions(options: $models.NotificationOptions): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(1886542847, options) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServiceName returns the name of the service.
|
||||||
|
*/
|
||||||
|
export function ServiceName(): Promise<string> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(2704532675) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServiceShutdown is called when the service is unloaded.
|
||||||
|
*/
|
||||||
|
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(2550195434) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServiceStartup is called when the service is loaded.
|
||||||
|
*/
|
||||||
|
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(4047820929, options) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
4
frontend/bindings/image/color/index.ts
Normal file
4
frontend/bindings/image/color/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export * from "./models.js";
|
||||||
46
frontend/bindings/image/color/models.ts
Normal file
46
frontend/bindings/image/color/models.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import {Create as $Create} from "@wailsio/runtime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RGBA represents a traditional 32-bit alpha-premultiplied color, having 8
|
||||||
|
* bits for each of red, green, blue and alpha.
|
||||||
|
*
|
||||||
|
* An alpha-premultiplied color component C has been scaled by alpha (A), so
|
||||||
|
* has valid values 0 <= C <= A.
|
||||||
|
*/
|
||||||
|
export class RGBA {
|
||||||
|
"R": number;
|
||||||
|
"G": number;
|
||||||
|
"B": number;
|
||||||
|
"A": number;
|
||||||
|
|
||||||
|
/** Creates a new RGBA instance. */
|
||||||
|
constructor($$source: Partial<RGBA> = {}) {
|
||||||
|
if (!("R" in $$source)) {
|
||||||
|
this["R"] = 0;
|
||||||
|
}
|
||||||
|
if (!("G" in $$source)) {
|
||||||
|
this["G"] = 0;
|
||||||
|
}
|
||||||
|
if (!("B" in $$source)) {
|
||||||
|
this["B"] = 0;
|
||||||
|
}
|
||||||
|
if (!("A" in $$source)) {
|
||||||
|
this["A"] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this, $$source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new RGBA instance from a string or object.
|
||||||
|
*/
|
||||||
|
static createFrom($$source: any = {}): RGBA {
|
||||||
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
return new RGBA($$parsedSource as Partial<RGBA>);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import * as MigrationService from "./migrationservice.js";
|
|||||||
import * as SelfUpdateService from "./selfupdateservice.js";
|
import * as SelfUpdateService from "./selfupdateservice.js";
|
||||||
import * as StartupService from "./startupservice.js";
|
import * as StartupService from "./startupservice.js";
|
||||||
import * as SystemService from "./systemservice.js";
|
import * as SystemService from "./systemservice.js";
|
||||||
|
import * as TestService from "./testservice.js";
|
||||||
import * as ThemeService from "./themeservice.js";
|
import * as ThemeService from "./themeservice.js";
|
||||||
import * as TranslationService from "./translationservice.js";
|
import * as TranslationService from "./translationservice.js";
|
||||||
import * as TrayService from "./trayservice.js";
|
import * as TrayService from "./trayservice.js";
|
||||||
@@ -31,6 +32,7 @@ export {
|
|||||||
SelfUpdateService,
|
SelfUpdateService,
|
||||||
StartupService,
|
StartupService,
|
||||||
SystemService,
|
SystemService,
|
||||||
|
TestService,
|
||||||
ThemeService,
|
ThemeService,
|
||||||
TranslationService,
|
TranslationService,
|
||||||
TrayService,
|
TrayService,
|
||||||
|
|||||||
55
frontend/bindings/voidraft/internal/services/testservice.ts
Normal file
55
frontend/bindings/voidraft/internal/services/testservice.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TestService 测试服务 - 仅在开发环境使用
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClearAll 清除所有测试状态
|
||||||
|
*/
|
||||||
|
export function ClearAll(): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(2179720854) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServiceStartup 服务启动时调用
|
||||||
|
*/
|
||||||
|
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(617408198, options) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TestBadge 测试Badge功能
|
||||||
|
*/
|
||||||
|
export function TestBadge(text: string): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(4242952145, text) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TestNotification 测试通知功能
|
||||||
|
*/
|
||||||
|
export function TestNotification(title: string, subtitle: string, body: string): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(1697553289, title, subtitle, body) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TestUpdateNotification 测试更新通知
|
||||||
|
*/
|
||||||
|
export function TestUpdateNotification(): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(3091730060) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
@@ -8,19 +8,11 @@ import KeyBindingsPage from '@/views/settings/pages/KeyBindingsPage.vue';
|
|||||||
import UpdatesPage from '@/views/settings/pages/UpdatesPage.vue';
|
import UpdatesPage from '@/views/settings/pages/UpdatesPage.vue';
|
||||||
import ExtensionsPage from '@/views/settings/pages/ExtensionsPage.vue';
|
import ExtensionsPage from '@/views/settings/pages/ExtensionsPage.vue';
|
||||||
import BackupPage from '@/views/settings/pages/BackupPage.vue';
|
import BackupPage from '@/views/settings/pages/BackupPage.vue';
|
||||||
|
// 测试页面
|
||||||
|
import TestPage from '@/views/settings/pages/TestPage.vue';
|
||||||
|
|
||||||
const routes: RouteRecordRaw[] = [
|
// 基础设置子路由
|
||||||
{
|
const settingsChildren: RouteRecordRaw[] = [
|
||||||
path: '/',
|
|
||||||
name: 'Editor',
|
|
||||||
component: Editor
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/settings',
|
|
||||||
name: 'Settings',
|
|
||||||
redirect: '/settings/general',
|
|
||||||
component: Settings,
|
|
||||||
children: [
|
|
||||||
{
|
{
|
||||||
path: 'general',
|
path: 'general',
|
||||||
name: 'SettingsGeneral',
|
name: 'SettingsGeneral',
|
||||||
@@ -56,7 +48,29 @@ const routes: RouteRecordRaw[] = [
|
|||||||
name: 'SettingsBackup',
|
name: 'SettingsBackup',
|
||||||
component: BackupPage
|
component: BackupPage
|
||||||
}
|
}
|
||||||
]
|
];
|
||||||
|
|
||||||
|
// 仅在开发环境添加测试页面路由
|
||||||
|
if (import.meta.env.DEV) {
|
||||||
|
settingsChildren.push({
|
||||||
|
path: 'test',
|
||||||
|
name: 'SettingsTest',
|
||||||
|
component: TestPage
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const routes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'Editor',
|
||||||
|
component: Editor
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/settings',
|
||||||
|
name: 'Settings',
|
||||||
|
redirect: '/settings/general',
|
||||||
|
component: Settings,
|
||||||
|
children: settingsChildren
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ const navItems = [
|
|||||||
{ id: 'updates', icon: '🔄', route: '/settings/updates' }
|
{ id: 'updates', icon: '🔄', route: '/settings/updates' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 仅在开发环境添加测试页面导航
|
||||||
|
if (import.meta.env.DEV) {
|
||||||
|
navItems.push({ id: 'test', icon: '🧪', route: '/settings/test' });
|
||||||
|
}
|
||||||
|
|
||||||
const activeNavItem = ref(route.path.split('/').pop() || 'general');
|
const activeNavItem = ref(route.path.split('/').pop() || 'general');
|
||||||
|
|
||||||
// 处理导航点击
|
// 处理导航点击
|
||||||
@@ -56,7 +61,7 @@ const goBackToEditor = async () => {
|
|||||||
@click="handleNavClick(item)"
|
@click="handleNavClick(item)"
|
||||||
>
|
>
|
||||||
<span class="nav-icon">{{ item.icon }}</span>
|
<span class="nav-icon">{{ item.icon }}</span>
|
||||||
<span class="nav-text">{{ t(`settings.${item.id}`) }}</span>
|
<span class="nav-text">{{ item.id === 'test' ? 'Test' : t(`settings.${item.id}`) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-footer">
|
<div class="settings-footer">
|
||||||
|
|||||||
274
frontend/src/views/settings/pages/TestPage.vue
Normal file
274
frontend/src/views/settings/pages/TestPage.vue
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
<template>
|
||||||
|
<div class="settings-page">
|
||||||
|
<SettingSection title="Development Test Page">
|
||||||
|
<div class="dev-description">
|
||||||
|
This page is only available in development environment for testing notification and badge services.
|
||||||
|
</div>
|
||||||
|
</SettingSection>
|
||||||
|
|
||||||
|
<!-- Badge测试区域 -->
|
||||||
|
<SettingSection title="Badge Service Test">
|
||||||
|
<SettingItem title="Badge Text">
|
||||||
|
<input
|
||||||
|
v-model="badgeText"
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter badge text (empty to remove)"
|
||||||
|
class="select-input"
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem title="Actions">
|
||||||
|
<div class="button-group">
|
||||||
|
<button @click="testBadge" class="test-button primary">
|
||||||
|
Set Badge
|
||||||
|
</button>
|
||||||
|
<button @click="clearBadge" class="test-button">
|
||||||
|
Clear Badge
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</SettingItem>
|
||||||
|
<div v-if="badgeStatus" class="test-status" :class="badgeStatus.type">
|
||||||
|
{{ badgeStatus.message }}
|
||||||
|
</div>
|
||||||
|
</SettingSection>
|
||||||
|
|
||||||
|
<!-- 通知测试区域 -->
|
||||||
|
<SettingSection title="Notification Service Test">
|
||||||
|
<SettingItem title="Title">
|
||||||
|
<input
|
||||||
|
v-model="notificationTitle"
|
||||||
|
type="text"
|
||||||
|
placeholder="Notification title"
|
||||||
|
class="select-input"
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem title="Subtitle">
|
||||||
|
<input
|
||||||
|
v-model="notificationSubtitle"
|
||||||
|
type="text"
|
||||||
|
placeholder="Notification subtitle"
|
||||||
|
class="select-input"
|
||||||
|
/>
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem title="Body">
|
||||||
|
<textarea
|
||||||
|
v-model="notificationBody"
|
||||||
|
placeholder="Notification body text"
|
||||||
|
class="select-input textarea-input"
|
||||||
|
rows="3"
|
||||||
|
></textarea>
|
||||||
|
</SettingItem>
|
||||||
|
<SettingItem title="Actions">
|
||||||
|
<div class="button-group">
|
||||||
|
<button @click="testNotification" class="test-button primary">
|
||||||
|
Send Test Notification
|
||||||
|
</button>
|
||||||
|
<button @click="testUpdateNotification" class="test-button">
|
||||||
|
Test Update Notification
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</SettingItem>
|
||||||
|
<div v-if="notificationStatus" class="test-status" :class="notificationStatus.type">
|
||||||
|
{{ notificationStatus.message }}
|
||||||
|
</div>
|
||||||
|
</SettingSection>
|
||||||
|
|
||||||
|
<!-- 清除所有测试状态 -->
|
||||||
|
<SettingSection title="Cleanup">
|
||||||
|
<SettingItem title="Clear All">
|
||||||
|
<button @click="clearAll" class="test-button danger">
|
||||||
|
Clear All Test States
|
||||||
|
</button>
|
||||||
|
</SettingItem>
|
||||||
|
<div v-if="clearStatus" class="test-status" :class="clearStatus.type">
|
||||||
|
{{ clearStatus.message }}
|
||||||
|
</div>
|
||||||
|
</SettingSection>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import * as TestService from '@/../bindings/voidraft/internal/services/testservice'
|
||||||
|
import SettingSection from '../components/SettingSection.vue'
|
||||||
|
import SettingItem from '../components/SettingItem.vue'
|
||||||
|
|
||||||
|
// Badge测试状态
|
||||||
|
const badgeText = ref('')
|
||||||
|
const badgeStatus = ref<{ type: string; message: string } | null>(null)
|
||||||
|
|
||||||
|
// 通知测试状态
|
||||||
|
const notificationTitle = ref('')
|
||||||
|
const notificationSubtitle = ref('')
|
||||||
|
const notificationBody = ref('')
|
||||||
|
const notificationStatus = ref<{ type: string; message: string } | null>(null)
|
||||||
|
|
||||||
|
// 清除状态
|
||||||
|
const clearStatus = ref<{ type: string; message: string } | null>(null)
|
||||||
|
|
||||||
|
// 显示状态消息的辅助函数
|
||||||
|
const showStatus = (statusRef: any, type: 'success' | 'error', message: string) => {
|
||||||
|
statusRef.value = { type, message }
|
||||||
|
setTimeout(() => {
|
||||||
|
statusRef.value = null
|
||||||
|
}, 5000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试Badge功能
|
||||||
|
const testBadge = async () => {
|
||||||
|
try {
|
||||||
|
await TestService.TestBadge(badgeText.value)
|
||||||
|
showStatus(badgeStatus, 'success', `Badge ${badgeText.value ? 'set to: ' + badgeText.value : 'cleared'} successfully`)
|
||||||
|
} catch (error: any) {
|
||||||
|
showStatus(badgeStatus, 'error', `Failed to set badge: ${error.message || error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除Badge
|
||||||
|
const clearBadge = async () => {
|
||||||
|
try {
|
||||||
|
await TestService.TestBadge('')
|
||||||
|
badgeText.value = ''
|
||||||
|
showStatus(badgeStatus, 'success', 'Badge cleared successfully')
|
||||||
|
} catch (error: any) {
|
||||||
|
showStatus(badgeStatus, 'error', `Failed to clear badge: ${error.message || error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试通知功能
|
||||||
|
const testNotification = async () => {
|
||||||
|
try {
|
||||||
|
await TestService.TestNotification(
|
||||||
|
notificationTitle.value,
|
||||||
|
notificationSubtitle.value,
|
||||||
|
notificationBody.value
|
||||||
|
)
|
||||||
|
showStatus(notificationStatus, 'success', 'Notification sent successfully')
|
||||||
|
} catch (error: any) {
|
||||||
|
showStatus(notificationStatus, 'error', `Failed to send notification: ${error.message || error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试更新通知
|
||||||
|
const testUpdateNotification = async () => {
|
||||||
|
try {
|
||||||
|
await TestService.TestUpdateNotification()
|
||||||
|
showStatus(notificationStatus, 'success', 'Update notification sent successfully (badge + notification)')
|
||||||
|
} catch (error: any) {
|
||||||
|
showStatus(notificationStatus, 'error', `Failed to send update notification: ${error.message || error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除所有测试状态
|
||||||
|
const clearAll = async () => {
|
||||||
|
try {
|
||||||
|
await TestService.ClearAll()
|
||||||
|
// 清空表单
|
||||||
|
badgeText.value = ''
|
||||||
|
notificationTitle.value = ''
|
||||||
|
notificationSubtitle.value = ''
|
||||||
|
notificationBody.value = ''
|
||||||
|
showStatus(clearStatus, 'success', 'All test states cleared successfully')
|
||||||
|
} catch (error: any) {
|
||||||
|
showStatus(clearStatus, 'error', `Failed to clear test states: ${error.message || error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.settings-page {
|
||||||
|
padding: 20px 0 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-description {
|
||||||
|
color: var(--settings-text-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.4;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-input {
|
||||||
|
padding: 6px 8px;
|
||||||
|
border: 1px solid var(--settings-input-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--settings-input-bg);
|
||||||
|
color: var(--settings-text);
|
||||||
|
font-size: 12px;
|
||||||
|
width: 180px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #4a9eff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.textarea-input {
|
||||||
|
min-height: 60px;
|
||||||
|
resize: vertical;
|
||||||
|
font-family: inherit;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-button {
|
||||||
|
padding: 6px 12px;
|
||||||
|
border: 1px solid var(--settings-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--settings-card-bg);
|
||||||
|
color: var(--settings-text);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--settings-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
background-color: #4a9eff;
|
||||||
|
color: white;
|
||||||
|
border-color: #4a9eff;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #3a8eef;
|
||||||
|
border-color: #3a8eef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.danger {
|
||||||
|
background-color: var(--text-danger);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--text-danger);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-status {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
border: 1px solid;
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background-color: rgba(34, 197, 94, 0.1);
|
||||||
|
color: #16a34a;
|
||||||
|
border-color: rgba(34, 197, 94, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
background-color: rgba(239, 68, 68, 0.1);
|
||||||
|
color: #dc2626;
|
||||||
|
border-color: rgba(239, 68, 68, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
2
go.mod
2
go.mod
@@ -21,6 +21,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
code.gitea.io/sdk/gitea v0.21.0 // indirect
|
code.gitea.io/sdk/gitea v0.21.0 // indirect
|
||||||
dario.cat/mergo v1.0.2 // indirect
|
dario.cat/mergo v1.0.2 // indirect
|
||||||
|
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect
|
||||||
github.com/42wim/httpsig v1.2.3 // indirect
|
github.com/42wim/httpsig v1.2.3 // indirect
|
||||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
@@ -77,6 +78,7 @@ require (
|
|||||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
golang.org/x/crypto v0.41.0 // indirect
|
golang.org/x/crypto v0.41.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect
|
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect
|
||||||
|
golang.org/x/image v0.24.0 // indirect
|
||||||
golang.org/x/oauth2 v0.30.0 // indirect
|
golang.org/x/oauth2 v0.30.0 // indirect
|
||||||
golang.org/x/time v0.12.0 // indirect
|
golang.org/x/time v0.12.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -2,6 +2,8 @@ code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
|
|||||||
code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
|
code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
|
||||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||||
|
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 h1:N3IGoHHp9pb6mj1cbXbuaSXV/UMKwmbKLf53nQmtqMA=
|
||||||
|
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3/go.mod h1:QtOLZGz8olr4qH2vWK0QH0w0O4T9fEIjMuWpKUsH7nc=
|
||||||
github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs=
|
github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs=
|
||||||
github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM=
|
github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM=
|
||||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||||
@@ -178,6 +180,8 @@ golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
|||||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||||
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 h1:SbTAbRFnd5kjQXbczszQ0hdk3ctwYf3qBNH9jIsGclE=
|
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 h1:SbTAbRFnd5kjQXbczszQ0hdk3ctwYf3qBNH9jIsGclE=
|
||||||
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
|
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
|
||||||
|
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
|
||||||
|
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
|
||||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ package services
|
|||||||
#cgo CFLAGS: -I../lib
|
#cgo CFLAGS: -I../lib
|
||||||
#cgo LDFLAGS: -luser32
|
#cgo LDFLAGS: -luser32
|
||||||
#include "../lib/hotkey_windows.c"
|
#include "../lib/hotkey_windows.c"
|
||||||
#include "hotkey_windows.h"
|
#include "../lib/hotkey_windows.h"
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/creativeprojects/go-selfupdate"
|
"github.com/creativeprojects/go-selfupdate"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/services/badge"
|
||||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/services/notifications"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
@@ -28,6 +30,8 @@ type SelfUpdateResult struct {
|
|||||||
type SelfUpdateService struct {
|
type SelfUpdateService struct {
|
||||||
logger *log.LogService
|
logger *log.LogService
|
||||||
configService *ConfigService
|
configService *ConfigService
|
||||||
|
badgeService *badge.BadgeService // 直接使用Wails原生badge服务
|
||||||
|
notificationService *notifications.NotificationService // 通知服务
|
||||||
config *models.AppConfig
|
config *models.AppConfig
|
||||||
|
|
||||||
// 状态管理
|
// 状态管理
|
||||||
@@ -35,7 +39,7 @@ type SelfUpdateService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewSelfUpdateService 创建自我更新服务实例
|
// NewSelfUpdateService 创建自我更新服务实例
|
||||||
func NewSelfUpdateService(configService *ConfigService, logger *log.LogService) (*SelfUpdateService, error) {
|
func NewSelfUpdateService(configService *ConfigService, badgeService *badge.BadgeService, notificationService *notifications.NotificationService, logger *log.LogService) (*SelfUpdateService, error) {
|
||||||
// 获取配置
|
// 获取配置
|
||||||
appConfig, err := configService.GetConfig()
|
appConfig, err := configService.GetConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -45,6 +49,8 @@ func NewSelfUpdateService(configService *ConfigService, logger *log.LogService)
|
|||||||
service := &SelfUpdateService{
|
service := &SelfUpdateService{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
configService: configService,
|
configService: configService,
|
||||||
|
badgeService: badgeService,
|
||||||
|
notificationService: notificationService,
|
||||||
config: appConfig,
|
config: appConfig,
|
||||||
isUpdating: false,
|
isUpdating: false,
|
||||||
}
|
}
|
||||||
@@ -63,6 +69,7 @@ func (s *SelfUpdateService) CheckForUpdates(ctx context.Context) (*SelfUpdateRes
|
|||||||
// 首先尝试主要更新源
|
// 首先尝试主要更新源
|
||||||
primaryResult, err := s.checkSourceForUpdates(ctx, s.config.Updates.PrimarySource)
|
primaryResult, err := s.checkSourceForUpdates(ctx, s.config.Updates.PrimarySource)
|
||||||
if err == nil && primaryResult != nil {
|
if err == nil && primaryResult != nil {
|
||||||
|
s.handleUpdateBadge(primaryResult)
|
||||||
return primaryResult, nil
|
return primaryResult, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,9 +78,12 @@ func (s *SelfUpdateService) CheckForUpdates(ctx context.Context) (*SelfUpdateRes
|
|||||||
if backupErr != nil {
|
if backupErr != nil {
|
||||||
// 如果备用源也失败,返回主要源的错误信息
|
// 如果备用源也失败,返回主要源的错误信息
|
||||||
result.Error = fmt.Sprintf("Primary source error: %v; Backup source error: %v", err, backupErr)
|
result.Error = fmt.Sprintf("Primary source error: %v; Backup source error: %v", err, backupErr)
|
||||||
|
// 确保在检查失败时也调用handleUpdateBadge来清除可能存在的badge
|
||||||
|
s.handleUpdateBadge(result)
|
||||||
return result, errors.New(result.Error)
|
return result, errors.New(result.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.handleUpdateBadge(backupResult)
|
||||||
return backupResult, nil
|
return backupResult, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,6 +324,13 @@ func (s *SelfUpdateService) ApplyUpdate(ctx context.Context) (*SelfUpdateResult,
|
|||||||
s.logger.Error("Failed to migrate config after update", "error", err)
|
s.logger.Error("Failed to migrate config after update", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新成功,移除badge
|
||||||
|
if s.badgeService != nil {
|
||||||
|
if err := s.badgeService.RemoveBadge(); err != nil {
|
||||||
|
s.logger.Error("failed to remove update badge after successful update", "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,6 +412,13 @@ func (s *SelfUpdateService) updateFromSource(ctx context.Context, sourceType mod
|
|||||||
s.logger.Error("Failed to update config version", "error", err)
|
s.logger.Error("Failed to update config version", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新成功,移除badge
|
||||||
|
if s.badgeService != nil {
|
||||||
|
if err := s.badgeService.RemoveBadge(); err != nil {
|
||||||
|
s.logger.Error("failed to remove update badge after successful update", "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,3 +489,63 @@ func (s *SelfUpdateService) cleanupBackup(backupPath string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleUpdateBadge 处理更新通知badge和通知
|
||||||
|
func (s *SelfUpdateService) handleUpdateBadge(result *SelfUpdateResult) {
|
||||||
|
if result != nil && result.HasUpdate {
|
||||||
|
// 有更新时显示更新badge
|
||||||
|
if s.badgeService != nil {
|
||||||
|
if err := s.badgeService.SetBadge("●"); err != nil {
|
||||||
|
s.logger.Error("failed to set update badge", "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送简单通知
|
||||||
|
s.sendUpdateNotification(result)
|
||||||
|
} else {
|
||||||
|
// 没有更新或出错时移除badge
|
||||||
|
if s.badgeService != nil {
|
||||||
|
if err := s.badgeService.RemoveBadge(); err != nil {
|
||||||
|
s.logger.Error("failed to remove update badge", "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendUpdateNotification 发送更新通知
|
||||||
|
func (s *SelfUpdateService) sendUpdateNotification(result *SelfUpdateResult) {
|
||||||
|
if s.notificationService == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查通知授权(macOS需要)
|
||||||
|
authorized, err := s.notificationService.CheckNotificationAuthorization()
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to check notification authorization", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !authorized {
|
||||||
|
authorized, err = s.notificationService.RequestNotificationAuthorization()
|
||||||
|
if err != nil || !authorized {
|
||||||
|
s.logger.Error("Failed to get notification authorization", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建简单通知内容
|
||||||
|
title := "Voidraft Update Available"
|
||||||
|
body := fmt.Sprintf("New version %s available (current: %s)", result.LatestVersion, result.CurrentVersion)
|
||||||
|
|
||||||
|
// 发送简单通知
|
||||||
|
err = s.notificationService.SendNotification(notifications.NotificationOptions{
|
||||||
|
ID: "update_available",
|
||||||
|
Title: title,
|
||||||
|
Subtitle: "New version available",
|
||||||
|
Body: body,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to send notification", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import (
|
|||||||
"voidraft/internal/models"
|
"voidraft/internal/models"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/application"
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/services/badge"
|
||||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/services/notifications"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceManager 服务管理器,负责协调各个服务
|
// ServiceManager 服务管理器,负责协调各个服务
|
||||||
@@ -25,6 +27,9 @@ type ServiceManager struct {
|
|||||||
selfUpdateService *SelfUpdateService
|
selfUpdateService *SelfUpdateService
|
||||||
translationService *TranslationService
|
translationService *TranslationService
|
||||||
themeService *ThemeService
|
themeService *ThemeService
|
||||||
|
badgeService *badge.BadgeService
|
||||||
|
notificationService *notifications.NotificationService
|
||||||
|
testService *TestService // 测试服务(仅开发环境)
|
||||||
BackupService *BackupService
|
BackupService *BackupService
|
||||||
logger *log.LogService
|
logger *log.LogService
|
||||||
}
|
}
|
||||||
@@ -34,6 +39,12 @@ func NewServiceManager() *ServiceManager {
|
|||||||
// 初始化日志服务
|
// 初始化日志服务
|
||||||
logger := log.New()
|
logger := log.New()
|
||||||
|
|
||||||
|
// 初始化badge服务
|
||||||
|
badgeService := badge.New()
|
||||||
|
|
||||||
|
// 初始化通知服务
|
||||||
|
notificationService := notifications.New()
|
||||||
|
|
||||||
// 初始化配置服务
|
// 初始化配置服务
|
||||||
configService := NewConfigService(logger)
|
configService := NewConfigService(logger)
|
||||||
|
|
||||||
@@ -77,7 +88,7 @@ func NewServiceManager() *ServiceManager {
|
|||||||
startupService := NewStartupService(configService, logger)
|
startupService := NewStartupService(configService, logger)
|
||||||
|
|
||||||
// 初始化自我更新服务
|
// 初始化自我更新服务
|
||||||
selfUpdateService, err := NewSelfUpdateService(configService, logger)
|
selfUpdateService, err := NewSelfUpdateService(configService, badgeService, notificationService, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -91,6 +102,9 @@ func NewServiceManager() *ServiceManager {
|
|||||||
// 初始化备份服务
|
// 初始化备份服务
|
||||||
backupService := NewBackupService(configService, databaseService, logger)
|
backupService := NewBackupService(configService, databaseService, logger)
|
||||||
|
|
||||||
|
// 初始化测试服务(开发环境使用)
|
||||||
|
testService := NewTestService(badgeService, notificationService, logger)
|
||||||
|
|
||||||
// 使用新的配置通知系统设置热键配置变更监听
|
// 使用新的配置通知系统设置热键配置变更监听
|
||||||
err = configService.SetHotkeyChangeCallback(func(enable bool, hotkey *models.HotkeyCombo) error {
|
err = configService.SetHotkeyChangeCallback(func(enable bool, hotkey *models.HotkeyCombo) error {
|
||||||
return hotkeyService.UpdateHotkey(enable, hotkey)
|
return hotkeyService.UpdateHotkey(enable, hotkey)
|
||||||
@@ -140,6 +154,9 @@ func NewServiceManager() *ServiceManager {
|
|||||||
selfUpdateService: selfUpdateService,
|
selfUpdateService: selfUpdateService,
|
||||||
translationService: translationService,
|
translationService: translationService,
|
||||||
themeService: themeService,
|
themeService: themeService,
|
||||||
|
badgeService: badgeService,
|
||||||
|
notificationService: notificationService,
|
||||||
|
testService: testService,
|
||||||
BackupService: backupService,
|
BackupService: backupService,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
@@ -164,6 +181,9 @@ func (sm *ServiceManager) GetServices() []application.Service {
|
|||||||
application.NewService(sm.selfUpdateService),
|
application.NewService(sm.selfUpdateService),
|
||||||
application.NewService(sm.translationService),
|
application.NewService(sm.translationService),
|
||||||
application.NewService(sm.themeService),
|
application.NewService(sm.themeService),
|
||||||
|
application.NewService(sm.badgeService),
|
||||||
|
application.NewService(sm.notificationService),
|
||||||
|
application.NewService(sm.testService), // 注册测试服务
|
||||||
application.NewService(sm.BackupService),
|
application.NewService(sm.BackupService),
|
||||||
}
|
}
|
||||||
return services
|
return services
|
||||||
@@ -243,3 +263,13 @@ func (sm *ServiceManager) GetThemeService() *ThemeService {
|
|||||||
func (sm *ServiceManager) GetWindowSnapService() *WindowSnapService {
|
func (sm *ServiceManager) GetWindowSnapService() *WindowSnapService {
|
||||||
return sm.windowSnapService
|
return sm.windowSnapService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBadgeService 获取badge服务实例
|
||||||
|
func (sm *ServiceManager) GetBadgeService() *badge.BadgeService {
|
||||||
|
return sm.badgeService
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNotificationService 获取通知服务实例
|
||||||
|
func (sm *ServiceManager) GetNotificationService() *notifications.NotificationService {
|
||||||
|
return sm.notificationService
|
||||||
|
}
|
||||||
|
|||||||
139
internal/services/test_service.go
Normal file
139
internal/services/test_service.go
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/services/badge"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/services/notifications"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestService 测试服务 - 仅在开发环境使用
|
||||||
|
type TestService struct {
|
||||||
|
logger *log.LogService
|
||||||
|
badgeService *badge.BadgeService
|
||||||
|
notificationService *notifications.NotificationService
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTestService 创建测试服务实例
|
||||||
|
func NewTestService(badgeService *badge.BadgeService, notificationService *notifications.NotificationService, logger *log.LogService) *TestService {
|
||||||
|
if logger == nil {
|
||||||
|
logger = log.New()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &TestService{
|
||||||
|
logger: logger,
|
||||||
|
badgeService: badgeService,
|
||||||
|
notificationService: notificationService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceStartup 服务启动时调用
|
||||||
|
func (ts *TestService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBadge 测试Badge功能
|
||||||
|
func (ts *TestService) TestBadge(text string) error {
|
||||||
|
if ts.badgeService == nil {
|
||||||
|
return fmt.Errorf("badge service not available")
|
||||||
|
}
|
||||||
|
|
||||||
|
if text == "" {
|
||||||
|
// 如果文本为空,则移除badge
|
||||||
|
err := ts.badgeService.RemoveBadge()
|
||||||
|
if err != nil {
|
||||||
|
ts.logger.Error("Failed to remove badge", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ts.logger.Info("Badge removed successfully")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置badge
|
||||||
|
err := ts.badgeService.SetBadge(text)
|
||||||
|
if err != nil {
|
||||||
|
ts.logger.Error("Failed to set badge", "text", text, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ts.logger.Info("Badge set successfully", "text", text)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNotification 测试通知功能
|
||||||
|
func (ts *TestService) TestNotification(title, subtitle, body string) error {
|
||||||
|
if ts.notificationService == nil {
|
||||||
|
return fmt.Errorf("notification service not available")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查通知授权(macOS需要)
|
||||||
|
authorized, err := ts.notificationService.CheckNotificationAuthorization()
|
||||||
|
if err != nil {
|
||||||
|
ts.logger.Error("Failed to check notification authorization", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !authorized {
|
||||||
|
authorized, err = ts.notificationService.RequestNotificationAuthorization()
|
||||||
|
if err != nil || !authorized {
|
||||||
|
ts.logger.Error("Failed to get notification authorization", "error", err)
|
||||||
|
return fmt.Errorf("notification authorization denied")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用默认值如果参数为空
|
||||||
|
if title == "" {
|
||||||
|
title = "Test Notification"
|
||||||
|
}
|
||||||
|
if subtitle == "" {
|
||||||
|
subtitle = "Testing notification system"
|
||||||
|
}
|
||||||
|
if body == "" {
|
||||||
|
body = "This is a test notification to verify the system is working correctly."
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送测试通知
|
||||||
|
err = ts.notificationService.SendNotification(notifications.NotificationOptions{
|
||||||
|
ID: "test_notification",
|
||||||
|
Title: title,
|
||||||
|
Subtitle: subtitle,
|
||||||
|
Body: body,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ts.logger.Error("Failed to send test notification", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ts.logger.Info("Test notification sent successfully", "title", title)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestUpdateNotification 测试更新通知
|
||||||
|
func (ts *TestService) TestUpdateNotification() error {
|
||||||
|
// 设置badge
|
||||||
|
if err := ts.TestBadge("●"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送更新通知
|
||||||
|
return ts.TestNotification(
|
||||||
|
"Voidraft Update Available",
|
||||||
|
"New version available",
|
||||||
|
"New version 1.2.3 available (current: 1.2.0)",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearAll 清除所有测试状态
|
||||||
|
func (ts *TestService) ClearAll() error {
|
||||||
|
// 移除badge
|
||||||
|
if err := ts.TestBadge(""); err != nil {
|
||||||
|
ts.logger.Error("Failed to clear badge during cleanup", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ts.logger.Info("Test states cleared successfully")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user