Release 1.1.0: switch to dewemoji.com and simplify account UI
This commit is contained in:
@@ -4,7 +4,7 @@ This checklist verifies extension behavior against current backend flow.
|
|||||||
|
|
||||||
## Preconditions
|
## Preconditions
|
||||||
- Extension loaded in Chrome (unpacked).
|
- Extension loaded in Chrome (unpacked).
|
||||||
- Backend reachable at `https://api.dewemoji.com/v1`.
|
- Backend reachable at `https://dewemoji.com/v1`.
|
||||||
- Backend deployed with extension search route:
|
- Backend deployed with extension search route:
|
||||||
- `GET /v1/extension/search`
|
- `GET /v1/extension/search`
|
||||||
- Browser DevTools available for network/console checks.
|
- Browser DevTools available for network/console checks.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Dewemoji - Emojis Made Effortless",
|
"name": "Dewemoji - Emojis Made Effortless",
|
||||||
"description": "Find and copy emojis instantly. Optional Pro license unlocks tone lock, insert mode, and more.",
|
"description": "Find and copy emojis instantly. Connect your account to use private keywords across web and extension.",
|
||||||
"version": "1.0.2",
|
"version": "1.1.0",
|
||||||
"offline_enabled": false,
|
"offline_enabled": false,
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
"storage",
|
||||||
@@ -12,8 +12,7 @@
|
|||||||
],
|
],
|
||||||
"host_permissions": [
|
"host_permissions": [
|
||||||
"<all_urls>",
|
"<all_urls>",
|
||||||
"https://dewemoji.backoffice.biz.id/*",
|
"https://dewemoji.com/*"
|
||||||
"https://api.dewemoji.com/*"
|
|
||||||
],
|
],
|
||||||
"web_accessible_resources": [
|
"web_accessible_resources": [
|
||||||
{
|
{
|
||||||
|
|||||||
44
panel.html
44
panel.html
@@ -87,33 +87,33 @@
|
|||||||
<div id="tab-pro" class="tabpane">
|
<div id="tab-pro" class="tabpane">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="lbl">Account</label>
|
<label class="lbl">Account</label>
|
||||||
<div class="row">
|
<div id="account-connect-form">
|
||||||
<input id="account-email" class="inp" placeholder="Email" type="email" autocomplete="username">
|
<div class="row">
|
||||||
|
<input id="account-email" class="inp" placeholder="Email" type="email" autocomplete="username">
|
||||||
|
</div>
|
||||||
|
<div class="row" style="margin-top:6px;">
|
||||||
|
<input id="account-password" class="inp" placeholder="Password" type="password" autocomplete="current-password">
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<button id="account-login" class="btn">Connect</button>
|
||||||
|
</div>
|
||||||
|
<div class="row" style="margin-top:6px;">
|
||||||
|
<span id="account-status" class="muted">Not connected. Public keywords only.</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" style="margin-top:6px;">
|
|
||||||
<input id="account-password" class="inp" placeholder="Password" type="password" autocomplete="current-password">
|
<div id="account-connected" style="display:none;">
|
||||||
</div>
|
<div class="row">
|
||||||
<div class="row">
|
<span id="account-greeting" class="muted">Connected.</span>
|
||||||
<button id="account-login" class="btn">Connect</button>
|
</div>
|
||||||
<button id="account-logout" class="btn ghost">Logout</button>
|
<div class="row" style="margin-top:8px;">
|
||||||
</div>
|
<button id="account-logout" class="btn ghost">Logout</button>
|
||||||
<div class="row" style="margin-top:6px;">
|
</div>
|
||||||
<span id="account-status" class="muted">Not connected. Public keywords only.</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tone settings are injected here by panel.js -->
|
<!-- Tone settings are injected here by panel.js -->
|
||||||
|
|
||||||
<!-- Diagnostics -->
|
|
||||||
<div class="field">
|
|
||||||
<label class="lbl">Extension Health</label>
|
|
||||||
<div class="row">
|
|
||||||
<button id="diag-run" class="btn">Run health check</button>
|
|
||||||
<span id="diag-spin" class="muted" style="display:none;">Running…</span>
|
|
||||||
</div>
|
|
||||||
<div id="diag-out" class="diagbox muted"></div>
|
|
||||||
<p class="hint muted">Tip: Click in a text box on the page first, then run health check.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -136,6 +136,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="toast" class="toast" role="status" aria-live="polite"></div>
|
<div id="toast" class="toast" role="status" aria-live="polite"></div>
|
||||||
<script src="panel.js?ver=1.0.1"></script>
|
<script src="panel.js?ver=1.1.0"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
61
panel.js
61
panel.js
@@ -7,6 +7,7 @@ let licenseKeyCurrent = '';
|
|||||||
let authApiKey = '';
|
let authApiKey = '';
|
||||||
let authTier = 'guest'; // guest | free | personal
|
let authTier = 'guest'; // guest | free | personal
|
||||||
let authEmail = '';
|
let authEmail = '';
|
||||||
|
let authName = '';
|
||||||
|
|
||||||
// Tone settings (persisted in sync storage)
|
// Tone settings (persisted in sync storage)
|
||||||
let toneLock = false; // if true, always use preferred tone
|
let toneLock = false; // if true, always use preferred tone
|
||||||
@@ -29,6 +30,9 @@ const accountPasswordEl = document.getElementById('account-password');
|
|||||||
const accountLoginBtn = document.getElementById('account-login');
|
const accountLoginBtn = document.getElementById('account-login');
|
||||||
const accountLogoutBtn = document.getElementById('account-logout');
|
const accountLogoutBtn = document.getElementById('account-logout');
|
||||||
const accountStatusEl = document.getElementById('account-status');
|
const accountStatusEl = document.getElementById('account-status');
|
||||||
|
const accountGreetingEl = document.getElementById('account-greeting');
|
||||||
|
const accountConnectFormEl = document.getElementById('account-connect-form');
|
||||||
|
const accountConnectedEl = document.getElementById('account-connected');
|
||||||
|
|
||||||
// --- Branded confirm modal helper ---
|
// --- Branded confirm modal helper ---
|
||||||
function showConfirmModal(opts = {}) {
|
function showConfirmModal(opts = {}) {
|
||||||
@@ -113,12 +117,8 @@ function setLicenseBusy(on, label){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const diagRunBtn = document.getElementById('diag-run');
|
|
||||||
const diagSpin = document.getElementById('diag-spin');
|
|
||||||
const diagOut = document.getElementById('diag-out');
|
|
||||||
|
|
||||||
const API = {
|
const API = {
|
||||||
base: "https://dewemoji.backoffice.biz.id/v1",
|
base: "https://dewemoji.com/v1",
|
||||||
list: "/emojis"
|
list: "/emojis"
|
||||||
};
|
};
|
||||||
API.cats = "/categories";
|
API.cats = "/categories";
|
||||||
@@ -650,8 +650,7 @@ function getListPaths(){
|
|||||||
function getApiBases(){
|
function getApiBases(){
|
||||||
const list = [
|
const list = [
|
||||||
API.base,
|
API.base,
|
||||||
'https://dewemoji.backoffice.biz.id/v1',
|
'https://dewemoji.com/v1',
|
||||||
'https://api.dewemoji.com/v1',
|
|
||||||
'http://127.0.0.1:8000/v1',
|
'http://127.0.0.1:8000/v1',
|
||||||
];
|
];
|
||||||
const seen = new Set();
|
const seen = new Set();
|
||||||
@@ -1233,13 +1232,14 @@ loadCategories().catch(console.error);
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
async function loadSettings() {
|
async function loadSettings() {
|
||||||
const data = await chrome.storage.local.get(['actionMode', 'authApiKey', 'authTier', 'authEmail']);
|
const data = await chrome.storage.local.get(['actionMode', 'authApiKey', 'authTier', 'authEmail', 'authName']);
|
||||||
licenseValid = true;
|
licenseValid = true;
|
||||||
refreshPageLimit();
|
refreshPageLimit();
|
||||||
actionMode = data.actionMode || 'copy';
|
actionMode = data.actionMode || 'copy';
|
||||||
authApiKey = data.authApiKey || '';
|
authApiKey = data.authApiKey || '';
|
||||||
authTier = data.authTier || 'guest';
|
authTier = data.authTier || 'guest';
|
||||||
authEmail = data.authEmail || '';
|
authEmail = data.authEmail || '';
|
||||||
|
authName = data.authName || '';
|
||||||
if (accountEmailEl && authEmail) accountEmailEl.value = authEmail;
|
if (accountEmailEl && authEmail) accountEmailEl.value = authEmail;
|
||||||
applyLicenseUI();
|
applyLicenseUI();
|
||||||
applyModeUI();
|
applyModeUI();
|
||||||
@@ -1253,7 +1253,7 @@ async function loadSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveSettings() {
|
async function saveSettings() {
|
||||||
await chrome.storage.local.set({ licenseValid: true, actionMode, authApiKey, authTier, authEmail });
|
await chrome.storage.local.set({ licenseValid: true, actionMode, authApiKey, authTier, authEmail, authName });
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Helper to render Free status with CTA ---
|
// --- Helper to render Free status with CTA ---
|
||||||
@@ -1305,6 +1305,8 @@ function updateAccountUI() {
|
|||||||
if (!accountStatusEl) return;
|
if (!accountStatusEl) return;
|
||||||
if (!authApiKey) {
|
if (!authApiKey) {
|
||||||
accountStatusEl.textContent = 'Not connected. Public keywords only.';
|
accountStatusEl.textContent = 'Not connected. Public keywords only.';
|
||||||
|
if (accountConnectFormEl) accountConnectFormEl.style.display = '';
|
||||||
|
if (accountConnectedEl) accountConnectedEl.style.display = 'none';
|
||||||
if (accountLoginBtn) {
|
if (accountLoginBtn) {
|
||||||
accountLoginBtn.disabled = false;
|
accountLoginBtn.disabled = false;
|
||||||
accountLoginBtn.style.display = '';
|
accountLoginBtn.style.display = '';
|
||||||
@@ -1319,7 +1321,12 @@ function updateAccountUI() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const tierLabel = authTier === 'personal' ? 'Personal' : 'Free';
|
const tierLabel = authTier === 'personal' ? 'Personal' : 'Free';
|
||||||
|
const fallbackName = (authEmail || 'User').split('@')[0] || 'User';
|
||||||
|
const displayName = (authName || '').trim() || fallbackName;
|
||||||
accountStatusEl.textContent = `Connected as ${authEmail || 'user'} (${tierLabel})`;
|
accountStatusEl.textContent = `Connected as ${authEmail || 'user'} (${tierLabel})`;
|
||||||
|
if (accountGreetingEl) accountGreetingEl.textContent = `Hi, ${displayName}`;
|
||||||
|
if (accountConnectFormEl) accountConnectFormEl.style.display = 'none';
|
||||||
|
if (accountConnectedEl) accountConnectedEl.style.display = '';
|
||||||
if (accountLoginBtn) {
|
if (accountLoginBtn) {
|
||||||
accountLoginBtn.disabled = true;
|
accountLoginBtn.disabled = true;
|
||||||
accountLoginBtn.style.display = 'none';
|
accountLoginBtn.style.display = 'none';
|
||||||
@@ -1535,6 +1542,7 @@ accountLoginBtn?.addEventListener('click', async () => {
|
|||||||
authApiKey = data?.api_key || '';
|
authApiKey = data?.api_key || '';
|
||||||
authTier = String(data?.user?.tier || 'free');
|
authTier = String(data?.user?.tier || 'free');
|
||||||
authEmail = String(data?.user?.email || email);
|
authEmail = String(data?.user?.email || email);
|
||||||
|
authName = String(data?.user?.name || '');
|
||||||
await saveSettings();
|
await saveSettings();
|
||||||
updateAccountUI();
|
updateAccountUI();
|
||||||
setStatusTag();
|
setStatusTag();
|
||||||
@@ -1566,6 +1574,7 @@ accountLogoutBtn?.addEventListener('click', async () => {
|
|||||||
} finally {
|
} finally {
|
||||||
authApiKey = '';
|
authApiKey = '';
|
||||||
authTier = 'guest';
|
authTier = 'guest';
|
||||||
|
authName = '';
|
||||||
await saveSettings();
|
await saveSettings();
|
||||||
updateAccountUI();
|
updateAccountUI();
|
||||||
setStatusTag();
|
setStatusTag();
|
||||||
@@ -1574,40 +1583,6 @@ accountLogoutBtn?.addEventListener('click', async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function renderDiag(obj) {
|
|
||||||
const lines = [];
|
|
||||||
lines.push(`Content script loaded: ${obj ? 'yes' : 'no'}`);
|
|
||||||
if (!obj) return lines.join('\n');
|
|
||||||
|
|
||||||
lines.push(`Active editable type: ${obj.activeType ?? 'none'}`);
|
|
||||||
lines.push(`Has caret/selection: ${obj.hasRange ? 'yes' : 'no'}`);
|
|
||||||
lines.push(`Last insert result: ${obj.lastInsertOK === null ? 'n/a' : obj.lastInsertOK ? 'success' : 'failed'}`);
|
|
||||||
if (obj.lastInsertMessage) lines.push(`Note: ${obj.lastInsertMessage}`);
|
|
||||||
return lines.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runDiagnostics() {
|
|
||||||
diagOut.textContent = '';
|
|
||||||
diagSpin.style.display = 'inline';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
||||||
if (!tab?.id) { diagOut.textContent = 'No active tab.'; return; }
|
|
||||||
|
|
||||||
const ready = await ensureContentScript(tab.id);
|
|
||||||
if (!ready) { diagOut.textContent = await renderDiag(null); return; }
|
|
||||||
|
|
||||||
const info = await chrome.tabs.sendMessage(tab.id, { type: 'dewemoji_diag' });
|
|
||||||
diagOut.textContent = await renderDiag(info || null);
|
|
||||||
} catch (e) {
|
|
||||||
diagOut.textContent = `Error: ${e?.message || e}`;
|
|
||||||
} finally {
|
|
||||||
diagSpin.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diagRunBtn?.addEventListener('click', () => runDiagnostics());
|
|
||||||
|
|
||||||
async function performEmojiAction(glyph) {
|
async function performEmojiAction(glyph) {
|
||||||
// Free users always copy
|
// Free users always copy
|
||||||
const mode = licenseValid ? actionMode : 'copy';
|
const mode = licenseValid ? actionMode : 'copy';
|
||||||
|
|||||||
Reference in New Issue
Block a user