Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ concurrency:
jobs:
verify:
name: Verify (lint, unit, build, e2e)
# Prevent duplicate runs on internal PRs from branches that already trigger a push event
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository || !contains(fromJSON('["main", "dev"]'), github.event.pull_request.head.ref)
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG-DEV.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## [1.11.1-dev.1](https://github.com/codebridger/subturtle-extension-apps/compare/v1.11.0...v1.11.1-dev.1) (2026-05-06)


### Bug Fixes

* [#86](https://github.com/codebridger/subturtle-extension-apps/issues/86)exgqfpu skip token writes to host LS on dashboard origins ([838451e](https://github.com/codebridger/subturtle-extension-apps/commit/838451e9ad10d3a3755691b4ca586a0d3815a008)), closes [#86exgqfpu](https://github.com/codebridger/subturtle-extension-apps/issues/86exgqfpu)

# [1.11.0-dev.4](https://github.com/codebridger/subturtle-extension-apps/compare/v1.11.0-dev.3...v1.11.0-dev.4) (2026-05-06)


Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "subturtle-extension",
"version": "1.11.1",
"version": "1.11.1-dev.1",
"private": true,
"scripts": {
"dev": "webpack --watch",
Expand Down Expand Up @@ -60,4 +60,4 @@
"vue": "3.5.17",
"vue-router": "4.5.1"
}
}
}
18 changes: 14 additions & 4 deletions src/common/helper/log.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
export function log(...arg) {
console.log("Subturtle:", ...arg);
export function log(...arg: any[]) {
console.log("[Subturtle]", ...arg);
}

export function error(...arg) {
console.error("Subturtle:", ...arg);
export function warn(...arg: any[]) {
console.warn("[Subturtle]", ...arg);
}

export function debug(...arg: any[]) {
if (process.env.NODE_ENV !== "production") {
console.debug("[Subturtle]", ...arg);
}
}

export function error(...arg: any[]) {
console.error("[Subturtle]", ...arg);
}
3 changes: 3 additions & 0 deletions src/common/helper/massage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BaseMessage, LoginStatusResponse } from "../types/messaging";
import { debug, log } from "./log";

/**
* Send message to background
Expand All @@ -10,6 +11,7 @@ export async function sendMessage<T extends BaseMessage | LoginStatusResponse>(
) {
return new Promise<T>((resolve, reject) => {
try {
debug("Sending message to background:", message);
chrome.runtime.sendMessage(message, (response) => {
if (chrome.runtime.lastError) {
// Chrome runtime errors should be properly formatted with message
Expand All @@ -22,6 +24,7 @@ export async function sendMessage<T extends BaseMessage | LoginStatusResponse>(
// Handle case where response is undefined/null
reject(new Error("No response received"));
} else {
debug("Received response:", response);
resolve(response);
}
});
Expand Down
7 changes: 5 additions & 2 deletions src/common/types/messaging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export type SettingsObject = {
export class BaseMessage {
type!: string;

static is(message: any) {}
static checkResponse(response: any) {}
static is(message: any) { }
static checkResponse(response: any) { }
}

export class GetLoginStatusMessage implements BaseMessage {
Expand All @@ -35,6 +35,9 @@ export class GetLoginStatusMessage implements BaseMessage {
return message.type === MESSAGE_TYPE.GET_LOGIN_STATUS;
}

/**
* To check if the message has an expected token in response.
*/
static checkResponse(response: any): response is LoginStatusResponse {
return response && response.status && response.token;
}
Expand Down
36 changes: 24 additions & 12 deletions src/plugins/modular-rest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
import { ref } from "vue";
import { useProfileStore } from "../stores/profile";
import { analytic } from "./mixpanel";
import { debug, error, log } from "../common/helper/log";

GlobalOptions.set({
host: process.env.SUBTURTLE_API_URL || "",
Expand All @@ -61,6 +62,7 @@ export {
} from "@modular-rest/client";

export const isLogin = ref(false);

function updateIsLogin() {
const loginInfo =
authentication.isLogin && authentication.user?.type.toLowerCase() == "user";
Expand All @@ -79,8 +81,8 @@ function updateIsLogin() {

return true;
})
.catch((error) => {
console.error("Error bootstrapping profile store:", error);
.catch((errorDetail) => {
error("Error bootstrapping profile store:", errorDetail);
return false;
});
}
Expand Down Expand Up @@ -111,11 +113,20 @@ export async function loginWithLastSession() {

const user = await authentication.loginWithToken(
res.token as string,
true
// token will be stored on background service, so we don't need to store it here
false
);
return user;

debug("retrieved user from last login: ", user);

if (user.type == "anonymous") {
authentication.logout();
return false;
} else {
return updateIsLogin();
}

})
.then((_user) => updateIsLogin())
.then(async (_isRegisteredUser) => {
// updateIsLogin's truthy result means "registered user with a real
// account". Anonymous users return false here even though they hold a
Expand All @@ -126,19 +137,20 @@ export async function loginWithLastSession() {
// session, clearing chrome.storage.sync and broadcasting null to every
// tab β€” and the next translate from any content script then 412s
// because its Authorization header is empty.
if (!authentication.isLogin) {
if (!_isRegisteredUser && !authentication.isLogin) {
await logout();
return false;
}
return true;
})

.finally(() => {
if (!authentication.isLogin) {
debug("Login with last session failed, trying anonymous login");

authentication
.loginAsAnonymous()
.then(async () => {
console.log(
debug(
"Subturtle Anonymous login succeded",
authentication.isLogin
);
Expand All @@ -153,14 +165,14 @@ export async function loginWithLastSession() {
try {
await sendMessage(new StoreUserTokenMessage(token));
} catch (err) {
console.warn(
error(
"Subturtle: persisting anonymous token to background failed",
err
);
}
if (typeof localStorage !== "undefined") {
localStorage.setItem("token", token);
}
// if (typeof localStorage !== "undefined") {
// localStorage.setItem("token", token);
// }
}
updateIsLogin();
})
Expand Down
10 changes: 6 additions & 4 deletions src/trusted-types-polyfill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
* when the extension needs to run on a host that blocks Vue's policy.
*/

import { debug } from "./common/helper/log";

const trustedTypes = (window as any).trustedTypes;

if (trustedTypes && trustedTypes.createPolicy) {
console.log("[Subturtle] Setting up Trusted Types workaround");
debug("[Subturtle] Setting up Trusted Types workaround");

const originalCreatePolicy = trustedTypes.createPolicy.bind(trustedTypes);

Expand All @@ -32,7 +34,7 @@ if (trustedTypes && trustedTypes.createPolicy) {

trustedTypes.createPolicy = function (name: string, rules: any) {
if (name === "vue" || name.startsWith("vue-")) {
console.log(
debug(
`[Subturtle] Intercepted policy creation for "${name}", returning passthrough`
);
return createPassthroughPolicy();
Expand All @@ -46,7 +48,7 @@ if (trustedTypes && trustedTypes.createPolicy) {
}
};

console.log("[Subturtle] Trusted Types workaround installed");
debug("[Subturtle] Trusted Types workaround installed");
}

export {};
export { };
7 changes: 4 additions & 3 deletions static/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "Subturtle",
"description": "Turn video subtitles into English lessons. Learn new vocabulary in context as you watch on YouTube and Netflix.",
"version": "1.11.1",
"version": "1.11.1.1",
"manifest_version": 3,
"icons": {
"128": "/assets/logo-128.png",
Expand Down Expand Up @@ -79,5 +79,6 @@
"<all_urls>"
]
}
]
}
],
"version_name": "1.11.1-dev.1"
}
Loading
Loading