Merge branch 'dev' into master
This commit is contained in:
@@ -0,0 +1,403 @@
|
||||
(function attachSidepanelContributionMode(globalScope) {
|
||||
const ACTIVE_STATUSES = new Set(['started', 'waiting', 'processing']);
|
||||
const FINAL_STATUSES = new Set(['auto_approved', 'auto_rejected', 'manual_review_required', 'expired', 'error']);
|
||||
const DEFAULT_COPY = '当前账号将用于支持项目维护。扩展会自动申请贡献登录地址并持续跟踪授权状态;如检测到回调地址,会自动提交,无需手动复制。';
|
||||
|
||||
function createContributionModeManager(context = {}) {
|
||||
const {
|
||||
state,
|
||||
dom,
|
||||
helpers,
|
||||
runtime,
|
||||
constants = {},
|
||||
} = context;
|
||||
|
||||
const contributionUploadUrl = constants.contributionUploadUrl || 'https://apikey.qzz.io/';
|
||||
const pollIntervalMs = Math.max(1500, Math.floor(Number(constants.pollIntervalMs) || 2500));
|
||||
|
||||
const hiddenRows = [
|
||||
dom.rowVpsUrl,
|
||||
dom.rowVpsPassword,
|
||||
dom.rowLocalCpaStep9Mode,
|
||||
dom.rowSub2ApiUrl,
|
||||
dom.rowSub2ApiEmail,
|
||||
dom.rowSub2ApiPassword,
|
||||
dom.rowSub2ApiGroup,
|
||||
dom.rowSub2ApiDefaultProxy,
|
||||
dom.rowCustomPassword,
|
||||
dom.rowAccountRunHistoryTextEnabled,
|
||||
dom.rowAccountRunHistoryHelperBaseUrl,
|
||||
].filter(Boolean);
|
||||
|
||||
let actionInFlight = false;
|
||||
let pollInFlight = false;
|
||||
let pollTimer = null;
|
||||
|
||||
function getLatestState() {
|
||||
return state.getLatestState?.() || {};
|
||||
}
|
||||
|
||||
function normalizeString(value = '') {
|
||||
return String(value || '').trim();
|
||||
}
|
||||
|
||||
function normalizeStatus(value = '') {
|
||||
const normalized = normalizeString(value).toLowerCase();
|
||||
if (ACTIVE_STATUSES.has(normalized) || FINAL_STATUSES.has(normalized)) {
|
||||
return normalized;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function normalizeCallbackStatus(value = '') {
|
||||
const normalized = normalizeString(value).toLowerCase();
|
||||
switch (normalized) {
|
||||
case 'waiting':
|
||||
case 'captured':
|
||||
case 'submitting':
|
||||
case 'submitted':
|
||||
case 'not_required':
|
||||
case 'failed':
|
||||
case 'idle':
|
||||
return normalized;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function isContributionModeEnabled(currentState = getLatestState()) {
|
||||
return Boolean(currentState.contributionMode);
|
||||
}
|
||||
|
||||
function hasActiveContributionSession(currentState = getLatestState()) {
|
||||
const status = normalizeStatus(currentState.contributionStatus);
|
||||
return Boolean(normalizeString(currentState.contributionSessionId) && status && !FINAL_STATUSES.has(status));
|
||||
}
|
||||
|
||||
function isModeSwitchBlocked() {
|
||||
return Boolean(helpers.isModeSwitchBlocked?.(getLatestState()));
|
||||
}
|
||||
|
||||
function setContributionHidden(element, hidden) {
|
||||
element?.classList.toggle('is-contribution-hidden', hidden);
|
||||
}
|
||||
|
||||
function syncContributionRows(enabled) {
|
||||
hiddenRows.forEach((row) => {
|
||||
setContributionHidden(row, enabled);
|
||||
});
|
||||
}
|
||||
|
||||
function syncContributionButton(enabled, blocked) {
|
||||
if (!dom.btnContributionMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
dom.btnContributionMode.classList.toggle('is-active', enabled);
|
||||
dom.btnContributionMode.setAttribute('aria-pressed', String(enabled));
|
||||
dom.btnContributionMode.disabled = enabled || blocked;
|
||||
dom.btnContributionMode.title = blocked
|
||||
? '当前流程运行中,暂时不能切换贡献模式'
|
||||
: (enabled ? '当前已在贡献模式' : '进入贡献模式');
|
||||
}
|
||||
|
||||
function stopPolling() {
|
||||
if (pollTimer) {
|
||||
clearTimeout(pollTimer);
|
||||
pollTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function schedulePolling(delayMs = pollIntervalMs) {
|
||||
stopPolling();
|
||||
if (!isContributionModeEnabled() || !hasActiveContributionSession()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pollTimer = setTimeout(() => {
|
||||
pollOnce({ silentError: true }).catch(() => {});
|
||||
}, delayMs);
|
||||
}
|
||||
|
||||
function ensurePolling() {
|
||||
if (!isContributionModeEnabled() || !hasActiveContributionSession()) {
|
||||
stopPolling();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pollTimer && !pollInFlight) {
|
||||
schedulePolling(1200);
|
||||
}
|
||||
}
|
||||
|
||||
function getOauthStatusText(currentState = getLatestState()) {
|
||||
const status = normalizeStatus(currentState.contributionStatus);
|
||||
const hasAuthUrl = Boolean(normalizeString(currentState.contributionAuthUrl));
|
||||
if (!normalizeString(currentState.contributionSessionId) || !hasAuthUrl) {
|
||||
return '未生成登录地址';
|
||||
}
|
||||
if (status === 'waiting') {
|
||||
return '等待授权完成';
|
||||
}
|
||||
if (status === 'processing' || status === 'auto_approved' || status === 'auto_rejected' || status === 'manual_review_required') {
|
||||
return '授权已完成';
|
||||
}
|
||||
if (status === 'expired' || status === 'error') {
|
||||
return '授权失败';
|
||||
}
|
||||
if (Number(currentState.contributionAuthOpenedAt) > 0) {
|
||||
return '已打开授权页';
|
||||
}
|
||||
return '登录地址已生成';
|
||||
}
|
||||
|
||||
function getCallbackStatusText(currentState = getLatestState()) {
|
||||
const status = normalizeCallbackStatus(currentState.contributionCallbackStatus);
|
||||
switch (status) {
|
||||
case 'captured':
|
||||
return '已捕获回调地址';
|
||||
case 'submitting':
|
||||
return '正在提交回调';
|
||||
case 'submitted':
|
||||
return '已提交回调';
|
||||
case 'not_required':
|
||||
return '当前流程无需手动回调';
|
||||
case 'failed':
|
||||
return '回调提交失败';
|
||||
case 'waiting':
|
||||
case 'idle':
|
||||
default:
|
||||
return normalizeString(currentState.contributionCallbackUrl)
|
||||
? '已捕获回调地址'
|
||||
: '等待回调';
|
||||
}
|
||||
}
|
||||
|
||||
function getSummaryText(currentState = getLatestState()) {
|
||||
return normalizeString(currentState.contributionStatusMessage) || DEFAULT_COPY;
|
||||
}
|
||||
|
||||
async function requestContributionMode(enabled) {
|
||||
const response = await runtime.sendMessage({
|
||||
type: 'SET_CONTRIBUTION_MODE',
|
||||
source: 'sidepanel',
|
||||
payload: { enabled: Boolean(enabled) },
|
||||
});
|
||||
|
||||
if (response?.error) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
if (!response?.state) {
|
||||
throw new Error('贡献模式切换后未返回最新状态。');
|
||||
}
|
||||
|
||||
helpers.applySettingsState?.(response.state);
|
||||
helpers.updateStatusDisplay?.(response.state);
|
||||
render();
|
||||
}
|
||||
|
||||
async function pollOnce(options = {}) {
|
||||
if (pollInFlight || !isContributionModeEnabled() || !hasActiveContributionSession()) {
|
||||
if (!hasActiveContributionSession()) {
|
||||
stopPolling();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
pollInFlight = true;
|
||||
try {
|
||||
const response = await runtime.sendMessage({
|
||||
type: 'POLL_CONTRIBUTION_STATUS',
|
||||
source: 'sidepanel',
|
||||
payload: {
|
||||
reason: options.reason || 'sidepanel_poll',
|
||||
},
|
||||
});
|
||||
|
||||
if (response?.error) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
if (response?.state) {
|
||||
helpers.applySettingsState?.(response.state);
|
||||
helpers.updateStatusDisplay?.(response.state);
|
||||
}
|
||||
} finally {
|
||||
pollInFlight = false;
|
||||
render();
|
||||
if (hasActiveContributionSession()) {
|
||||
schedulePolling();
|
||||
} else {
|
||||
stopPolling();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function startContributionFlow() {
|
||||
if (typeof helpers.startContributionAutoRun !== 'function') {
|
||||
throw new Error('贡献模式尚未接入主自动流程启动能力。');
|
||||
}
|
||||
|
||||
const started = await helpers.startContributionAutoRun();
|
||||
if (!started) {
|
||||
return;
|
||||
}
|
||||
|
||||
helpers.showToast?.('贡献自动流程已启动。', 'info', 1800);
|
||||
render();
|
||||
}
|
||||
|
||||
async function enterContributionMode() {
|
||||
const confirmed = await helpers.openConfirmModal?.({
|
||||
title: '贡献账号',
|
||||
message: '是否确认给作者 QLHazyCoder 提供账号以支持继续维护项目?',
|
||||
confirmLabel: '确定',
|
||||
confirmVariant: 'btn-primary',
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
await requestContributionMode(true);
|
||||
helpers.showToast?.('已进入贡献模式。', 'success', 1800);
|
||||
}
|
||||
|
||||
async function exitContributionMode() {
|
||||
stopPolling();
|
||||
await requestContributionMode(false);
|
||||
helpers.showToast?.('已退出贡献模式。', 'info', 1800);
|
||||
}
|
||||
|
||||
function render() {
|
||||
const currentState = getLatestState();
|
||||
const enabled = isContributionModeEnabled(currentState);
|
||||
const blocked = isModeSwitchBlocked();
|
||||
|
||||
if (enabled && dom.selectPanelMode) {
|
||||
dom.selectPanelMode.value = 'cpa';
|
||||
}
|
||||
|
||||
helpers.updatePanelModeUI?.();
|
||||
helpers.updateAccountRunHistorySettingsUI?.();
|
||||
|
||||
if (dom.contributionModePanel) {
|
||||
dom.contributionModePanel.hidden = !enabled;
|
||||
}
|
||||
if (dom.contributionModeText) {
|
||||
dom.contributionModeText.textContent = DEFAULT_COPY;
|
||||
}
|
||||
if (dom.contributionOauthStatus) {
|
||||
dom.contributionOauthStatus.textContent = getOauthStatusText(currentState);
|
||||
}
|
||||
if (dom.contributionCallbackStatus) {
|
||||
dom.contributionCallbackStatus.textContent = getCallbackStatusText(currentState);
|
||||
}
|
||||
if (dom.contributionModeSummary) {
|
||||
dom.contributionModeSummary.textContent = getSummaryText(currentState);
|
||||
}
|
||||
|
||||
syncContributionRows(enabled);
|
||||
syncContributionButton(enabled, blocked);
|
||||
|
||||
if (dom.selectPanelMode) {
|
||||
dom.selectPanelMode.disabled = enabled;
|
||||
}
|
||||
|
||||
if (dom.btnStartContribution) {
|
||||
dom.btnStartContribution.disabled = actionInFlight || blocked;
|
||||
}
|
||||
|
||||
if (dom.btnOpenContributionUpload) {
|
||||
dom.btnOpenContributionUpload.disabled = false;
|
||||
}
|
||||
|
||||
if (dom.btnExitContributionMode) {
|
||||
dom.btnExitContributionMode.disabled = actionInFlight || blocked;
|
||||
dom.btnExitContributionMode.title = blocked ? '当前流程运行中,暂时不能退出贡献模式' : '退出贡献模式';
|
||||
}
|
||||
|
||||
if (dom.btnOpenAccountRecords) {
|
||||
dom.btnOpenAccountRecords.disabled = enabled;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
helpers.closeConfigMenu?.();
|
||||
helpers.closeAccountRecordsPanel?.();
|
||||
ensurePolling();
|
||||
} else {
|
||||
stopPolling();
|
||||
}
|
||||
|
||||
helpers.updateConfigMenuControls?.();
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
dom.btnContributionMode?.addEventListener('click', async () => {
|
||||
if (actionInFlight) {
|
||||
return;
|
||||
}
|
||||
actionInFlight = true;
|
||||
render();
|
||||
try {
|
||||
await enterContributionMode();
|
||||
} catch (error) {
|
||||
helpers.showToast?.(error.message, 'error');
|
||||
} finally {
|
||||
actionInFlight = false;
|
||||
render();
|
||||
}
|
||||
});
|
||||
|
||||
dom.btnStartContribution?.addEventListener('click', async () => {
|
||||
if (actionInFlight) {
|
||||
return;
|
||||
}
|
||||
actionInFlight = true;
|
||||
render();
|
||||
try {
|
||||
await startContributionFlow();
|
||||
} catch (error) {
|
||||
helpers.showToast?.(error.message, 'error');
|
||||
} finally {
|
||||
actionInFlight = false;
|
||||
render();
|
||||
}
|
||||
});
|
||||
|
||||
dom.btnOpenContributionUpload?.addEventListener('click', () => {
|
||||
try {
|
||||
helpers.openExternalUrl?.(contributionUploadUrl);
|
||||
} catch (error) {
|
||||
helpers.showToast?.(`打开上传页面失败:${error.message}`, 'error');
|
||||
}
|
||||
});
|
||||
|
||||
dom.btnExitContributionMode?.addEventListener('click', async () => {
|
||||
if (actionInFlight) {
|
||||
return;
|
||||
}
|
||||
actionInFlight = true;
|
||||
render();
|
||||
try {
|
||||
await exitContributionMode();
|
||||
} catch (error) {
|
||||
helpers.showToast?.(error.message, 'error');
|
||||
} finally {
|
||||
actionInFlight = false;
|
||||
render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
bindEvents,
|
||||
pollOnce,
|
||||
render,
|
||||
stopPolling,
|
||||
};
|
||||
}
|
||||
|
||||
globalScope.SidepanelContributionMode = {
|
||||
createContributionModeManager,
|
||||
};
|
||||
})(typeof window !== 'undefined' ? window : globalThis);
|
||||
+112
-1
@@ -100,7 +100,9 @@ body {
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 200;
|
||||
@@ -228,12 +230,19 @@ header {
|
||||
gap: 4px;
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.btn-contribution-mode {
|
||||
padding-inline: 10px;
|
||||
}
|
||||
|
||||
.btn-contribution-mode.is-active {
|
||||
border-color: color-mix(in srgb, var(--orange) 58%, var(--border));
|
||||
background: color-mix(in srgb, var(--orange) 14%, var(--bg-base));
|
||||
color: var(--orange);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Theme Toggle
|
||||
============================================================ */
|
||||
@@ -541,6 +550,108 @@ header {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.is-contribution-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.contribution-mode-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 12px;
|
||||
border: 1px solid color-mix(in srgb, var(--orange) 28%, var(--border));
|
||||
border-radius: var(--radius-md);
|
||||
background:
|
||||
linear-gradient(180deg,
|
||||
color-mix(in srgb, var(--orange) 10%, var(--bg-base)),
|
||||
color-mix(in srgb, var(--bg-surface) 96%, var(--bg-base))
|
||||
);
|
||||
}
|
||||
|
||||
.contribution-mode-panel[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.contribution-mode-panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.contribution-mode-badge {
|
||||
padding: 4px 8px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--orange) 16%, var(--bg-base));
|
||||
color: var(--orange);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.contribution-mode-text {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.contribution-mode-status-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.contribution-mode-status-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: color-mix(in srgb, var(--bg-base) 92%, var(--orange-soft));
|
||||
}
|
||||
|
||||
.contribution-mode-status-label {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.contribution-mode-status-value {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.contribution-mode-summary {
|
||||
padding: 10px 12px;
|
||||
border-radius: var(--radius-sm);
|
||||
background: color-mix(in srgb, var(--bg-base) 88%, var(--orange-soft));
|
||||
color: var(--text-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.contribution-mode-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.contribution-mode-actions .btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media (max-width: 540px) {
|
||||
.contribution-mode-status-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.section-mini-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10" />
|
||||
</svg>
|
||||
</button>
|
||||
<button id="btn-contribution-mode" class="btn btn-outline btn-sm btn-contribution-mode" type="button"
|
||||
aria-pressed="false">贡献</button>
|
||||
<button id="btn-theme" class="theme-toggle" title="切换主题">
|
||||
<svg class="icon-moon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
@@ -104,6 +106,29 @@
|
||||
<option value="sub2api">SUB2API</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="contribution-mode-panel" class="contribution-mode-panel" hidden>
|
||||
<div class="contribution-mode-panel-header">
|
||||
<span class="section-label">贡献模式</span>
|
||||
<span class="contribution-mode-badge">CPA</span>
|
||||
</div>
|
||||
<p id="contribution-mode-text" class="contribution-mode-text">当前账号将用于支持项目维护。扩展会自动申请贡献登录地址并持续跟踪授权状态;如检测到回调地址,会自动提交,无需手动复制。</p>
|
||||
<div class="contribution-mode-status-grid">
|
||||
<div class="contribution-mode-status-card">
|
||||
<span class="contribution-mode-status-label">OAUTH</span>
|
||||
<span id="contribution-oauth-status" class="contribution-mode-status-value">未生成登录地址</span>
|
||||
</div>
|
||||
<div class="contribution-mode-status-card">
|
||||
<span class="contribution-mode-status-label">回调</span>
|
||||
<span id="contribution-callback-status" class="contribution-mode-status-value">等待回调</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="contribution-mode-summary" class="contribution-mode-summary">等待开始贡献</div>
|
||||
<div class="contribution-mode-actions">
|
||||
<button id="btn-start-contribution" class="btn btn-primary btn-sm" type="button">开始贡献</button>
|
||||
<button id="btn-open-contribution-upload" class="btn btn-outline btn-sm" type="button">已有认证文件?前往上传</button>
|
||||
<button id="btn-exit-contribution-mode" class="btn btn-ghost btn-sm" type="button">退出贡献模式</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-vps-url">
|
||||
<span class="data-label">CPA</span>
|
||||
<div class="input-with-icon">
|
||||
@@ -151,7 +176,7 @@
|
||||
<input type="text" id="input-sub2api-default-proxy" class="data-input"
|
||||
placeholder="留空则不使用代理;或填写代理名称 / ID" />
|
||||
</div>
|
||||
<div class="data-row">
|
||||
<div class="data-row" id="row-custom-password">
|
||||
<span class="data-label">账户密码</span>
|
||||
<div class="input-with-icon">
|
||||
<input type="password" id="input-password" class="data-input data-input-with-icon"
|
||||
@@ -292,16 +317,21 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row">
|
||||
<div class="data-row" id="row-account-run-history-text-enabled">
|
||||
<span class="data-label">本地同步</span>
|
||||
<div class="data-inline auto-delay-inline">
|
||||
<div class="data-inline">
|
||||
<label class="toggle-switch" for="input-account-run-history-text-enabled">
|
||||
<input type="checkbox" id="input-account-run-history-text-enabled" />
|
||||
<span class="toggle-switch-track" aria-hidden="true">
|
||||
<span class="toggle-switch-thumb"></span>
|
||||
</span>
|
||||
</label>
|
||||
<div class="timing-field timing-field-right setting-inline-right">
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-verification-resend-count">
|
||||
<span class="data-label">验证码</span>
|
||||
<div class="data-inline">
|
||||
<div class="timing-field timing-field-labeled timing-field-right setting-inline-right">
|
||||
<span class="auto-delay-caption">验证码重发</span>
|
||||
<div class="auto-delay-controls">
|
||||
<input type="number" id="input-verification-resend-count" class="data-input auto-delay-input" value="4"
|
||||
@@ -628,6 +658,7 @@
|
||||
<script src="hotmail-manager.js"></script>
|
||||
<script src="icloud-manager.js"></script>
|
||||
<script src="luckmail-manager.js"></script>
|
||||
<script src="contribution-mode.js"></script>
|
||||
<script src="account-records-manager.js"></script>
|
||||
<script src="sidepanel.js"></script>
|
||||
</body>
|
||||
|
||||
+165
-10
@@ -33,6 +33,14 @@ const updateCardSummary = document.getElementById('update-card-summary');
|
||||
const updateReleaseList = document.getElementById('update-release-list');
|
||||
const btnOpenRelease = document.getElementById('btn-open-release');
|
||||
const settingsCard = document.getElementById('settings-card');
|
||||
const contributionModePanel = document.getElementById('contribution-mode-panel');
|
||||
const contributionModeText = document.getElementById('contribution-mode-text');
|
||||
const contributionOauthStatus = document.getElementById('contribution-oauth-status');
|
||||
const contributionCallbackStatus = document.getElementById('contribution-callback-status');
|
||||
const contributionModeSummary = document.getElementById('contribution-mode-summary');
|
||||
const btnStartContribution = document.getElementById('btn-start-contribution');
|
||||
const btnOpenContributionUpload = document.getElementById('btn-open-contribution-upload');
|
||||
const btnExitContributionMode = document.getElementById('btn-exit-contribution-mode');
|
||||
const displayOauthUrl = document.getElementById('display-oauth-url');
|
||||
const displayLocalhostUrl = document.getElementById('display-localhost-url');
|
||||
const displayStatus = document.getElementById('display-status');
|
||||
@@ -80,6 +88,7 @@ const rowSub2ApiGroup = document.getElementById('row-sub2api-group');
|
||||
const inputSub2ApiGroup = document.getElementById('input-sub2api-group');
|
||||
const rowSub2ApiDefaultProxy = document.getElementById('row-sub2api-default-proxy');
|
||||
const inputSub2ApiDefaultProxy = document.getElementById('input-sub2api-default-proxy');
|
||||
const rowCustomPassword = document.getElementById('row-custom-password');
|
||||
const selectMailProvider = document.getElementById('select-mail-provider');
|
||||
const btnMailLogin = document.getElementById('btn-mail-login');
|
||||
const rowMail2925Mode = document.getElementById('row-mail-2925-mode');
|
||||
@@ -175,6 +184,7 @@ const inputAutoDelayEnabled = document.getElementById('input-auto-delay-enabled'
|
||||
const inputAutoDelayMinutes = document.getElementById('input-auto-delay-minutes');
|
||||
const inputAutoStepDelaySeconds = document.getElementById('input-auto-step-delay-seconds');
|
||||
const inputVerificationResendCount = document.getElementById('input-verification-resend-count');
|
||||
const rowAccountRunHistoryTextEnabled = document.getElementById('row-account-run-history-text-enabled');
|
||||
const inputAccountRunHistoryTextEnabled = document.getElementById('input-account-run-history-text-enabled');
|
||||
const rowAccountRunHistoryHelperBaseUrl = document.getElementById('row-account-run-history-helper-base-url');
|
||||
const inputAccountRunHistoryHelperBaseUrl = document.getElementById('input-account-run-history-helper-base-url');
|
||||
@@ -776,18 +786,23 @@ async function openAutoRunFallbackRiskConfirmModal(totalRuns, fallbackThreadInte
|
||||
|
||||
function updateConfigMenuControls() {
|
||||
const disabled = configActionInFlight || settingsSaveInFlight;
|
||||
const contributionModeEnabled = Boolean(latestState?.contributionMode);
|
||||
if (contributionModeEnabled && configMenuOpen) {
|
||||
configMenuOpen = false;
|
||||
}
|
||||
const importLocked = disabled
|
||||
|| contributionModeEnabled
|
||||
|| currentAutoRun.autoRunning
|
||||
|| Object.values(getStepStatuses()).some((status) => status === 'running');
|
||||
if (btnConfigMenu) {
|
||||
btnConfigMenu.disabled = disabled;
|
||||
btnConfigMenu.disabled = disabled || contributionModeEnabled;
|
||||
btnConfigMenu.setAttribute('aria-expanded', String(configMenuOpen));
|
||||
}
|
||||
if (configMenu) {
|
||||
configMenu.hidden = !configMenuOpen;
|
||||
configMenu.hidden = contributionModeEnabled || !configMenuOpen;
|
||||
}
|
||||
if (btnExportSettings) {
|
||||
btnExportSettings.disabled = disabled;
|
||||
btnExportSettings.disabled = disabled || contributionModeEnabled;
|
||||
}
|
||||
if (btnImportSettings) {
|
||||
btnImportSettings.disabled = importLocked;
|
||||
@@ -870,6 +885,12 @@ function hasSavedProgress(state = latestState) {
|
||||
return Object.values(statuses).some((status) => status !== 'pending');
|
||||
}
|
||||
|
||||
function isContributionModeSwitchBlocked(state = latestState) {
|
||||
const statuses = getStepStatuses(state);
|
||||
const anyRunning = Object.values(statuses).some((status) => status === 'running');
|
||||
return anyRunning || isAutoRunLockedPhase() || isAutoRunPausedPhase() || isAutoRunScheduledPhase();
|
||||
}
|
||||
|
||||
function shouldOfferAutoModeChoice(state = latestState) {
|
||||
return hasSavedProgress(state) && getFirstUnfinishedStep(state) !== null;
|
||||
}
|
||||
@@ -1319,6 +1340,7 @@ function collectSettingsPayload() {
|
||||
const selectedCloudflareTempEmailDomain = normalizeCloudflareTempEmailDomainValue(
|
||||
!cloudflareTempEmailDomainEditMode ? selectTempEmailDomain.value : tempEmailActiveDomain
|
||||
) || tempEmailActiveDomain;
|
||||
const contributionModeEnabled = Boolean(latestState?.contributionMode);
|
||||
return {
|
||||
panelMode: selectPanelMode.value,
|
||||
vpsUrl: inputVpsUrl.value.trim(),
|
||||
@@ -1329,14 +1351,18 @@ function collectSettingsPayload() {
|
||||
sub2apiPassword: inputSub2ApiPassword.value,
|
||||
sub2apiGroupName: inputSub2ApiGroup.value.trim(),
|
||||
sub2apiDefaultProxyName: inputSub2ApiDefaultProxy.value.trim(),
|
||||
customPassword: inputPassword.value,
|
||||
...(contributionModeEnabled ? {} : {
|
||||
customPassword: inputPassword.value,
|
||||
}),
|
||||
mailProvider: selectMailProvider.value,
|
||||
mail2925Mode: getSelectedMail2925Mode(),
|
||||
emailGenerator: selectEmailGenerator.value,
|
||||
autoDeleteUsedIcloudAlias: checkboxAutoDeleteIcloud?.checked,
|
||||
icloudHostPreference: selectIcloudHostPreference?.value || 'auto',
|
||||
accountRunHistoryTextEnabled: Boolean(inputAccountRunHistoryTextEnabled?.checked),
|
||||
accountRunHistoryHelperBaseUrl: normalizeAccountRunHistoryHelperBaseUrlValue(inputAccountRunHistoryHelperBaseUrl?.value),
|
||||
...(contributionModeEnabled ? {} : {
|
||||
accountRunHistoryTextEnabled: Boolean(inputAccountRunHistoryTextEnabled?.checked),
|
||||
accountRunHistoryHelperBaseUrl: normalizeAccountRunHistoryHelperBaseUrlValue(inputAccountRunHistoryHelperBaseUrl?.value),
|
||||
}),
|
||||
...buildManagedAliasBaseEmailPayload(),
|
||||
inbucketHost: inputInbucketHost.value.trim(),
|
||||
inbucketMailbox: inputInbucketMailbox.value.trim(),
|
||||
@@ -1461,7 +1487,9 @@ function updateAccountRunHistorySettingsUI() {
|
||||
return;
|
||||
}
|
||||
|
||||
rowAccountRunHistoryHelperBaseUrl.style.display = inputAccountRunHistoryTextEnabled.checked ? '' : 'none';
|
||||
rowAccountRunHistoryHelperBaseUrl.style.display = inputAccountRunHistoryTextEnabled.checked && !latestState?.contributionMode
|
||||
? ''
|
||||
: 'none';
|
||||
}
|
||||
|
||||
function setSettingsCardLocked(locked) {
|
||||
@@ -1632,6 +1660,7 @@ function applyAutoRunStatus(payload = currentAutoRun) {
|
||||
syncScheduledCountdownTicker();
|
||||
updateStopButtonState(scheduled || paused || locked || Object.values(getStepStatuses()).some(status => status === 'running'));
|
||||
updateConfigMenuControls();
|
||||
renderContributionMode();
|
||||
}
|
||||
|
||||
function initializeManualStepActions() {
|
||||
@@ -1813,6 +1842,7 @@ async function restoreState() {
|
||||
|
||||
updateStatusDisplay(latestState);
|
||||
updateProgressCounter();
|
||||
renderContributionMode();
|
||||
} catch (err) {
|
||||
console.error('Failed to restore state:', err);
|
||||
}
|
||||
@@ -2072,7 +2102,7 @@ async function initializeReleaseInfo() {
|
||||
}
|
||||
|
||||
function syncPasswordField(state) {
|
||||
inputPassword.value = state.customPassword || state.password || '';
|
||||
inputPassword.value = state?.contributionMode ? '' : (state.customPassword || state.password || '');
|
||||
}
|
||||
|
||||
function isCustomMailProvider(provider = selectMailProvider.value) {
|
||||
@@ -2634,6 +2664,7 @@ function updateButtonStates() {
|
||||
if (checkboxAutoDeleteIcloud) checkboxAutoDeleteIcloud.disabled = disableIcloudControls;
|
||||
if (btnContributionMode) btnContributionMode.disabled = isContributionButtonLocked();
|
||||
updateStopButtonState(anyRunning || autoScheduled || isAutoRunPausedPhase() || autoLocked);
|
||||
renderContributionMode();
|
||||
}
|
||||
|
||||
function updateStopButtonState(active) {
|
||||
@@ -3010,7 +3041,66 @@ const renderAccountRecords = accountRecordsManager?.render
|
||||
|| (() => { });
|
||||
const bindAccountRecordEvents = accountRecordsManager?.bindEvents
|
||||
|| (() => { });
|
||||
const closeAccountRecordsPanel = accountRecordsManager?.closePanel
|
||||
|| (() => { });
|
||||
bindAccountRecordEvents();
|
||||
const contributionModeManager = window.SidepanelContributionMode?.createContributionModeManager({
|
||||
state: {
|
||||
getLatestState: () => latestState,
|
||||
},
|
||||
dom: {
|
||||
btnConfigMenu,
|
||||
btnContributionMode,
|
||||
contributionCallbackStatus,
|
||||
btnExitContributionMode,
|
||||
btnOpenAccountRecords,
|
||||
btnOpenContributionUpload,
|
||||
btnStartContribution,
|
||||
contributionModePanel,
|
||||
contributionModeSummary,
|
||||
contributionModeText,
|
||||
contributionOauthStatus,
|
||||
rowAccountRunHistoryHelperBaseUrl,
|
||||
rowAccountRunHistoryTextEnabled,
|
||||
rowCustomPassword,
|
||||
rowLocalCpaStep9Mode,
|
||||
rowSub2ApiDefaultProxy,
|
||||
rowSub2ApiEmail,
|
||||
rowSub2ApiGroup,
|
||||
rowSub2ApiPassword,
|
||||
rowSub2ApiUrl,
|
||||
rowVpsPassword,
|
||||
rowVpsUrl,
|
||||
selectPanelMode,
|
||||
},
|
||||
helpers: {
|
||||
applySettingsState,
|
||||
closeAccountRecordsPanel,
|
||||
closeConfigMenu,
|
||||
getContributionNickname: () => latestState?.email || '',
|
||||
isModeSwitchBlocked: isContributionModeSwitchBlocked,
|
||||
openConfirmModal,
|
||||
openExternalUrl,
|
||||
showToast,
|
||||
startContributionAutoRun: () => startAutoRunFromCurrentSettings(),
|
||||
updateAccountRunHistorySettingsUI,
|
||||
updateConfigMenuControls,
|
||||
updatePanelModeUI,
|
||||
updateStatusDisplay,
|
||||
},
|
||||
runtime: {
|
||||
sendMessage: (message) => chrome.runtime.sendMessage(message),
|
||||
},
|
||||
constants: {
|
||||
contributionOauthUrl: 'https://apikey.qzz.io/oauth/',
|
||||
contributionUploadUrl: 'https://apikey.qzz.io/',
|
||||
},
|
||||
});
|
||||
const renderContributionMode = contributionModeManager?.render
|
||||
|| (() => { });
|
||||
const bindContributionModeEvents = contributionModeManager?.bindEvents
|
||||
|| (() => { });
|
||||
bindContributionModeEvents();
|
||||
renderStepsList();
|
||||
|
||||
async function exportSettingsFile() {
|
||||
@@ -3370,9 +3460,65 @@ autoStartModal?.addEventListener('click', (event) => {
|
||||
});
|
||||
btnAutoStartClose?.addEventListener('click', () => resolveModalChoice(null));
|
||||
|
||||
async function startAutoRunFromCurrentSettings() {
|
||||
const totalRuns = getRunCountValue();
|
||||
let mode = 'restart';
|
||||
const autoRunSkipFailures = inputAutoSkipFailures.checked;
|
||||
const fallbackThreadIntervalMinutes = normalizeAutoRunThreadIntervalMinutes(
|
||||
inputAutoSkipFailuresThreadIntervalMinutes.value
|
||||
);
|
||||
inputAutoSkipFailuresThreadIntervalMinutes.value = String(fallbackThreadIntervalMinutes);
|
||||
|
||||
if (shouldOfferAutoModeChoice()) {
|
||||
const startStep = getFirstUnfinishedStep();
|
||||
const runningStep = getRunningSteps()[0] ?? null;
|
||||
const choice = await openAutoStartChoiceDialog(startStep, { runningStep });
|
||||
if (!choice) {
|
||||
return false;
|
||||
}
|
||||
mode = choice;
|
||||
}
|
||||
|
||||
if (shouldWarnAutoRunFallbackRisk(totalRuns, autoRunSkipFailures)
|
||||
&& !isAutoRunFallbackRiskPromptDismissed()) {
|
||||
const result = await openAutoRunFallbackRiskConfirmModal(totalRuns, fallbackThreadIntervalMinutes);
|
||||
if (!result.confirmed) {
|
||||
return false;
|
||||
}
|
||||
if (result.dismissPrompt) {
|
||||
setAutoRunFallbackRiskPromptDismissed(true);
|
||||
}
|
||||
}
|
||||
|
||||
btnAutoRun.disabled = true;
|
||||
inputRunCount.disabled = true;
|
||||
const delayEnabled = inputAutoDelayEnabled.checked;
|
||||
const delayMinutes = normalizeAutoDelayMinutes(inputAutoDelayMinutes.value);
|
||||
inputAutoDelayMinutes.value = String(delayMinutes);
|
||||
btnAutoRun.innerHTML = delayEnabled
|
||||
? '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg> 璁″垝涓?..'
|
||||
: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg> 杩愯涓?..';
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
type: delayEnabled ? 'SCHEDULE_AUTO_RUN' : 'AUTO_RUN',
|
||||
source: 'sidepanel',
|
||||
payload: {
|
||||
totalRuns,
|
||||
delayMinutes,
|
||||
autoRunSkipFailures,
|
||||
contributionMode: Boolean(latestState?.contributionMode),
|
||||
mode,
|
||||
},
|
||||
});
|
||||
if (response?.error) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Auto Run
|
||||
btnAutoRun.addEventListener('click', async () => {
|
||||
try {
|
||||
return await startAutoRunFromCurrentSettings();
|
||||
const totalRuns = getRunCountValue();
|
||||
let mode = 'restart';
|
||||
const autoRunSkipFailures = inputAutoSkipFailures.checked;
|
||||
@@ -4072,12 +4218,20 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||
if (message.payload.email !== undefined) {
|
||||
inputEmail.value = message.payload.email || '';
|
||||
}
|
||||
if (message.payload.password !== undefined) {
|
||||
inputPassword.value = message.payload.password || '';
|
||||
if (
|
||||
message.payload.password !== undefined
|
||||
|| message.payload.customPassword !== undefined
|
||||
|| message.payload.contributionMode !== undefined
|
||||
) {
|
||||
syncPasswordField(latestState || {});
|
||||
}
|
||||
if (message.payload.localCpaStep9Mode !== undefined) {
|
||||
setLocalCpaStep9Mode(message.payload.localCpaStep9Mode);
|
||||
}
|
||||
if (message.payload.panelMode !== undefined) {
|
||||
selectPanelMode.value = message.payload.panelMode || 'cpa';
|
||||
updatePanelModeUI();
|
||||
}
|
||||
if (message.payload.oauthUrl !== undefined) {
|
||||
displayOauthUrl.textContent = message.payload.oauthUrl || '等待中...';
|
||||
displayOauthUrl.classList.toggle('has-value', Boolean(message.payload.oauthUrl));
|
||||
@@ -4180,6 +4334,7 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||
)
|
||||
);
|
||||
}
|
||||
renderContributionMode();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user