Skip to content

Commit 0912603

Browse files
deadlyjackAjit Kumar
andauthored
feat: add native app auth code login flow (Acode-Foundation#2440)
* feat: add native app auth code login flow - move Android login to custom tab auth-code exchange - handle reserved auth callback intents natively - add semantic plugin version comparison for updates - improve sidebar avatar loading state - cover auth/version and quick-tools behavior in sanity tests * fix: harden app auth callback handling * fix: remove stray quick tools sanity tests - drop quick-tools tests accidentally carried over during conflict resolution - remove missing quickToolsState import from sanity tests - keep auth branch version comparison coverage intact * fix: preserve plugin update fallback notifications - keep server-confirmed plugin updates when legacy metadata fetch lacks a version - reject any previous pending Android login callback before replacing it --------- Co-authored-by: Ajit Kumar <dellevenjack@gmail>
1 parent 7255999 commit 0912603

12 files changed

Lines changed: 372 additions & 35 deletions

File tree

src/components/sidebar/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ function create($container, $toggler) {
216216
return;
217217
}
218218

219-
defaultAvatar.classList.add("loading");
219+
defaultAvatar.classList.add("avatar-loading");
220220

221221
const img = <img alt="User avatar" className="avatar" />;
222222
const avatarFile = await getUserAvatar(user);

src/components/sidebar/style.scss

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ body.no-animation {
132132
&.active {
133133
opacity: 1;
134134
}
135+
136+
&.avatar-loading {
137+
opacity: 0.45;
138+
background-color: rgba(255, 255, 255, 0.08);
139+
animation: sidebar-avatar-loading 1.2s ease-in-out infinite;
140+
141+
&:hover {
142+
opacity: 0.45;
143+
background-color: rgba(255, 255, 255, 0.08);
144+
}
145+
}
135146
}
136147
}
137148

@@ -340,6 +351,17 @@ body.no-animation {
340351
}
341352
}
342353

