feat: integrate iCloud hide my email generation into OAuth flow

- 合并 PR #72 的核心改动:新增 iCloud Hide My Email 生成、alias 管理与成功后自动标记/删除流程
- 本地补充修复:解决与 Cloudflare Temp Email 分支差异的冲突,并补齐合并后的测试常量
- 影响范围:background 邮箱生成与 Step 9 收尾、sidepanel alias 管理 UI、manifest/rules 与新增 iCloud 测试
This commit is contained in:
Q3CC
2026-04-14 23:16:59 +08:00
parent d5a63f9e55
commit 14e3d6271d
11 changed files with 2007 additions and 11 deletions
+573 -7
View File
@@ -79,6 +79,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');
@@ -174,6 +194,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;
@@ -1032,6 +1057,8 @@ function collectSettingsPayload() {
customPassword: inputPassword.value,
mailProvider: selectMailProvider.value,
emailGenerator: selectEmailGenerator.value,
autoDeleteUsedIcloudAlias: checkboxAutoDeleteIcloud?.checked,
icloudHostPreference: selectIcloudHostPreference?.value || 'auto',
emailPrefix: inputEmailPrefix.value.trim(),
inbucketHost: inputInbucketHost.value.trim(),
inbucketMailbox: inputInbucketMailbox.value.trim(),
@@ -1323,10 +1350,26 @@ function applySettingsState(state) {
? 'custom'
: '163');
selectMailProvider.value = restoredMailProvider;
const restoredEmailGenerator = String(state?.emailGenerator || '').trim().toLowerCase();
selectEmailGenerator.value = ['cloudflare', 'cloudflare-temp-email'].includes(restoredEmailGenerator)
? restoredEmailGenerator
: 'duck';
{
const restoredGenerator = String(state?.emailGenerator || '').trim().toLowerCase();
if (restoredGenerator === 'icloud') {
selectEmailGenerator.value = 'icloud';
} else if (restoredGenerator === 'cloudflare') {
selectEmailGenerator.value = 'cloudflare';
} else if (restoredGenerator === '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 || '';
@@ -1362,6 +1405,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;
@@ -1609,6 +1655,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';
@@ -1618,6 +1667,14 @@ function getEmailGeneratorUiCopy() {
if (getSelectedEmailGenerator() === 'custom') {
return getCustomMailProviderUiCopy();
}
if (getSelectedEmailGenerator() === 'icloud') {
return {
buttonLabel: '获取',
placeholder: '点击获取 iCloud 隐私邮箱,或手动粘贴邮箱',
successVerb: '获取',
label: 'iCloud 隐私邮箱',
};
}
if (getSelectedEmailGenerator() === 'cloudflare') {
return {
buttonLabel: '生成',
@@ -1946,14 +2003,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) {
@@ -2002,7 +2071,7 @@ function updateMailProviderUI() {
? '请先校验并选择一个 Hotmail 账号'
: (useGeneratedAlias
? '步骤 3 会自动生成邮箱,无需手动获取'
: (useCustomEmail ? '请先填写自定义注册邮箱,成功一轮后会自动清空' : '先自动获取邮箱,或手动粘贴邮箱后再继续'));
: (useCustomEmail ? '请先填写自定义注册邮箱,成功一轮后会自动清空' : `先自动获取${uiCopy.label},或手动粘贴邮箱后再继续`));
}
if (useHotmail) {
inputEmail.value = getCurrentHotmailEmail();
@@ -2166,6 +2235,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);
}
@@ -2288,6 +2362,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();
@@ -2316,6 +2759,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) {
@@ -2628,6 +3074,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);
});
@@ -3106,6 +3621,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();
@@ -3202,6 +3726,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);
@@ -3502,6 +4039,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({
@@ -3559,6 +4101,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();
@@ -3582,6 +4133,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),