Merge commit 'f18e6951cd147a3839df51837f3cb296502febfa' into dev
# Conflicts: # sidepanel/sidepanel.js
This commit is contained in:
@@ -877,6 +877,163 @@ header {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.icloud-card {
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius-sm);
|
||||
background: color-mix(in srgb, var(--bg-base) 84%, transparent);
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.icloud-summary {
|
||||
color: var(--text-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.icloud-login-help {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
border: 1px solid color-mix(in srgb, var(--orange) 26%, var(--border));
|
||||
background: color-mix(in srgb, var(--orange-soft) 62%, var(--bg-base));
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.icloud-login-help-main {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.icloud-login-help-title {
|
||||
color: var(--text-primary);
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.icloud-login-help-text {
|
||||
color: var(--text-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.icloud-toolbar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icloud-search {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.icloud-filter {
|
||||
flex: 0 0 130px;
|
||||
}
|
||||
|
||||
.icloud-bulkbar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.icloud-select-all {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.icloud-bulk-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.icloud-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
max-height: 360px;
|
||||
overflow: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.icloud-item {
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg-base);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.icloud-item-check {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.icloud-item-main {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.icloud-item-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.icloud-item-email {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.icloud-item-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.icloud-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
font-size: 11px;
|
||||
background: var(--bg-surface);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.icloud-tag.used {
|
||||
background: var(--orange-soft);
|
||||
color: var(--orange);
|
||||
}
|
||||
|
||||
.icloud-tag.active {
|
||||
background: var(--green-soft);
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
.icloud-empty {
|
||||
color: var(--text-muted);
|
||||
font-size: 12px;
|
||||
border: 1px dashed var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.data-select {
|
||||
flex: 1;
|
||||
padding: 7px 10px;
|
||||
|
||||
@@ -176,6 +176,7 @@
|
||||
<select id="select-email-generator" class="data-select">
|
||||
<option value="duck">DuckDuckGo</option>
|
||||
<option value="cloudflare">Cloudflare</option>
|
||||
<option value="icloud">iCloud 隐私邮箱</option>
|
||||
<option value="cloudflare-temp-email">Cloudflare Temp Email</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -346,6 +347,66 @@
|
||||
<div id="hotmail-accounts-list" class="hotmail-accounts-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="icloud-section" class="data-card hotmail-card" style="display:none;">
|
||||
<div class="section-mini-header">
|
||||
<div class="section-mini-copy">
|
||||
<span class="section-label">iCloud 隐私邮箱</span>
|
||||
</div>
|
||||
<div class="section-mini-actions">
|
||||
<button id="btn-icloud-refresh" class="btn btn-ghost btn-xs" type="button">刷新</button>
|
||||
<button id="btn-icloud-delete-used" class="btn btn-ghost btn-xs" type="button">删除已用</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row">
|
||||
<span class="data-label">iCloud</span>
|
||||
<select id="select-icloud-host-preference" class="data-select">
|
||||
<option value="auto">自动</option>
|
||||
<option value="icloud.com">iCloud.com</option>
|
||||
<option value="icloud.com.cn">iCloud.com.cn</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="data-row">
|
||||
<span class="data-label">自动删除</span>
|
||||
<label class="option-toggle" for="checkbox-auto-delete-icloud">
|
||||
<input type="checkbox" id="checkbox-auto-delete-icloud" />
|
||||
<span>成功使用后自动删除 iCloud 别名</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="icloud-card">
|
||||
<div id="icloud-summary" class="icloud-summary">加载你的 iCloud Hide My Email 别名以便在这里管理。</div>
|
||||
<div id="icloud-login-help" class="icloud-login-help" style="display:none;">
|
||||
<div class="icloud-login-help-main">
|
||||
<div id="icloud-login-help-title" class="icloud-login-help-title">需要登录 iCloud</div>
|
||||
<div id="icloud-login-help-text" class="icloud-login-help-text">我已经为你打开 iCloud 登录页。请在那个页面完成登录,然后回到这里点击“我已登录”。</div>
|
||||
</div>
|
||||
<button id="btn-icloud-login-done" class="btn btn-primary btn-xs" type="button">我已登录</button>
|
||||
</div>
|
||||
<div class="icloud-toolbar">
|
||||
<input id="input-icloud-search" class="data-input icloud-search" type="text" placeholder="搜索邮箱 / 标签 / 备注" />
|
||||
<select id="select-icloud-filter" class="data-select icloud-filter">
|
||||
<option value="all">全部</option>
|
||||
<option value="active">可用</option>
|
||||
<option value="used">已用</option>
|
||||
<option value="unused">未用</option>
|
||||
<option value="preserved">保留</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="icloud-bulkbar">
|
||||
<label class="option-toggle icloud-select-all" for="checkbox-icloud-select-all">
|
||||
<input type="checkbox" id="checkbox-icloud-select-all" />
|
||||
<span id="icloud-selection-summary">已选 0 个</span>
|
||||
</label>
|
||||
<div class="icloud-bulk-actions">
|
||||
<button id="btn-icloud-bulk-used" class="btn btn-outline btn-xs" type="button">标记已用</button>
|
||||
<button id="btn-icloud-bulk-unused" class="btn btn-outline btn-xs" type="button">标记未用</button>
|
||||
<button id="btn-icloud-bulk-preserve" class="btn btn-outline btn-xs" type="button">保留</button>
|
||||
<button id="btn-icloud-bulk-unpreserve" class="btn btn-outline btn-xs" type="button">取消保留</button>
|
||||
<button id="btn-icloud-bulk-delete" class="btn btn-outline btn-xs" type="button">删除</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="icloud-list" class="icloud-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="status-bar" class="status-bar">
|
||||
<div class="status-dot"></div>
|
||||
<span id="display-status">就绪</span>
|
||||
|
||||
+573
-7
@@ -81,6 +81,26 @@ const selectTempEmailDomain = document.getElementById('select-temp-email-domain'
|
||||
const inputTempEmailDomain = document.getElementById('input-temp-email-domain');
|
||||
const btnTempEmailDomainMode = document.getElementById('btn-temp-email-domain-mode');
|
||||
const hotmailSection = document.getElementById('hotmail-section');
|
||||
const icloudSection = document.getElementById('icloud-section');
|
||||
const icloudSummary = document.getElementById('icloud-summary');
|
||||
const icloudList = document.getElementById('icloud-list');
|
||||
const icloudLoginHelp = document.getElementById('icloud-login-help');
|
||||
const icloudLoginHelpTitle = document.getElementById('icloud-login-help-title');
|
||||
const icloudLoginHelpText = document.getElementById('icloud-login-help-text');
|
||||
const btnIcloudLoginDone = document.getElementById('btn-icloud-login-done');
|
||||
const btnIcloudRefresh = document.getElementById('btn-icloud-refresh');
|
||||
const btnIcloudDeleteUsed = document.getElementById('btn-icloud-delete-used');
|
||||
const selectIcloudHostPreference = document.getElementById('select-icloud-host-preference');
|
||||
const checkboxAutoDeleteIcloud = document.getElementById('checkbox-auto-delete-icloud');
|
||||
const inputIcloudSearch = document.getElementById('input-icloud-search');
|
||||
const selectIcloudFilter = document.getElementById('select-icloud-filter');
|
||||
const checkboxIcloudSelectAll = document.getElementById('checkbox-icloud-select-all');
|
||||
const icloudSelectionSummary = document.getElementById('icloud-selection-summary');
|
||||
const btnIcloudBulkUsed = document.getElementById('btn-icloud-bulk-used');
|
||||
const btnIcloudBulkUnused = document.getElementById('btn-icloud-bulk-unused');
|
||||
const btnIcloudBulkPreserve = document.getElementById('btn-icloud-bulk-preserve');
|
||||
const btnIcloudBulkUnpreserve = document.getElementById('btn-icloud-bulk-unpreserve');
|
||||
const btnIcloudBulkDelete = document.getElementById('btn-icloud-bulk-delete');
|
||||
const rowHotmailServiceMode = document.getElementById('row-hotmail-service-mode');
|
||||
const hotmailServiceModeButtons = Array.from(document.querySelectorAll('[data-hotmail-service-mode]'));
|
||||
const rowHotmailRemoteBaseUrl = document.getElementById('row-hotmail-remote-base-url');
|
||||
@@ -179,6 +199,11 @@ let settingsSaveInFlight = false;
|
||||
let settingsAutoSaveTimer = null;
|
||||
let cloudflareDomainEditMode = false;
|
||||
let cloudflareTempEmailDomainEditMode = false;
|
||||
let icloudRefreshQueued = false;
|
||||
let lastRenderedIcloudAliases = [];
|
||||
let icloudSelectedEmails = new Set();
|
||||
let icloudSearchTerm = '';
|
||||
let icloudFilterMode = 'all';
|
||||
let modalChoiceResolver = null;
|
||||
let currentModalActions = [];
|
||||
let modalResultBuilder = null;
|
||||
@@ -1039,6 +1064,8 @@ function collectSettingsPayload() {
|
||||
mailProvider: selectMailProvider.value,
|
||||
mail2925Mode: getSelectedMail2925Mode(),
|
||||
emailGenerator: selectEmailGenerator.value,
|
||||
autoDeleteUsedIcloudAlias: checkboxAutoDeleteIcloud?.checked,
|
||||
icloudHostPreference: selectIcloudHostPreference?.value || 'auto',
|
||||
emailPrefix: inputEmailPrefix.value.trim(),
|
||||
inbucketHost: inputInbucketHost.value.trim(),
|
||||
inbucketMailbox: inputInbucketMailbox.value.trim(),
|
||||
@@ -1351,10 +1378,26 @@ function applySettingsState(state) {
|
||||
: '163');
|
||||
selectMailProvider.value = restoredMailProvider;
|
||||
setMail2925Mode(state?.mail2925Mode);
|
||||
const restoredEmailGenerator = String(state?.emailGenerator || '').trim().toLowerCase();
|
||||
selectEmailGenerator.value = ['cloudflare', 'cloudflare-temp-email'].includes(restoredEmailGenerator)
|
||||
? restoredEmailGenerator
|
||||
: 'duck';
|
||||
{
|
||||
const restoredEmailGenerator = String(state?.emailGenerator || '').trim().toLowerCase();
|
||||
if (restoredEmailGenerator === 'icloud') {
|
||||
selectEmailGenerator.value = 'icloud';
|
||||
} else if (restoredEmailGenerator === 'cloudflare') {
|
||||
selectEmailGenerator.value = 'cloudflare';
|
||||
} else if (restoredEmailGenerator === 'cloudflare-temp-email') {
|
||||
selectEmailGenerator.value = 'cloudflare-temp-email';
|
||||
} else {
|
||||
selectEmailGenerator.value = 'duck';
|
||||
}
|
||||
}
|
||||
if (selectIcloudHostPreference) {
|
||||
selectIcloudHostPreference.value = String(state?.icloudHostPreference || '').trim().toLowerCase() === 'icloud.com'
|
||||
? 'icloud.com'
|
||||
: (String(state?.icloudHostPreference || '').trim().toLowerCase() === 'icloud.com.cn' ? 'icloud.com.cn' : 'auto');
|
||||
}
|
||||
if (checkboxAutoDeleteIcloud) {
|
||||
checkboxAutoDeleteIcloud.checked = Boolean(state?.autoDeleteUsedIcloudAlias);
|
||||
}
|
||||
inputEmailPrefix.value = state?.emailPrefix || '';
|
||||
inputInbucketHost.value = state?.inbucketHost || '';
|
||||
inputInbucketMailbox.value = state?.inbucketMailbox || '';
|
||||
@@ -1390,6 +1433,9 @@ async function restoreState() {
|
||||
try {
|
||||
const state = await chrome.runtime.sendMessage({ type: 'GET_STATE', source: 'sidepanel' });
|
||||
applySettingsState(state);
|
||||
if (getSelectedEmailGenerator() === 'icloud' && icloudSection?.style.display !== 'none') {
|
||||
refreshIcloudAliases({ silent: true }).catch(() => { });
|
||||
}
|
||||
|
||||
if (state.oauthUrl) {
|
||||
displayOauthUrl.textContent = state.oauthUrl;
|
||||
@@ -1637,6 +1683,9 @@ function getSelectedEmailGenerator() {
|
||||
if (generator === 'custom' || generator === 'manual') {
|
||||
return 'custom';
|
||||
}
|
||||
if (generator === 'icloud') {
|
||||
return 'icloud';
|
||||
}
|
||||
if (generator === 'cloudflare') return 'cloudflare';
|
||||
if (generator === 'cloudflare-temp-email') return 'cloudflare-temp-email';
|
||||
return 'duck';
|
||||
@@ -1646,6 +1695,14 @@ function getEmailGeneratorUiCopy() {
|
||||
if (getSelectedEmailGenerator() === 'custom') {
|
||||
return getCustomMailProviderUiCopy();
|
||||
}
|
||||
if (getSelectedEmailGenerator() === 'icloud') {
|
||||
return {
|
||||
buttonLabel: '获取',
|
||||
placeholder: '点击获取 iCloud 隐私邮箱,或手动粘贴邮箱',
|
||||
successVerb: '获取',
|
||||
label: 'iCloud 隐私邮箱',
|
||||
};
|
||||
}
|
||||
if (getSelectedEmailGenerator() === 'cloudflare') {
|
||||
return {
|
||||
buttonLabel: '生成',
|
||||
@@ -1982,14 +2039,26 @@ function updateMailProviderUI() {
|
||||
const hotmailServiceMode = getSelectedHotmailServiceMode();
|
||||
rowInbucketHost.style.display = useInbucket ? '' : 'none';
|
||||
rowInbucketMailbox.style.display = useInbucket ? '' : 'none';
|
||||
const useCloudflare = selectEmailGenerator.value === 'cloudflare';
|
||||
const useCloudflareTempEmailGenerator = selectEmailGenerator.value === 'cloudflare-temp-email';
|
||||
const selectedGenerator = getSelectedEmailGenerator();
|
||||
const useCloudflare = selectedGenerator === 'cloudflare';
|
||||
const useIcloud = selectedGenerator === 'icloud';
|
||||
const useCloudflareTempEmailGenerator = selectedGenerator === 'cloudflare-temp-email';
|
||||
const showCloudflareDomain = useEmailGenerator && useCloudflare;
|
||||
const showCloudflareTempEmailSettings = useCloudflareTempEmailProvider || (useEmailGenerator && useCloudflareTempEmailGenerator);
|
||||
const showCloudflareTempEmailDomain = useEmailGenerator && useCloudflareTempEmailGenerator;
|
||||
if (rowEmailGenerator) {
|
||||
rowEmailGenerator.style.display = useEmailGenerator ? '' : 'none';
|
||||
}
|
||||
if (icloudSection) {
|
||||
const showIcloudSection = useEmailGenerator && useIcloud;
|
||||
icloudSection.style.display = showIcloudSection ? '' : 'none';
|
||||
if (showIcloudSection && !lastRenderedIcloudAliases.length) {
|
||||
queueIcloudAliasRefresh();
|
||||
}
|
||||
if (!showIcloudSection) {
|
||||
hideIcloudLoginHelp();
|
||||
}
|
||||
}
|
||||
rowCfDomain.style.display = showCloudflareDomain ? '' : 'none';
|
||||
const { domains } = getCloudflareDomainsFromState();
|
||||
if (showCloudflareDomain) {
|
||||
@@ -2038,7 +2107,7 @@ function updateMailProviderUI() {
|
||||
? '请先校验并选择一个 Hotmail 账号'
|
||||
: (useGeneratedAlias
|
||||
? '步骤 3 会自动生成邮箱,无需手动获取'
|
||||
: (useCustomEmail ? '请先填写自定义注册邮箱,成功一轮后会自动清空' : '先自动获取邮箱,或手动粘贴邮箱后再继续'));
|
||||
: (useCustomEmail ? '请先填写自定义注册邮箱,成功一轮后会自动清空' : `先自动获取${uiCopy.label},或手动粘贴邮箱后再继续`));
|
||||
}
|
||||
if (useHotmail) {
|
||||
inputEmail.value = getCurrentHotmailEmail();
|
||||
@@ -2202,6 +2271,11 @@ function updateButtonStates() {
|
||||
});
|
||||
|
||||
btnReset.disabled = anyRunning || autoScheduled || isAutoRunPausedPhase() || autoLocked;
|
||||
const disableIcloudControls = anyRunning || autoScheduled || autoLocked;
|
||||
if (btnIcloudRefresh) btnIcloudRefresh.disabled = disableIcloudControls;
|
||||
if (btnIcloudDeleteUsed) btnIcloudDeleteUsed.disabled = disableIcloudControls || !(lastRenderedIcloudAliases.some((alias) => alias.used && !alias.preserved));
|
||||
if (selectIcloudHostPreference) selectIcloudHostPreference.disabled = disableIcloudControls;
|
||||
if (checkboxAutoDeleteIcloud) checkboxAutoDeleteIcloud.disabled = disableIcloudControls;
|
||||
updateStopButtonState(anyRunning || autoScheduled || isAutoRunPausedPhase() || autoLocked);
|
||||
}
|
||||
|
||||
@@ -2324,6 +2398,375 @@ function escapeHtml(text) {
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function normalizeIcloudSearchText(value) {
|
||||
return String(value || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
function getFilteredIcloudAliases(aliases = lastRenderedIcloudAliases) {
|
||||
const searchTerm = normalizeIcloudSearchText(icloudSearchTerm);
|
||||
return (Array.isArray(aliases) ? aliases : []).filter((alias) => {
|
||||
const matchesFilter = (() => {
|
||||
switch (icloudFilterMode) {
|
||||
case 'active': return Boolean(alias.active);
|
||||
case 'used': return Boolean(alias.used);
|
||||
case 'unused': return !alias.used;
|
||||
case 'preserved': return Boolean(alias.preserved);
|
||||
default: return true;
|
||||
}
|
||||
})();
|
||||
|
||||
if (!matchesFilter) return false;
|
||||
if (!searchTerm) return true;
|
||||
|
||||
const haystack = [
|
||||
alias.email,
|
||||
alias.label,
|
||||
alias.note,
|
||||
alias.used ? '已用 used' : '未用 unused',
|
||||
alias.active ? '可用 active' : '不可用 inactive',
|
||||
alias.preserved ? '保留 preserved' : '',
|
||||
].join(' ').toLowerCase();
|
||||
|
||||
return haystack.includes(searchTerm);
|
||||
});
|
||||
}
|
||||
|
||||
function pruneIcloudSelection(aliases = lastRenderedIcloudAliases) {
|
||||
const existing = new Set((Array.isArray(aliases) ? aliases : []).map((alias) => alias.email));
|
||||
icloudSelectedEmails = new Set([...icloudSelectedEmails].filter((email) => existing.has(email)));
|
||||
}
|
||||
|
||||
function updateIcloudBulkUI(visibleAliases = getFilteredIcloudAliases()) {
|
||||
if (!checkboxIcloudSelectAll || !icloudSelectionSummary) {
|
||||
return;
|
||||
}
|
||||
|
||||
const visibleEmails = visibleAliases.map((alias) => alias.email);
|
||||
const selectedVisibleCount = visibleEmails.filter((email) => icloudSelectedEmails.has(email)).length;
|
||||
const hasVisible = visibleEmails.length > 0;
|
||||
|
||||
checkboxIcloudSelectAll.checked = hasVisible && selectedVisibleCount === visibleEmails.length;
|
||||
checkboxIcloudSelectAll.indeterminate = selectedVisibleCount > 0 && selectedVisibleCount < visibleEmails.length;
|
||||
checkboxIcloudSelectAll.disabled = !hasVisible;
|
||||
icloudSelectionSummary.textContent = `已选 ${icloudSelectedEmails.size} 个(当前显示 ${visibleEmails.length} 个)`;
|
||||
|
||||
const hasSelection = icloudSelectedEmails.size > 0;
|
||||
if (btnIcloudBulkUsed) btnIcloudBulkUsed.disabled = !hasSelection;
|
||||
if (btnIcloudBulkUnused) btnIcloudBulkUnused.disabled = !hasSelection;
|
||||
if (btnIcloudBulkPreserve) btnIcloudBulkPreserve.disabled = !hasSelection;
|
||||
if (btnIcloudBulkUnpreserve) btnIcloudBulkUnpreserve.disabled = !hasSelection;
|
||||
if (btnIcloudBulkDelete) btnIcloudBulkDelete.disabled = !hasSelection;
|
||||
}
|
||||
|
||||
function setIcloudLoadingState(loading, summary = '') {
|
||||
if (btnIcloudRefresh) btnIcloudRefresh.disabled = loading;
|
||||
if (btnIcloudDeleteUsed) btnIcloudDeleteUsed.disabled = loading;
|
||||
if (btnIcloudLoginDone) btnIcloudLoginDone.disabled = loading;
|
||||
if (inputIcloudSearch) inputIcloudSearch.disabled = loading;
|
||||
if (selectIcloudFilter) selectIcloudFilter.disabled = loading;
|
||||
if (checkboxIcloudSelectAll) checkboxIcloudSelectAll.disabled = loading || getFilteredIcloudAliases().length === 0;
|
||||
if (btnIcloudBulkUsed) btnIcloudBulkUsed.disabled = loading || icloudSelectedEmails.size === 0;
|
||||
if (btnIcloudBulkUnused) btnIcloudBulkUnused.disabled = loading || icloudSelectedEmails.size === 0;
|
||||
if (btnIcloudBulkPreserve) btnIcloudBulkPreserve.disabled = loading || icloudSelectedEmails.size === 0;
|
||||
if (btnIcloudBulkUnpreserve) btnIcloudBulkUnpreserve.disabled = loading || icloudSelectedEmails.size === 0;
|
||||
if (btnIcloudBulkDelete) btnIcloudBulkDelete.disabled = loading || icloudSelectedEmails.size === 0;
|
||||
if (summary && icloudSummary) icloudSummary.textContent = summary;
|
||||
}
|
||||
|
||||
function showIcloudLoginHelp(payload = {}) {
|
||||
if (!icloudLoginHelp) return;
|
||||
const loginUrl = String(payload.loginUrl || '').trim();
|
||||
const host = loginUrl ? new URL(loginUrl).host : 'icloud.com.cn / icloud.com';
|
||||
if (icloudLoginHelpTitle) icloudLoginHelpTitle.textContent = '需要登录 iCloud';
|
||||
if (icloudLoginHelpText) icloudLoginHelpText.textContent = `我已经为你打开 ${host}。请在那个页面完成登录,然后回到这里点击“我已登录”。`;
|
||||
icloudLoginHelp.style.display = 'flex';
|
||||
}
|
||||
|
||||
function hideIcloudLoginHelp() {
|
||||
if (icloudLoginHelp) {
|
||||
icloudLoginHelp.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function renderIcloudAliases(aliases = []) {
|
||||
if (!icloudList || !icloudSummary) return;
|
||||
|
||||
lastRenderedIcloudAliases = Array.isArray(aliases) ? aliases : [];
|
||||
pruneIcloudSelection(lastRenderedIcloudAliases);
|
||||
icloudList.innerHTML = '';
|
||||
|
||||
if (!aliases.length) {
|
||||
icloudSelectedEmails.clear();
|
||||
icloudList.innerHTML = '<div class="icloud-empty">未找到 iCloud Hide My Email 别名。</div>';
|
||||
icloudSummary.textContent = '加载你的 iCloud Hide My Email 别名以便在这里管理。';
|
||||
if (btnIcloudDeleteUsed) btnIcloudDeleteUsed.disabled = true;
|
||||
updateIcloudBulkUI([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const usedCount = aliases.filter((alias) => alias.used).length;
|
||||
const deletableUsedCount = aliases.filter((alias) => alias.used && !alias.preserved).length;
|
||||
icloudSummary.textContent = `已加载 ${aliases.length} 个别名,其中 ${usedCount} 个已标记为已用。`;
|
||||
if (btnIcloudDeleteUsed) btnIcloudDeleteUsed.disabled = deletableUsedCount === 0;
|
||||
|
||||
const visibleAliases = getFilteredIcloudAliases(aliases);
|
||||
if (!visibleAliases.length) {
|
||||
icloudList.innerHTML = '<div class="icloud-empty">没有匹配当前筛选条件的别名。</div>';
|
||||
updateIcloudBulkUI([]);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const alias of visibleAliases) {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'icloud-item';
|
||||
item.innerHTML = `
|
||||
<input class="icloud-item-check" type="checkbox" data-action="select" ${icloudSelectedEmails.has(alias.email) ? 'checked' : ''} />
|
||||
<div class="icloud-item-main">
|
||||
<div class="icloud-item-email">${escapeHtml(alias.email)}</div>
|
||||
<div class="icloud-item-meta">
|
||||
${alias.used ? '<span class="icloud-tag used">已用</span>' : ''}
|
||||
${!alias.used && alias.active ? '<span class="icloud-tag active">可用</span>' : ''}
|
||||
${alias.preserved ? '<span class="icloud-tag">保留</span>' : ''}
|
||||
${alias.label ? `<span class="icloud-tag">${escapeHtml(alias.label)}</span>` : ''}
|
||||
${alias.note ? `<span class="icloud-tag">${escapeHtml(alias.note)}</span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="icloud-item-actions">
|
||||
<button class="btn btn-outline btn-xs" type="button" data-action="toggle-used">${escapeHtml(alias.used ? '标记未用' : '标记已用')}</button>
|
||||
<button class="btn btn-outline btn-xs" type="button" data-action="toggle-preserved">${escapeHtml(alias.preserved ? '取消保留' : '保留')}</button>
|
||||
<button class="btn btn-outline btn-xs" type="button" data-action="delete">删除</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
item.querySelector('[data-action="select"]').addEventListener('change', (event) => {
|
||||
if (event.target.checked) {
|
||||
icloudSelectedEmails.add(alias.email);
|
||||
} else {
|
||||
icloudSelectedEmails.delete(alias.email);
|
||||
}
|
||||
updateIcloudBulkUI(visibleAliases);
|
||||
});
|
||||
item.querySelector('[data-action="toggle-used"]').addEventListener('click', async () => {
|
||||
await setSingleIcloudAliasUsedState(alias, !alias.used);
|
||||
});
|
||||
item.querySelector('[data-action="toggle-preserved"]').addEventListener('click', async () => {
|
||||
await setSingleIcloudAliasPreservedState(alias, !alias.preserved);
|
||||
});
|
||||
item.querySelector('[data-action="delete"]').addEventListener('click', async () => {
|
||||
await deleteSingleIcloudAlias(alias);
|
||||
});
|
||||
icloudList.appendChild(item);
|
||||
}
|
||||
|
||||
updateIcloudBulkUI(visibleAliases);
|
||||
}
|
||||
|
||||
async function refreshIcloudAliases(options = {}) {
|
||||
const { silent = false } = options;
|
||||
if (!icloudSection || icloudSection.style.display === 'none') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!silent) setIcloudLoadingState(true, '正在加载 iCloud 别名...');
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
type: 'LIST_ICLOUD_ALIASES',
|
||||
source: 'sidepanel',
|
||||
payload: {},
|
||||
});
|
||||
if (response?.error) throw new Error(response.error);
|
||||
hideIcloudLoginHelp();
|
||||
renderIcloudAliases(response?.aliases || []);
|
||||
} catch (err) {
|
||||
icloudSelectedEmails.clear();
|
||||
if (icloudList) {
|
||||
icloudList.innerHTML = '<div class="icloud-empty">无法加载 iCloud 别名。</div>';
|
||||
}
|
||||
if (icloudSummary) {
|
||||
icloudSummary.textContent = err.message;
|
||||
}
|
||||
updateIcloudBulkUI([]);
|
||||
if (!silent) showToast(`iCloud 别名加载失败:${err.message}`, 'error');
|
||||
} finally {
|
||||
setIcloudLoadingState(false);
|
||||
}
|
||||
}
|
||||
|
||||
function queueIcloudAliasRefresh() {
|
||||
if (icloudRefreshQueued) return;
|
||||
icloudRefreshQueued = true;
|
||||
setTimeout(async () => {
|
||||
icloudRefreshQueued = false;
|
||||
await refreshIcloudAliases({ silent: true });
|
||||
}, 150);
|
||||
}
|
||||
|
||||
async function deleteSingleIcloudAlias(alias) {
|
||||
const confirmed = await openConfirmModal({
|
||||
title: '删除 iCloud 别名',
|
||||
message: `确认删除 ${alias.email} 吗?此操作不可撤销。`,
|
||||
confirmLabel: '确认删除',
|
||||
confirmVariant: 'btn-danger',
|
||||
});
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIcloudLoadingState(true, `正在删除 ${alias.email} ...`);
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
type: 'DELETE_ICLOUD_ALIAS',
|
||||
source: 'sidepanel',
|
||||
payload: { email: alias.email, anonymousId: alias.anonymousId },
|
||||
});
|
||||
if (response?.error) throw new Error(response.error);
|
||||
showToast(`已删除 ${alias.email}`, 'success', 2200);
|
||||
await refreshIcloudAliases({ silent: true });
|
||||
} catch (err) {
|
||||
if (icloudSummary) icloudSummary.textContent = err.message;
|
||||
showToast(`删除 iCloud 别名失败:${err.message}`, 'error');
|
||||
} finally {
|
||||
setIcloudLoadingState(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function setSingleIcloudAliasUsedState(alias, used) {
|
||||
setIcloudLoadingState(true, `正在更新 ${alias.email} 的使用状态...`);
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
type: 'SET_ICLOUD_ALIAS_USED_STATE',
|
||||
source: 'sidepanel',
|
||||
payload: { email: alias.email, used },
|
||||
});
|
||||
if (response?.error) throw new Error(response.error);
|
||||
showToast(`${alias.email} 已${used ? '标记为已用' : '恢复为未用'}`, 'success', 2200);
|
||||
await refreshIcloudAliases({ silent: true });
|
||||
} catch (err) {
|
||||
if (icloudSummary) icloudSummary.textContent = err.message;
|
||||
showToast(`更新 iCloud 使用状态失败:${err.message}`, 'error');
|
||||
} finally {
|
||||
setIcloudLoadingState(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function setSingleIcloudAliasPreservedState(alias, preserved) {
|
||||
setIcloudLoadingState(true, `正在更新 ${alias.email} 的保留状态...`);
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
type: 'SET_ICLOUD_ALIAS_PRESERVED_STATE',
|
||||
source: 'sidepanel',
|
||||
payload: { email: alias.email, preserved },
|
||||
});
|
||||
if (response?.error) throw new Error(response.error);
|
||||
showToast(`${alias.email} 已${preserved ? '设为保留' : '取消保留'}`, 'success', 2200);
|
||||
await refreshIcloudAliases({ silent: true });
|
||||
} catch (err) {
|
||||
if (icloudSummary) icloudSummary.textContent = err.message;
|
||||
showToast(`更新 iCloud 保留状态失败:${err.message}`, 'error');
|
||||
} finally {
|
||||
setIcloudLoadingState(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function runBulkIcloudAction(action) {
|
||||
const selectedAliases = lastRenderedIcloudAliases.filter((alias) => icloudSelectedEmails.has(alias.email));
|
||||
if (!selectedAliases.length) {
|
||||
updateIcloudBulkUI();
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === 'delete') {
|
||||
const confirmed = await openConfirmModal({
|
||||
title: '批量删除 iCloud 别名',
|
||||
message: `确认删除选中的 ${selectedAliases.length} 个 iCloud 别名吗?此操作不可撤销。`,
|
||||
confirmLabel: '确认删除',
|
||||
confirmVariant: 'btn-danger',
|
||||
});
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const actionLabelMap = {
|
||||
used: '标记已用',
|
||||
unused: '标记未用',
|
||||
preserve: '保留',
|
||||
unpreserve: '取消保留',
|
||||
delete: '删除',
|
||||
};
|
||||
setIcloudLoadingState(true, `正在批量${actionLabelMap[action] || '处理'} iCloud 别名...`);
|
||||
|
||||
try {
|
||||
for (const alias of selectedAliases) {
|
||||
let response = null;
|
||||
if (action === 'used' || action === 'unused') {
|
||||
response = await chrome.runtime.sendMessage({
|
||||
type: 'SET_ICLOUD_ALIAS_USED_STATE',
|
||||
source: 'sidepanel',
|
||||
payload: { email: alias.email, used: action === 'used' },
|
||||
});
|
||||
} else if (action === 'preserve' || action === 'unpreserve') {
|
||||
response = await chrome.runtime.sendMessage({
|
||||
type: 'SET_ICLOUD_ALIAS_PRESERVED_STATE',
|
||||
source: 'sidepanel',
|
||||
payload: { email: alias.email, preserved: action === 'preserve' },
|
||||
});
|
||||
} else if (action === 'delete') {
|
||||
response = await chrome.runtime.sendMessage({
|
||||
type: 'DELETE_ICLOUD_ALIAS',
|
||||
source: 'sidepanel',
|
||||
payload: { email: alias.email, anonymousId: alias.anonymousId },
|
||||
});
|
||||
icloudSelectedEmails.delete(alias.email);
|
||||
}
|
||||
|
||||
if (response?.error) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
}
|
||||
|
||||
showToast(`已批量${actionLabelMap[action] || '处理'} ${selectedAliases.length} 个 iCloud 别名`, 'success', 2400);
|
||||
await refreshIcloudAliases({ silent: true });
|
||||
} catch (err) {
|
||||
if (icloudSummary) icloudSummary.textContent = err.message;
|
||||
showToast(`批量处理 iCloud 别名失败:${err.message}`, 'error');
|
||||
} finally {
|
||||
setIcloudLoadingState(false);
|
||||
updateIcloudBulkUI();
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteUsedIcloudAliases() {
|
||||
const confirmed = await openConfirmModal({
|
||||
title: '删除已用 iCloud 别名',
|
||||
message: '确认删除所有未保留的已用 iCloud 别名吗?此操作不可撤销。',
|
||||
confirmLabel: '确认删除',
|
||||
confirmVariant: 'btn-danger',
|
||||
});
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIcloudLoadingState(true, '正在删除已用 iCloud 别名...');
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
type: 'DELETE_USED_ICLOUD_ALIASES',
|
||||
source: 'sidepanel',
|
||||
payload: {},
|
||||
});
|
||||
if (response?.error) throw new Error(response.error);
|
||||
const deleted = response?.deleted || [];
|
||||
const skipped = response?.skipped || [];
|
||||
showToast(`已删除 ${deleted.length} 个已用别名,跳过 ${skipped.length} 个`, skipped.length ? 'warn' : 'success', 2800);
|
||||
await refreshIcloudAliases({ silent: true });
|
||||
} catch (err) {
|
||||
if (icloudSummary) icloudSummary.textContent = err.message;
|
||||
showToast(`删除已用 iCloud 别名失败:${err.message}`, 'error');
|
||||
} finally {
|
||||
setIcloudLoadingState(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchGeneratedEmail(options = {}) {
|
||||
const { showFailureToast = true } = options;
|
||||
const uiCopy = getEmailGeneratorUiCopy();
|
||||
@@ -2352,6 +2795,9 @@ async function fetchGeneratedEmail(options = {}) {
|
||||
}
|
||||
|
||||
inputEmail.value = response.email;
|
||||
if (getSelectedEmailGenerator() === 'icloud') {
|
||||
queueIcloudAliasRefresh();
|
||||
}
|
||||
showToast(`已${uiCopy.successVerb} ${uiCopy.label}:${response.email}`, 'success', 2500);
|
||||
return response.email;
|
||||
} catch (err) {
|
||||
@@ -2664,6 +3110,75 @@ btnFetchEmail.addEventListener('click', async () => {
|
||||
await fetchGeneratedEmail().catch(() => { });
|
||||
});
|
||||
|
||||
btnIcloudRefresh?.addEventListener('click', async () => {
|
||||
await refreshIcloudAliases();
|
||||
});
|
||||
|
||||
btnIcloudDeleteUsed?.addEventListener('click', async () => {
|
||||
await deleteUsedIcloudAliases();
|
||||
});
|
||||
|
||||
inputIcloudSearch?.addEventListener('input', () => {
|
||||
icloudSearchTerm = inputIcloudSearch.value || '';
|
||||
renderIcloudAliases(lastRenderedIcloudAliases);
|
||||
});
|
||||
|
||||
selectIcloudFilter?.addEventListener('change', () => {
|
||||
icloudFilterMode = selectIcloudFilter.value || 'all';
|
||||
renderIcloudAliases(lastRenderedIcloudAliases);
|
||||
});
|
||||
|
||||
checkboxIcloudSelectAll?.addEventListener('change', () => {
|
||||
const visibleAliases = getFilteredIcloudAliases();
|
||||
if (checkboxIcloudSelectAll.checked) {
|
||||
visibleAliases.forEach((alias) => icloudSelectedEmails.add(alias.email));
|
||||
} else {
|
||||
visibleAliases.forEach((alias) => icloudSelectedEmails.delete(alias.email));
|
||||
}
|
||||
renderIcloudAliases(lastRenderedIcloudAliases);
|
||||
});
|
||||
|
||||
btnIcloudBulkUsed?.addEventListener('click', async () => {
|
||||
await runBulkIcloudAction('used');
|
||||
});
|
||||
|
||||
btnIcloudBulkUnused?.addEventListener('click', async () => {
|
||||
await runBulkIcloudAction('unused');
|
||||
});
|
||||
|
||||
btnIcloudBulkPreserve?.addEventListener('click', async () => {
|
||||
await runBulkIcloudAction('preserve');
|
||||
});
|
||||
|
||||
btnIcloudBulkUnpreserve?.addEventListener('click', async () => {
|
||||
await runBulkIcloudAction('unpreserve');
|
||||
});
|
||||
|
||||
btnIcloudBulkDelete?.addEventListener('click', async () => {
|
||||
await runBulkIcloudAction('delete');
|
||||
});
|
||||
|
||||
btnIcloudLoginDone?.addEventListener('click', async () => {
|
||||
btnIcloudLoginDone.disabled = true;
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
type: 'CHECK_ICLOUD_SESSION',
|
||||
source: 'sidepanel',
|
||||
payload: {},
|
||||
});
|
||||
if (response?.error) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
hideIcloudLoginHelp();
|
||||
showToast('iCloud 会话已恢复,别名列表已刷新。', 'success', 2600);
|
||||
await refreshIcloudAliases({ silent: true });
|
||||
} catch (err) {
|
||||
showToast(`看起来还没有登录完成:${err.message}`, 'warn', 4200);
|
||||
} finally {
|
||||
btnIcloudLoginDone.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
btnToggleHotmailList?.addEventListener('click', () => {
|
||||
setHotmailListExpanded(!hotmailListExpanded);
|
||||
});
|
||||
@@ -3142,6 +3657,15 @@ btnReset.addEventListener('click', async () => {
|
||||
displayStatus.textContent = '就绪';
|
||||
statusBar.className = 'status-bar';
|
||||
logArea.innerHTML = '';
|
||||
icloudSelectedEmails.clear();
|
||||
lastRenderedIcloudAliases = [];
|
||||
if (icloudList) {
|
||||
icloudList.innerHTML = '';
|
||||
}
|
||||
if (icloudSummary) {
|
||||
icloudSummary.textContent = '加载你的 iCloud Hide My Email 别名以便在这里管理。';
|
||||
}
|
||||
updateIcloudBulkUI([]);
|
||||
document.querySelectorAll('.step-row').forEach(row => row.className = 'step-row');
|
||||
document.querySelectorAll('.step-status').forEach(el => el.textContent = '');
|
||||
setDefaultAutoRunButton();
|
||||
@@ -3266,6 +3790,19 @@ selectEmailGenerator.addEventListener('change', () => {
|
||||
saveSettings({ silent: true }).catch(() => { });
|
||||
});
|
||||
|
||||
selectIcloudHostPreference?.addEventListener('change', () => {
|
||||
markSettingsDirty(true);
|
||||
saveSettings({ silent: true }).catch(() => { });
|
||||
if (getSelectedEmailGenerator() === 'icloud') {
|
||||
queueIcloudAliasRefresh();
|
||||
}
|
||||
});
|
||||
|
||||
checkboxAutoDeleteIcloud?.addEventListener('change', () => {
|
||||
markSettingsDirty(true);
|
||||
saveSettings({ silent: true }).catch(() => { });
|
||||
});
|
||||
|
||||
selectPanelMode.addEventListener('change', () => {
|
||||
updatePanelModeUI();
|
||||
markSettingsDirty(true);
|
||||
@@ -3566,6 +4103,11 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||
displayStatus.textContent = '就绪';
|
||||
statusBar.className = 'status-bar';
|
||||
logArea.innerHTML = '';
|
||||
icloudSelectedEmails.clear();
|
||||
lastRenderedIcloudAliases = [];
|
||||
if (icloudList) icloudList.innerHTML = '';
|
||||
if (icloudSummary) icloudSummary.textContent = '加载你的 iCloud Hide My Email 别名以便在这里管理。';
|
||||
updateIcloudBulkUI([]);
|
||||
document.querySelectorAll('.step-row').forEach(row => row.className = 'step-row');
|
||||
document.querySelectorAll('.step-status').forEach(el => el.textContent = '');
|
||||
syncAutoRunState({
|
||||
@@ -3623,6 +4165,15 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||
inputEmail.value = getCurrentHotmailEmail();
|
||||
}
|
||||
}
|
||||
if (message.payload.autoDeleteUsedIcloudAlias !== undefined && checkboxAutoDeleteIcloud) {
|
||||
checkboxAutoDeleteIcloud.checked = Boolean(message.payload.autoDeleteUsedIcloudAlias);
|
||||
}
|
||||
if (message.payload.icloudHostPreference !== undefined && selectIcloudHostPreference) {
|
||||
const hostPreference = String(message.payload.icloudHostPreference || '').trim().toLowerCase();
|
||||
selectIcloudHostPreference.value = hostPreference === 'icloud.com'
|
||||
? 'icloud.com'
|
||||
: (hostPreference === 'icloud.com.cn' ? 'icloud.com.cn' : 'auto');
|
||||
}
|
||||
if (message.payload.autoRunSkipFailures !== undefined) {
|
||||
inputAutoSkipFailures.checked = Boolean(message.payload.autoRunSkipFailures);
|
||||
updateFallbackThreadIntervalInputState();
|
||||
@@ -3646,6 +4197,21 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ICLOUD_LOGIN_REQUIRED': {
|
||||
const loginMessage = '需要登录 iCloud,我已经为你打开登录页。';
|
||||
showToast(loginMessage, 'warn', 5000);
|
||||
if (icloudSummary) {
|
||||
icloudSummary.textContent = loginMessage;
|
||||
}
|
||||
showIcloudLoginHelp(message.payload || {});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ICLOUD_ALIASES_CHANGED': {
|
||||
queueIcloudAliasRefresh();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'AUTO_RUN_STATUS': {
|
||||
syncLatestState({
|
||||
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(message.payload.phase),
|
||||
|
||||
Reference in New Issue
Block a user