354+
@keyframes sidebar-avatar-loading {
355+
0%,
356+
100% {
357+
transform: scale(1);
358+
}
359+
360+
50% {
361+
transform: scale(0.94);
362+
}
363+
}
364+
343365
.user-menu {
344366
position: absolute;
345367
bottom: 55px;

src/handlers/intent.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ export default async function HandleIntent(intent = {}) {
2828
const path = url.replace("acode://", "");
2929
const [module, action, value] = path.split("/");
3030

31+
if (module === "auth" && action === "callback") {
32+
return;
33+
}
34+
3135
let defaultPrevented = false;
3236
const event = new IntentEvent(module, action, value);
3337
for (const handler of handlers) {

src/lib/auth.js

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import toast from "components/toast";
2-
import { addIntentHandler } from "handlers/intent";
31
import config from "./config";
4-
import customTab from "./customTab";
52

63
/**
74
* @typedef {object} User
@@ -29,6 +26,7 @@ let loggedInUser = null;
2926
let cacheTimeout = null;
3027

3128
const CACHE_USER_KEY = "cached-logged-in-user";
29+
const LOGIN_RESUME_TIMEOUT_MS = 60_000;
3230

3331
const loginEvents = {
3432
listeners: new Set(),
@@ -50,7 +48,6 @@ class AuthService {
5048
#loginTimeout = null;
5149

5250
constructor() {
53-
addIntentHandler(this.onIntentReceiver.bind(this));
5451
loginEvents.addListener(() => {
5552
clearTimeout(this.#loginTimeout);
5653
for (const callback of this.#loginCallbacks) {
@@ -66,29 +63,10 @@ class AuthService {
6663
}
6764

6865
this.#loginCallbacks.clear();
69-
}, 1000);
66+
}, LOGIN_RESUME_TIMEOUT_MS);
7067
});
7168
}
7269

73-
async onIntentReceiver(event) {
74-
try {
75-
if (event?.module === "user" && event?.action === "login") {
76-
if (event?.value) {
77-
this.#exec("saveToken", [event.value]);
78-
toast("Logged in successfully");
79-
80-
setTimeout(() => {
81-
loginEvents.emit();
82-
}, 500);
83-
}
84-
}
85-
return null;
86-
} catch (error) {
87-
console.error("Failed to parse intent token.", error);
88-
return null;
89-
}
90-
}
91-
9270
/**
9371
* Helper to wrap cordova.exec in a Promise
9472
*/
@@ -159,12 +137,22 @@ class AuthService {
159137

160138
async login() {
161139
return new Promise((resolve, reject) => {
162-
customTab(`${config.BASE_URL}/login?redirect=app`).catch((err) => {
163-
console.error("Custom tab error", err);
164-
reject("Failed to open browser");
165-
});
166-
167-
this.#loginCallbacks.add({ resolve, reject });
140+
const callback = { resolve, reject };
141+
this.#loginCallbacks.add(callback);
142+
this.#exec("login", [
143+
{
144+
baseUrl: config.BASE_URL,
145+
appVersionCode: window.BuildInfo?.versionCode || 0,
146+
},
147+
])
148+
.then(() => {
149+
loginEvents.emit();
150+
})
151+
.catch((err) => {
152+
console.error("Native login error", err);
153+
this.#loginCallbacks.delete(callback);
154+
reject("Failed to login");
155+
});
168156
});
169157
}
170158
}

src/lib/checkPluginsUpdate.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import fsOperation from "fileSystem";
22
import Url from "utils/Url";
3+
import { isVersionGreater } from "utils/version";
34
import config from "./config";
45

56
export default async function checkPluginsUpdate() {
@@ -20,7 +21,26 @@ export default async function checkPluginsUpdate() {
2021

2122
if (res.ok) {
2223
const json = await res.json();
23-
if (json.update) {
24+
if (!json.update) return;
25+
26+
if (json.version) {
27+
if (isVersionGreater(json.version, plugin.version)) {
28+
updates.push(plugin.id);
29+
}
30+
return;
31+
}
32+
33+
const remotePlugin = await fsOperation(
34+
config.API_BASE,
35+
`plugin/${plugin.id}`,
36+
)
37+
.readFile("json")
38+
.catch(() => null);
39+
40+
if (
41+
!remotePlugin?.version ||
42+
isVersionGreater(remotePlugin.version, plugin.version)
43+
) {
2444
updates.push(plugin.id);
2545
}
2646
}

src/lib/installPlugin.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import purchaseListener from "handlers/purchase";
66
import JSZip from "jszip";
77
import helpers from "utils/helpers";
88
import Url from "utils/Url";
9+
import { isVersionGreater } from "utils/version";
910
import config from "./config";
1011
import InstallState from "./installState";
1112
import { loadPluginWithTimeout } from "./loadPlugins";
@@ -406,7 +407,8 @@ async function resolveDepsManifest(deps) {
406407
throw new Error(`Unknown plugin dependency: ${dependency}`);
407408

408409
const version = await getInstalledPluginVersion(remoteDependency.id);
409-
if (remoteDependency?.version === version) continue;
410+
if (version && !isVersionGreater(remoteDependency?.version, version))
411+
continue;
410412

411413
if (remoteDependency.dependencies) {
412414
const manifests = await resolveDepsManifest(

src/pages/plugin/plugin.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import markdownItTaskLists from "markdown-it-task-lists";
2121
import { highlightCodeBlock, initHighlighting } from "utils/codeHighlight";
2222
import helpers from "utils/helpers";
2323
import Url from "utils/Url";
24+
import { isVersionGreater } from "utils/version";
2425
import view, { cleanups } from "./plugin.view.js";
2526

2627
let $lastPluginPage;
@@ -160,7 +161,10 @@ export default async function PluginInclude(
160161

161162
if (cancelled || !remotePlugin) return;
162163

163-
if (installed && remotePlugin?.version !== plugin.version) {
164+
if (
165+
installed &&
166+
isVersionGreater(remotePlugin?.version, plugin.version)
167+
) {
164168
currentVersion = plugin.version;
165169
update = true;
166170
}

src/plugins/auth/plugin.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
</config-file>
1313

1414
<framework src="androidx.security:security-crypto:1.1.0" />
15+
<framework src="androidx.browser:browser:1.5.0" />
1516

1617
<source-file src="src/android/Authenticator.java" target-dir="src/com/foxdebug/acode/rk/auth" />
1718
<source-file src="src/android/EncryptedPreferenceManager.java" target-dir="src/com/foxdebug/acode/rk/auth" />
1819

1920

2021
</platform>
21-
</plugin>
22+
</plugin>

0 commit comments

Comments
 (0)