feat: 添加编辑和取消编辑功能到2925邮箱管理器,更新相关UI和测试用例
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
let actionInFlight = false;
|
let actionInFlight = false;
|
||||||
let listExpanded = false;
|
let listExpanded = false;
|
||||||
|
let editingAccountId = '';
|
||||||
|
|
||||||
function getMail2925Accounts(currentState = state.getLatestState()) {
|
function getMail2925Accounts(currentState = state.getLatestState()) {
|
||||||
return helpers.getMail2925Accounts(currentState);
|
return helpers.getMail2925Accounts(currentState);
|
||||||
@@ -93,7 +94,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyMail2925AccountMutation(account, options = {}) {
|
function applyMail2925AccountMutation(account) {
|
||||||
if (!account?.id) return;
|
if (!account?.id) return;
|
||||||
const latestState = state.getLatestState();
|
const latestState = state.getLatestState();
|
||||||
const currentId = getCurrentMail2925AccountId(latestState);
|
const currentId = getCurrentMail2925AccountId(latestState);
|
||||||
@@ -117,6 +118,32 @@
|
|||||||
if (dom.inputMail2925Password) dom.inputMail2925Password.value = '';
|
if (dom.inputMail2925Password) dom.inputMail2925Password.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function syncEditUi() {
|
||||||
|
if (dom.btnAddMail2925Account) {
|
||||||
|
dom.btnAddMail2925Account.textContent = editingAccountId ? '保存修改' : '添加账号';
|
||||||
|
}
|
||||||
|
if (dom.btnCancelMail2925Edit) {
|
||||||
|
dom.btnCancelMail2925Edit.style.display = editingAccountId ? '' : 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startEditingAccount(account) {
|
||||||
|
if (!account?.id) return;
|
||||||
|
editingAccountId = account.id;
|
||||||
|
if (dom.inputMail2925Email) dom.inputMail2925Email.value = String(account.email || '').trim();
|
||||||
|
if (dom.inputMail2925Password) dom.inputMail2925Password.value = String(account.password || '');
|
||||||
|
syncEditUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopEditingAccount(options = {}) {
|
||||||
|
const { clearForm = true } = options;
|
||||||
|
editingAccountId = '';
|
||||||
|
if (clearForm) {
|
||||||
|
clearMail2925Form();
|
||||||
|
}
|
||||||
|
syncEditUi();
|
||||||
|
}
|
||||||
|
|
||||||
function renderMail2925Accounts() {
|
function renderMail2925Accounts() {
|
||||||
if (!dom.mail2925AccountsList) return;
|
if (!dom.mail2925AccountsList) return;
|
||||||
|
|
||||||
@@ -160,6 +187,7 @@
|
|||||||
<div class="hotmail-account-actions">
|
<div class="hotmail-account-actions">
|
||||||
<button class="btn btn-outline btn-sm" type="button" data-account-action="select" data-account-id="${helpers.escapeHtml(account.id)}">使用此账号</button>
|
<button class="btn btn-outline btn-sm" type="button" data-account-action="select" data-account-id="${helpers.escapeHtml(account.id)}">使用此账号</button>
|
||||||
<button class="btn btn-primary btn-sm" type="button" data-account-action="login" data-account-id="${helpers.escapeHtml(account.id)}">登录</button>
|
<button class="btn btn-primary btn-sm" type="button" data-account-action="login" data-account-id="${helpers.escapeHtml(account.id)}">登录</button>
|
||||||
|
<button class="btn btn-outline btn-sm" type="button" data-account-action="edit" data-account-id="${helpers.escapeHtml(account.id)}">编辑</button>
|
||||||
<button class="btn btn-outline btn-sm" type="button" data-account-action="toggle-enabled" data-account-id="${helpers.escapeHtml(account.id)}">${account.enabled === false ? '启用' : '禁用'}</button>
|
<button class="btn btn-outline btn-sm" type="button" data-account-action="toggle-enabled" data-account-id="${helpers.escapeHtml(account.id)}">${account.enabled === false ? '启用' : '禁用'}</button>
|
||||||
${coolingDown ? `<button class="btn btn-outline btn-sm" type="button" data-account-action="clear-cooldown" data-account-id="${helpers.escapeHtml(account.id)}">清冷却</button>` : ''}
|
${coolingDown ? `<button class="btn btn-outline btn-sm" type="button" data-account-action="clear-cooldown" data-account-id="${helpers.escapeHtml(account.id)}">清冷却</button>` : ''}
|
||||||
<button class="btn btn-ghost btn-sm" type="button" data-account-action="delete" data-account-id="${helpers.escapeHtml(account.id)}">删除</button>
|
<button class="btn btn-ghost btn-sm" type="button" data-account-action="delete" data-account-id="${helpers.escapeHtml(account.id)}">删除</button>
|
||||||
@@ -185,6 +213,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updatingExisting = Boolean(editingAccountId);
|
||||||
actionInFlight = true;
|
actionInFlight = true;
|
||||||
if (dom.btnAddMail2925Account) {
|
if (dom.btnAddMail2925Account) {
|
||||||
dom.btnAddMail2925Account.disabled = true;
|
dom.btnAddMail2925Account.disabled = true;
|
||||||
@@ -195,6 +224,7 @@
|
|||||||
type: 'UPSERT_MAIL2925_ACCOUNT',
|
type: 'UPSERT_MAIL2925_ACCOUNT',
|
||||||
source: 'sidepanel',
|
source: 'sidepanel',
|
||||||
payload: {
|
payload: {
|
||||||
|
...(editingAccountId ? { id: editingAccountId } : {}),
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
},
|
},
|
||||||
@@ -204,8 +234,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyMail2925AccountMutation(response.account);
|
applyMail2925AccountMutation(response.account);
|
||||||
clearMail2925Form();
|
stopEditingAccount();
|
||||||
helpers.showToast(`已保存 2925 账号 ${email}`, 'success', 1800);
|
helpers.showToast(
|
||||||
|
updatingExisting
|
||||||
|
? `已更新 2925 账号 ${email}`
|
||||||
|
: `已保存 2925 账号 ${email}`,
|
||||||
|
'success',
|
||||||
|
1800
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
helpers.showToast(`保存 2925 账号失败:${err.message}`, 'error');
|
helpers.showToast(`保存 2925 账号失败:${err.message}`, 'error');
|
||||||
} finally {
|
} finally {
|
||||||
@@ -296,6 +332,7 @@
|
|||||||
mail2925Accounts: [],
|
mail2925Accounts: [],
|
||||||
currentMail2925AccountId: null,
|
currentMail2925AccountId: null,
|
||||||
});
|
});
|
||||||
|
stopEditingAccount();
|
||||||
refreshManagedAliasBaseEmail();
|
refreshManagedAliasBaseEmail();
|
||||||
renderMail2925Accounts();
|
renderMail2925Accounts();
|
||||||
helpers.showToast(`已删除全部 ${response.deletedCount || 0} 个 2925 账号`, 'success', 2200);
|
helpers.showToast(`已删除全部 ${response.deletedCount || 0} 个 2925 账号`, 'success', 2200);
|
||||||
@@ -356,6 +393,13 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action === 'edit') {
|
||||||
|
if (!targetAccount) throw new Error('未找到目标 2925 账号。');
|
||||||
|
startEditingAccount(targetAccount);
|
||||||
|
helpers.showToast(`已载入 ${targetAccount.email},修改后点“保存修改”即可`, 'info', 1800);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (action === 'toggle-enabled') {
|
if (action === 'toggle-enabled') {
|
||||||
if (!targetAccount) throw new Error('未找到目标 2925 账号。');
|
if (!targetAccount) throw new Error('未找到目标 2925 账号。');
|
||||||
const response = await runtime.sendMessage({
|
const response = await runtime.sendMessage({
|
||||||
@@ -415,6 +459,9 @@
|
|||||||
nextState.currentMail2925AccountId = null;
|
nextState.currentMail2925AccountId = null;
|
||||||
}
|
}
|
||||||
state.syncLatestState(nextState);
|
state.syncLatestState(nextState);
|
||||||
|
if (editingAccountId === accountId) {
|
||||||
|
stopEditingAccount();
|
||||||
|
}
|
||||||
refreshManagedAliasBaseEmail();
|
refreshManagedAliasBaseEmail();
|
||||||
renderMail2925Accounts();
|
renderMail2925Accounts();
|
||||||
helpers.showToast('2925 账号已删除', 'success', 1800);
|
helpers.showToast('2925 账号已删除', 'success', 1800);
|
||||||
@@ -446,8 +493,12 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
dom.btnAddMail2925Account?.addEventListener('click', handleAddMail2925Account);
|
dom.btnAddMail2925Account?.addEventListener('click', handleAddMail2925Account);
|
||||||
|
dom.btnCancelMail2925Edit?.addEventListener('click', () => {
|
||||||
|
stopEditingAccount();
|
||||||
|
});
|
||||||
dom.btnImportMail2925Accounts?.addEventListener('click', handleImportMail2925Accounts);
|
dom.btnImportMail2925Accounts?.addEventListener('click', handleImportMail2925Accounts);
|
||||||
dom.mail2925AccountsList?.addEventListener('click', handleAccountListClick);
|
dom.mail2925AccountsList?.addEventListener('click', handleAccountListClick);
|
||||||
|
syncEditUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
<div class="header-btns">
|
<div class="header-btns">
|
||||||
<div class="run-group">
|
<div class="run-group">
|
||||||
<button id="btn-contribution-mode" class="btn btn-outline btn-sm btn-contribution-mode" type="button"
|
<button id="btn-contribution-mode" class="btn btn-outline btn-sm btn-contribution-mode" type="button"
|
||||||
aria-pressed="false" title="进入贡献模式">贡献</button>
|
aria-pressed="false" title="进入贡献模式">贡献/使用</button>
|
||||||
<input type="number" id="input-run-count" class="run-count-input" value="1" min="1" max="50" title="运行次数" />
|
<input type="number" id="input-run-count" class="run-count-input" value="1" min="1" max="50" title="运行次数" />
|
||||||
<button id="btn-auto-run" class="btn btn-success" title="自动执行全部步骤">
|
<button id="btn-auto-run" class="btn btn-success" title="自动执行全部步骤">
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||||
@@ -455,7 +455,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="data-row hotmail-actions-row">
|
<div class="data-row hotmail-actions-row">
|
||||||
<span class="data-label"></span>
|
<span class="data-label"></span>
|
||||||
<button id="btn-add-mail2925-account" class="btn btn-primary btn-sm" type="button">添加账号</button>
|
<div class="data-inline">
|
||||||
|
<button id="btn-add-mail2925-account" class="btn btn-primary btn-sm" type="button">添加账号</button>
|
||||||
|
<button id="btn-cancel-mail2925-edit" class="btn btn-outline btn-sm" type="button" style="display:none;">取消编辑</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="data-row hotmail-import-row">
|
<div class="data-row hotmail-import-row">
|
||||||
<span class="data-label">批量导入</span>
|
<span class="data-label">批量导入</span>
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ const inputMail2925Email = document.getElementById('input-mail2925-email');
|
|||||||
const inputMail2925Password = document.getElementById('input-mail2925-password');
|
const inputMail2925Password = document.getElementById('input-mail2925-password');
|
||||||
const inputMail2925Import = document.getElementById('input-mail2925-import');
|
const inputMail2925Import = document.getElementById('input-mail2925-import');
|
||||||
const btnAddMail2925Account = document.getElementById('btn-add-mail2925-account');
|
const btnAddMail2925Account = document.getElementById('btn-add-mail2925-account');
|
||||||
|
const btnCancelMail2925Edit = document.getElementById('btn-cancel-mail2925-edit');
|
||||||
const btnImportMail2925Accounts = document.getElementById('btn-import-mail2925-accounts');
|
const btnImportMail2925Accounts = document.getElementById('btn-import-mail2925-accounts');
|
||||||
const btnDeleteAllMail2925Accounts = document.getElementById('btn-delete-all-mail2925-accounts');
|
const btnDeleteAllMail2925Accounts = document.getElementById('btn-delete-all-mail2925-accounts');
|
||||||
const btnToggleMail2925List = document.getElementById('btn-toggle-mail2925-list');
|
const btnToggleMail2925List = document.getElementById('btn-toggle-mail2925-list');
|
||||||
@@ -3040,6 +3041,7 @@ const mail2925Manager = window.SidepanelMail2925Manager?.createMail2925Manager({
|
|||||||
},
|
},
|
||||||
dom: {
|
dom: {
|
||||||
btnAddMail2925Account,
|
btnAddMail2925Account,
|
||||||
|
btnCancelMail2925Edit,
|
||||||
btnDeleteAllMail2925Accounts,
|
btnDeleteAllMail2925Accounts,
|
||||||
btnImportMail2925Accounts,
|
btnImportMail2925Accounts,
|
||||||
btnToggleMail2925List,
|
btnToggleMail2925List,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ test('sidepanel html keeps a single contribution mode button in header', () => {
|
|||||||
|
|
||||||
assert.equal(matches.length, 1);
|
assert.equal(matches.length, 1);
|
||||||
assert.match(html, /id="btn-contribution-mode"[^>]*title="进入贡献模式"/);
|
assert.match(html, /id="btn-contribution-mode"[^>]*title="进入贡献模式"/);
|
||||||
|
assert.match(html, />贡献\/使用<\/button>/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sidepanel source no longer keeps the legacy upload-page handler on the header contribution button', () => {
|
test('sidepanel source no longer keeps the legacy upload-page handler on the header contribution button', () => {
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
const test = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
|
||||||
|
test('sidepanel html contains cancel edit button for mail2925 form', () => {
|
||||||
|
const html = fs.readFileSync('sidepanel/sidepanel.html', 'utf8');
|
||||||
|
assert.match(html, /id="btn-cancel-mail2925-edit"/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('mail2925 manager renders edit action for existing accounts', () => {
|
||||||
|
const source = fs.readFileSync('sidepanel/mail-2925-manager.js', 'utf8');
|
||||||
|
const windowObject = {};
|
||||||
|
const localStorageMock = {
|
||||||
|
getItem() {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
setItem() {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const api = new Function('window', 'localStorage', `${source}; return window.SidepanelMail2925Manager;`)(
|
||||||
|
windowObject,
|
||||||
|
localStorageMock
|
||||||
|
);
|
||||||
|
|
||||||
|
const mail2925AccountsList = { innerHTML: '', addEventListener() {} };
|
||||||
|
const manager = api.createMail2925Manager({
|
||||||
|
state: {
|
||||||
|
getLatestState: () => ({
|
||||||
|
currentMail2925AccountId: 'acc-1',
|
||||||
|
mail2925Accounts: [{
|
||||||
|
id: 'acc-1',
|
||||||
|
email: 'demo@2925.com',
|
||||||
|
password: 'secret',
|
||||||
|
enabled: true,
|
||||||
|
lastLoginAt: 0,
|
||||||
|
lastUsedAt: 0,
|
||||||
|
lastLimitAt: 0,
|
||||||
|
disabledUntil: 0,
|
||||||
|
lastError: '',
|
||||||
|
}],
|
||||||
|
}),
|
||||||
|
syncLatestState() {},
|
||||||
|
},
|
||||||
|
dom: {
|
||||||
|
btnAddMail2925Account: { textContent: '', disabled: false, addEventListener() {} },
|
||||||
|
btnCancelMail2925Edit: { style: { display: 'none' }, addEventListener() {} },
|
||||||
|
btnDeleteAllMail2925Accounts: { textContent: '', disabled: false, addEventListener() {} },
|
||||||
|
btnImportMail2925Accounts: { disabled: false, addEventListener() {} },
|
||||||
|
btnToggleMail2925List: { textContent: '', disabled: false, setAttribute() {}, addEventListener() {} },
|
||||||
|
inputMail2925Email: { value: '' },
|
||||||
|
inputMail2925Import: { value: '' },
|
||||||
|
inputMail2925Password: { value: '' },
|
||||||
|
mail2925AccountsList,
|
||||||
|
mail2925ListShell: { classList: { toggle() {} } },
|
||||||
|
},
|
||||||
|
helpers: {
|
||||||
|
getMail2925Accounts: (state) => state.mail2925Accounts || [],
|
||||||
|
escapeHtml: (value) => String(value || ''),
|
||||||
|
showToast() {},
|
||||||
|
openConfirmModal: async () => true,
|
||||||
|
copyTextToClipboard: async () => {},
|
||||||
|
refreshManagedAliasBaseEmail() {},
|
||||||
|
},
|
||||||
|
runtime: {
|
||||||
|
sendMessage: async () => ({}),
|
||||||
|
},
|
||||||
|
constants: {
|
||||||
|
copyIcon: '',
|
||||||
|
displayTimeZone: 'Asia/Shanghai',
|
||||||
|
expandedStorageKey: 'multipage-mail2925-list-expanded',
|
||||||
|
},
|
||||||
|
mail2925Utils: {
|
||||||
|
getMail2925AccountStatus: () => 'ready',
|
||||||
|
getMail2925ListToggleLabel: () => '展开列表(1)',
|
||||||
|
upsertMail2925AccountInList: (_accounts, nextAccount) => [nextAccount],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
manager.renderMail2925Accounts();
|
||||||
|
assert.match(mail2925AccountsList.innerHTML, /data-account-action="edit"/);
|
||||||
|
});
|
||||||
@@ -17,6 +17,7 @@ test('sidepanel html contains mail2925 pool toggle and selector controls', () =>
|
|||||||
|
|
||||||
assert.match(html, /id="input-mail2925-use-account-pool"/);
|
assert.match(html, /id="input-mail2925-use-account-pool"/);
|
||||||
assert.match(html, /id="select-mail2925-pool-account"/);
|
assert.match(html, /id="select-mail2925-pool-account"/);
|
||||||
|
assert.match(html, /id="btn-cancel-mail2925-edit"/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('mail2925 manager exposes a factory and renders empty state', () => {
|
test('mail2925 manager exposes a factory and renders empty state', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user