Skip to content

Commit 72ecc07

Browse files
committed
Refactor GitHub unlock resume flow
1 parent d99033a commit 72ecc07

2 files changed

Lines changed: 315 additions & 139 deletions

File tree

entry/src/main/ets/pages/SettingsPageV2.ets

Lines changed: 100 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,8 @@ import { loadChangelog, ChangelogEntry, ChangeItem, getChangeTypeConfig } from '
2929
import { InlineSegmentPicker } from '../components/InlineSegmentPicker';
3030
import { isAihdrSupported } from '../components/AihdrModifier';
3131
import { 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';
3934
import { 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

Comments
 (0)