@@ -29,13 +29,8 @@ import { loadChangelog, ChangelogEntry, ChangeItem, getChangeTypeConfig } from '
2929import { InlineSegmentPicker } from '../components/InlineSegmentPicker';
3030import { isAihdrSupported } from '../components/AihdrModifier';
3131import { DevKeyVerifier } from '../utils/DevKeyVerifier';
32- import {
33- GitHubDeviceCode,
34- GitHubStarCheck,
35- GitHubStarVerifier,
36- GitHubTokenPollResult,
37- GitHubTokenPollStatus
38- } from '../utils/GitHubStarVerifier';
32+ import { DeveloperUnlockResult, DeveloperUnlockService, DeveloperUnlockStatus } from '../service/DeveloperUnlockService';
33+ import { GitHubDeviceCode } from '../utils/GitHubStarVerifier';
3934import { ToastQueue } from '../utils/ToastQueue';
4035
4136/**
@@ -265,8 +260,8 @@ struct SettingsPageV2 {
265260 @State developerMode: boolean = false; // 开发者模式
266261 @State developerUnlockBusy: boolean = false;
267262 private devKeyActivated: boolean = false; // 激活码已验证过
268- private developerPendingDeviceCode: GitHubDeviceCode | null = null ;
269- private developerLastPollMs: number = 0 ;
263+ private developerPendingDialogVisible: boolean = false ;
264+ private developerPendingResumeBusy: boolean = false ;
270265
271266 // 应用版本
272267 @State appVersion: string = '';
@@ -351,6 +346,12 @@ struct SettingsPageV2 {
351346 this.loadDecoderCapabilities();
352347 }
353348
349+ onPageShow(): void {
350+ setTimeout(() => {
351+ this.resumeDeveloperPendingUnlock();
352+ }, 250);
353+ }
354+
354355 onBackPress(): boolean {
355356 router.back();
356357 return true;
@@ -605,14 +606,17 @@ struct SettingsPageV2 {
605606 this.resumeStream = await this.loadBoolean(SettingsKeys.RESUME_STREAM, false);
606607 this.developerMode = await this.loadBoolean(SettingsKeys.DEVELOPER_MODE, false);
607608 this.devKeyActivated = await this.loadBoolean(SettingsKeys.DEV_KEY_ACTIVATED, false);
608- await this.restoreDeveloperPendingDeviceCode ();
609+ await DeveloperUnlockService.restorePendingDeviceCode ();
609610
610611 // 备份
611612 this.autoBackupEnabled = await SettingsBackupService.isAutoBackupEnabled();
612613 } catch (error) {
613614 console.error('Failed to load settings:', error);
614615 } finally {
615616 this.isLoading = false;
617+ setTimeout(() => {
618+ this.resumeDeveloperPendingUnlock();
619+ }, 250);
616620 }
617621 }
618622
@@ -3715,10 +3719,10 @@ struct SettingsPageV2 {
37153719 return '已开启';
37163720 }
37173721 if (this.devKeyActivated) {
3718- return GitHubStarVerifier.isConfigured () ? '已验证 Star,点击开启' : '已授权,点击开启';
3722+ return DeveloperUnlockService.isGitHubConfigured () ? '已验证 Star,点击开启' : '已授权,点击开启';
37193723 }
3720- if (GitHubStarVerifier.isConfigured ()) {
3721- return `验证 ${GitHubStarVerifier .projectFullName()} Star 后开启`;
3724+ if (DeveloperUnlockService.isGitHubConfigured ()) {
3725+ return `验证 ${DeveloperUnlockService .projectFullName()} Star 后开启`;
37223726 }
37233727 return '需要激活码';
37243728 }
@@ -3741,7 +3745,7 @@ struct SettingsPageV2 {
37413745 return;
37423746 }
37433747
3744- if (GitHubStarVerifier.isConfigured ()) {
3748+ if (DeveloperUnlockService.isGitHubConfigured ()) {
37453749 this.showDeveloperUnlockDialog();
37463750 } else {
37473751 this.developerMode = true;
@@ -3750,17 +3754,19 @@ struct SettingsPageV2 {
37503754 }
37513755
37523756 private async showDeveloperUnlockDialog(): Promise<void> {
3753- const pending = this.developerPendingDeviceCode ?? await this.loadDeveloperPendingDeviceCode ();
3757+ const pending = await DeveloperUnlockService.restorePendingDeviceCode ();
37543758 if (pending) {
3755- this.developerPendingDeviceCode = pending;
3756- this.showDeveloperDeviceCodeDialog(pending);
3757- this.pollDeveloperPendingDeviceCode(false, true);
3759+ const result = await this.pollDeveloperPendingDeviceCode(false, true);
3760+ if (result.status === DeveloperUnlockStatus.PENDING && result.deviceCode &&
3761+ !this.devKeyActivated && !this.developerMode) {
3762+ this.showDeveloperDeviceCodeDialog(result.deviceCode);
3763+ }
37583764 return;
37593765 }
37603766
37613767 promptAction.showDialog({
37623768 title: '激活开发者模式',
3763- message: `开发者功能面向项目支持者开放。登录 GitHub 后,应用会验证当前账号是否 Star 了 ${GitHubStarVerifier .projectFullName()}。\n\n说明:该方式依赖 GitHub 第三方服务;若市场审核不允许外部平台互动解锁功能,可继续保留激活码方式用于市场包。`,
3769+ message: `开发者功能面向项目支持者开放。登录 GitHub 后,应用会验证当前账号是否 Star 了 ${DeveloperUnlockService .projectFullName()}。\n\n说明:该方式依赖 GitHub 第三方服务;若市场审核不允许外部平台互动解锁功能,可继续保留激活码方式用于市场包。`,
37643770 buttons: [
37653771 { text: '验证 Star', color: AppColors.Primary },
37663772 { text: '打开项目', color: AppColors.TextSecondary },
@@ -3770,7 +3776,7 @@ struct SettingsPageV2 {
37703776 if (result.index === 0) {
37713777 this.startDeveloperUnlockVerification();
37723778 } else if (result.index === 1) {
3773- this.openUrl(GitHubStarVerifier .projectUrl());
3779+ this.openUrl(DeveloperUnlockService .projectUrl());
37743780 } else if (result.index === 2) {
37753781 this.developerMode = true;
37763782 this.showDevKeyActivation();
@@ -3787,193 +3793,148 @@ struct SettingsPageV2 {
37873793 await this.requestDeveloperDeviceCode();
37883794 }
37893795
3796+ private async resumeDeveloperPendingUnlock(): Promise<void> {
3797+ if (this.isLoading || !DeveloperUnlockService.isGitHubConfigured() || this.developerMode || this.devKeyActivated ||
3798+ this.developerUnlockBusy || this.developerPendingResumeBusy || this.developerPendingDialogVisible) {
3799+ return;
3800+ }
3801+
3802+ this.developerPendingResumeBusy = true;
3803+ try {
3804+ const pending = await DeveloperUnlockService.restorePendingDeviceCode();
3805+ if (!pending) {
3806+ return;
3807+ }
3808+
3809+ const result = await this.pollDeveloperPendingDeviceCode(false, true);
3810+ if (result.status === DeveloperUnlockStatus.PENDING && result.deviceCode &&
3811+ !this.devKeyActivated && !this.developerMode) {
3812+ this.showDeveloperDeviceCodeDialog(result.deviceCode);
3813+ }
3814+ } finally {
3815+ this.developerPendingResumeBusy = false;
3816+ }
3817+ }
3818+
37903819 private async requestDeveloperDeviceCode(): Promise<void> {
37913820 this.developerUnlockBusy = true;
37923821 ToastQueue.show({ message: '正在发起 GitHub 授权...', duration: 2000 });
37933822 try {
3794- const deviceCode = await GitHubStarVerifier.requestDeviceCode();
3795- this.developerPendingDeviceCode = deviceCode;
3796- await this.saveDeveloperPendingDeviceCode(deviceCode);
3797- this.showDeveloperDeviceCodeDialog(deviceCode);
3798- } catch (err) {
3799- const message = (err as Error).message ?? String(err);
3800- ToastQueue.show({ message: `GitHub 授权失败: ${message}`, duration: 3000 });
3823+ const result = await DeveloperUnlockService.requestDeviceCode();
3824+ await this.applyDeveloperUnlockResult(result, true);
38013825 } finally {
38023826 this.developerUnlockBusy = false;
38033827 }
38043828 }
38053829
38063830 private showDeveloperDeviceCodeDialog(deviceCode: GitHubDeviceCode): void {
3831+ if (this.developerPendingDialogVisible) {
3832+ return;
3833+ }
3834+
3835+ this.developerPendingDialogVisible = true;
38073836 promptAction.showDialog({
38083837 title: 'GitHub Star 验证',
3809- message: `1. 打开授权页并输入代码:${deviceCode.userCode}\n2. 确认已 Star ${GitHubStarVerifier .projectFullName()}\n3. 回到这里点击“我已授权”\n\n授权页:${deviceCode.verificationUri}`,
3838+ message: `1. 打开授权页并输入代码:${deviceCode.userCode}\n2. 确认已 Star ${DeveloperUnlockService .projectFullName()}\n3. 回到这里点击“我已授权”\n\n授权页:${deviceCode.verificationUri}`,
38103839 buttons: [
38113840 { text: '打开授权页', color: AppColors.Primary },
38123841 { text: '我已授权', color: AppColors.Primary },
38133842 { text: '打开项目', color: AppColors.TextSecondary }
38143843 ]
38153844 }).then((result) => {
3845+ this.developerPendingDialogVisible = false;
38163846 if (result.index === 0) {
38173847 this.openUrl(deviceCode.verificationUriComplete ?? deviceCode.verificationUri);
38183848 } else if (result.index === 1) {
38193849 this.pollDeveloperPendingDeviceCode(true, false);
38203850 } else if (result.index === 2) {
3821- this.openUrl(GitHubStarVerifier .projectUrl());
3851+ this.openUrl(DeveloperUnlockService .projectUrl());
38223852 }
3853+ }).catch((err: Error) => {
3854+ this.developerPendingDialogVisible = false;
3855+ console.warn(`DeveloperUnlockService: 授权弹窗已关闭: ${err}`);
38233856 });
38243857 }
38253858
3826- private async pollDeveloperPendingDeviceCode(showPendingToast: boolean, enforceThrottle: boolean): Promise<void> {
3827- const deviceCode = this.developerPendingDeviceCode ?? await this.loadDeveloperPendingDeviceCode();
3828- if (!deviceCode) {
3829- if (showPendingToast) {
3830- ToastQueue.show({ message: 'GitHub 授权已过期,请重新开始', duration: 2500 });
3831- }
3832- return;
3833- }
3834-
3835- const nowMs = Date.now();
3836- if (enforceThrottle && nowMs - this.developerLastPollMs < 1500) {
3837- return;
3838- }
3839- this.developerLastPollMs = nowMs;
3859+ private async pollDeveloperPendingDeviceCode(
3860+ showPendingToast: boolean,
3861+ enforceThrottle: boolean
3862+ ): Promise<DeveloperUnlockResult> {
38403863 this.developerUnlockBusy = true;
3841-
38423864 try {
3843- const poll = await GitHubStarVerifier.pollAccessToken(deviceCode);
3844- await this.handleDeveloperTokenPollResult(poll, deviceCode, showPendingToast);
3845- } catch (err) {
3846- const message = (err as Error).message ?? String(err);
3847- await this.failDeveloperUnlockVerification(message);
3865+ const result = await DeveloperUnlockService.pollPendingDeviceCode(enforceThrottle);
3866+ await this.applyDeveloperUnlockResult(result, showPendingToast);
3867+ return result;
38483868 } finally {
38493869 this.developerUnlockBusy = false;
38503870 }
38513871 }
38523872
3853- private async handleDeveloperTokenPollResult(
3854- poll: GitHubTokenPollResult,
3855- deviceCode: GitHubDeviceCode,
3873+ private async applyDeveloperUnlockResult(
3874+ result: DeveloperUnlockResult,
38563875 showPendingToast: boolean
38573876 ): Promise<void> {
3858- switch (poll.status) {
3859- case GitHubTokenPollStatus.AUTHORIZED:
3860- if (!poll.accessToken) {
3861- await this.failDeveloperUnlockVerification('GitHub 未返回 access token');
3862- return;
3877+ switch (result.status) {
3878+ case DeveloperUnlockStatus.REQUESTED:
3879+ if (result.deviceCode) {
3880+ this.showDeveloperDeviceCodeDialog(result.deviceCode);
38633881 }
3864- const starCheck = await GitHubStarVerifier.checkStar(poll.accessToken);
3865- await this.completeDeveloperUnlockVerification(starCheck);
38663882 break;
3867- case GitHubTokenPollStatus.PENDING:
3868- case GitHubTokenPollStatus.SLOW_DOWN:
3869- if (poll.intervalSeconds) {
3870- deviceCode.intervalSeconds = poll.intervalSeconds;
3871- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_PENDING_INTERVAL, deviceCode.intervalSeconds);
3872- }
3883+ case DeveloperUnlockStatus.PENDING:
38733884 if (showPendingToast) {
38743885 ToastQueue.show({ message: '仍在等待 GitHub 授权,请稍后再试', duration: 2500 });
38753886 }
38763887 break;
3877- case GitHubTokenPollStatus.FAILED:
3878- await this.failDeveloperUnlockVerification(poll.message ?? 'GitHub 授权失败');
3888+ case DeveloperUnlockStatus.VERIFIED:
3889+ await this.activateDeveloperMode(result.login);
3890+ break;
3891+ case DeveloperUnlockStatus.NOT_STARRED:
3892+ await this.showDeveloperNotStarredDialog();
3893+ break;
3894+ case DeveloperUnlockStatus.EXPIRED:
3895+ if (showPendingToast) {
3896+ ToastQueue.show({ message: result.message ?? 'GitHub 授权已过期,请重新开始', duration: 2500 });
3897+ }
3898+ break;
3899+ case DeveloperUnlockStatus.FAILED:
3900+ await this.failDeveloperUnlockVerification(result.message ?? 'GitHub 授权失败');
38793901 break;
38803902 }
38813903 }
38823904
3883- private async completeDeveloperUnlockVerification(starCheck: GitHubStarCheck): Promise<void> {
3884- await this.clearDeveloperPendingDeviceCode();
3885- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_LOGIN, starCheck.login ?? '');
3886-
3887- if (starCheck.starred) {
3888- this.developerMode = true;
3889- this.devKeyActivated = true;
3890- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_VERIFIED_AT, Date.now());
3891- await this.saveSetting(SettingsKeys.DEVELOPER_MODE, true);
3892- await this.saveSetting(SettingsKeys.DEV_KEY_ACTIVATED, true);
3893- const suffix = starCheck.login ? `,${starCheck.login}` : '';
3894- ToastQueue.show({ message: `开发者模式已激活${suffix}`, duration: 2500 });
3895- return;
3896- }
3905+ private async activateDeveloperMode(login?: string): Promise<void> {
3906+ this.developerMode = true;
3907+ this.devKeyActivated = true;
3908+ await this.saveSetting(SettingsKeys.DEVELOPER_MODE, true);
3909+ await this.saveSetting(SettingsKeys.DEV_KEY_ACTIVATED, true);
3910+ const suffix = login ? `,${login}` : '';
3911+ ToastQueue.show({ message: `开发者模式已激活${suffix}`, duration: 2500 });
3912+ }
38973913
3914+ private async showDeveloperNotStarredDialog(): Promise<void> {
38983915 this.developerMode = false;
38993916 this.devKeyActivated = false;
39003917 await this.saveSetting(SettingsKeys.DEVELOPER_MODE, false);
39013918 await this.saveSetting(SettingsKeys.DEV_KEY_ACTIVATED, false);
39023919 promptAction.showDialog({
39033920 title: '未检测到 Star',
3904- message: `GitHub 授权成功,但当前账号尚未 Star ${GitHubStarVerifier .projectFullName()}。如果刚刚 Star,请稍等片刻后重新验证。`,
3921+ message: `GitHub 授权成功,但当前账号尚未 Star ${DeveloperUnlockService .projectFullName()}。如果刚刚 Star,请稍等片刻后重新验证。`,
39053922 buttons: [
39063923 { text: '打开项目', color: AppColors.Primary },
39073924 { text: '取消', color: AppColors.TextSecondary }
39083925 ]
39093926 }).then((result) => {
39103927 if (result.index === 0) {
3911- this.openUrl(GitHubStarVerifier .projectUrl());
3928+ this.openUrl(DeveloperUnlockService .projectUrl());
39123929 }
39133930 });
39143931 }
39153932
39163933 private async failDeveloperUnlockVerification(message: string): Promise<void> {
3917- await this.clearDeveloperPendingDeviceCode();
39183934 this.developerMode = false;
39193935 ToastQueue.show({ message: `开发者模式验证失败: ${message}`, duration: 3000 });
39203936 }
39213937
3922- private async saveDeveloperPendingDeviceCode(deviceCode: GitHubDeviceCode): Promise<void> {
3923- const expiresAt = Date.now() + deviceCode.expiresInSeconds * 1000;
3924- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_PENDING_DEVICE_CODE, deviceCode.deviceCode);
3925- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_PENDING_USER_CODE, deviceCode.userCode);
3926- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_PENDING_VERIFICATION_URI, deviceCode.verificationUri);
3927- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_PENDING_VERIFICATION_URI_COMPLETE, deviceCode.verificationUriComplete ?? '');
3928- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_PENDING_EXPIRES_AT, expiresAt);
3929- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_PENDING_INTERVAL, deviceCode.intervalSeconds);
3930- }
3931-
3932- private async loadDeveloperPendingDeviceCode(): Promise<GitHubDeviceCode | null> {
3933- const expiresAt = await PreferencesUtil.get<number>(SettingsKeys.DEV_GITHUB_PENDING_EXPIRES_AT, 0);
3934- const remainingSeconds = Math.floor((expiresAt - Date.now()) / 1000);
3935- if (remainingSeconds <= 0) {
3936- await this.clearDeveloperPendingDeviceCode();
3937- return null;
3938- }
3939-
3940- const deviceCode = await PreferencesUtil.get<string>(SettingsKeys.DEV_GITHUB_PENDING_DEVICE_CODE, '');
3941- const userCode = await PreferencesUtil.get<string>(SettingsKeys.DEV_GITHUB_PENDING_USER_CODE, '');
3942- const verificationUri = await PreferencesUtil.get<string>(SettingsKeys.DEV_GITHUB_PENDING_VERIFICATION_URI, '');
3943- if (!deviceCode || !userCode || !verificationUri) {
3944- await this.clearDeveloperPendingDeviceCode();
3945- return null;
3946- }
3947-
3948- const verificationUriComplete = await PreferencesUtil.get<string>(
3949- SettingsKeys.DEV_GITHUB_PENDING_VERIFICATION_URI_COMPLETE,
3950- ''
3951- );
3952- const intervalSeconds = await PreferencesUtil.get<number>(SettingsKeys.DEV_GITHUB_PENDING_INTERVAL, 5);
3953- return new GitHubDeviceCode(
3954- deviceCode,
3955- userCode,
3956- verificationUri,
3957- verificationUriComplete || undefined,
3958- remainingSeconds,
3959- Math.max(1, intervalSeconds)
3960- );
3961- }
3962-
3963- private async restoreDeveloperPendingDeviceCode(): Promise<void> {
3964- this.developerPendingDeviceCode = await this.loadDeveloperPendingDeviceCode();
3965- }
3966-
3967- private async clearDeveloperPendingDeviceCode(): Promise<void> {
3968- this.developerPendingDeviceCode = null;
3969- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_PENDING_DEVICE_CODE, '');
3970- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_PENDING_USER_CODE, '');
3971- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_PENDING_VERIFICATION_URI, '');
3972- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_PENDING_VERIFICATION_URI_COMPLETE, '');
3973- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_PENDING_EXPIRES_AT, 0);
3974- await PreferencesUtil.put(SettingsKeys.DEV_GITHUB_PENDING_INTERVAL, 0);
3975- }
3976-
39773938 /**
39783939 * 显示开发者模式激活码输入
39793940 */
0 commit comments