From d6a6125ce863f19283190e5c3e3dffa76260b02b Mon Sep 17 00:00:00 2001 From: michaeljwalla Date: Fri, 14 Aug 2026 23:28:20 -0500 Subject: [PATCH 1/5] copied from test repo w 'fake' droid signatures --- .../maroonrides/LegacyPrefsPlugin.java | 65 ++++++++++ .../maroonrides/maroonrides/MainActivity.java | 25 ++-- src/lib/components/ui/map/Map.svelte | 12 +- src/lib/managers/frontpage.manager.svelte.ts | 8 +- src/lib/utils/prefs.ts | 120 +++++++++++++----- src/routes/+layout.svelte | 4 +- 6 files changed, 185 insertions(+), 49 deletions(-) create mode 100644 android/app/src/main/java/com/maroonrides/maroonrides/LegacyPrefsPlugin.java diff --git a/android/app/src/main/java/com/maroonrides/maroonrides/LegacyPrefsPlugin.java b/android/app/src/main/java/com/maroonrides/maroonrides/LegacyPrefsPlugin.java new file mode 100644 index 0000000..060418d --- /dev/null +++ b/android/app/src/main/java/com/maroonrides/maroonrides/LegacyPrefsPlugin.java @@ -0,0 +1,65 @@ +package com.maroonrides.maroonrides; + +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import com.getcapacitor.JSObject; +import com.getcapacitor.Plugin; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginMethod; +import com.getcapacitor.annotation.CapacitorPlugin; +import java.io.File; + +// for moving react natives sqlite db to capacitor layer +@CapacitorPlugin(name = "LegacyPrefs") +public class LegacyPrefsPlugin extends Plugin { + + private static final String DATABASE_NAME = "RKStorage"; + private static final String TABLE_NAME = "catalystLocalStorage"; + private static final String KEY_COLUMN = "key"; + private static final String VALUE_COLUMN = "value"; + + @PluginMethod + public void getLegacyPrefs(PluginCall call) { + JSObject entries = new JSObject(); + JSObject result = new JSObject(); + result.put("available", false); + + File database = getContext().getDatabasePath(DATABASE_NAME); + if (!database.exists()) { + result.put("entries", entries); + call.resolve(result); + return; + } + + SQLiteDatabase db = null; + Cursor cursor = null; + + try { + db = SQLiteDatabase.openDatabase(database.getPath(), null, SQLiteDatabase.OPEN_READONLY); + cursor = db.query(TABLE_NAME, new String[] { KEY_COLUMN, VALUE_COLUMN }, null, null, null, null, null); + + while (cursor.moveToNext()) { + String key = cursor.getString(0); + String value = cursor.getString(1); + if (key != null && value != null) { + entries.put(key, value); + } + } + + result.put("available", true); + } catch (Exception e) { + // oh well resave your prefs + result.put("available", false); + } finally { + if (cursor != null) { + cursor.close(); + } + if (db != null) { + db.close(); + } + } + + result.put("entries", entries); + call.resolve(result); + } +} diff --git a/android/app/src/main/java/com/maroonrides/maroonrides/MainActivity.java b/android/app/src/main/java/com/maroonrides/maroonrides/MainActivity.java index eee00c6..8523b6b 100644 --- a/android/app/src/main/java/com/maroonrides/maroonrides/MainActivity.java +++ b/android/app/src/main/java/com/maroonrides/maroonrides/MainActivity.java @@ -14,25 +14,34 @@ public class MainActivity extends BridgeActivity { @Override public void onCreate(Bundle savedInstanceState) { + // register before super.onCreate() helped load properly + registerPlugin(LegacyPrefsPlugin.class); + super.onCreate(savedInstanceState); - + // DANGER: Forces the native Java layer to accept expired/invalid certs bypassNativeSSLChecks(); } private void bypassNativeSSLChecks() { try { - TrustManager[] trustAllCerts = new TrustManager[]{ - new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } - public void checkClientTrusted(X509Certificate[] certs, String authType) {} - public void checkServerTrusted(X509Certificate[] certs, String authType) {} - } + TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + } }; SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new SecureRandom()); - + // Apply trust-all logic globally to native requests HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { diff --git a/src/lib/components/ui/map/Map.svelte b/src/lib/components/ui/map/Map.svelte index d72af3a..43d9360 100644 --- a/src/lib/components/ui/map/Map.svelte +++ b/src/lib/components/ui/map/Map.svelte @@ -43,7 +43,8 @@ let isMounted = $state(false); let isLoaded = $state(false); let isStyleLoaded = $state(false); - let initialStyleApplied = false; + let initialStyleApplied = $state(false); + let appliedStyle: MapStyleOption | null = null; let styleTimeoutId: ReturnType | null = null; const mapStyles = $derived({ @@ -91,9 +92,11 @@ bounds = new MapLibreGL.LngLatBounds([h.minLon, h.minLat], [h.maxLon, h.maxLat]); + appliedStyle = currentStyle; + const mapInstance = new MapLibreGL.Map({ container: mapContainer, - style: currentStyle, + style: appliedStyle, renderWorldCopies: false, // TODO move attribution elsewhere attributionControl: false, @@ -137,12 +140,15 @@ $effect(() => { const style = currentStyle; - if (!map || !initialStyleApplied) { + // making this a state \/ bc prefs loaded like half a sec later + // where first load on update desyncs color from prefs + if (!map || !initialStyleApplied || style === appliedStyle) { return; } untrack(() => { isStyleLoaded = false; + appliedStyle = style; // Diff mode helps reuse existing layers for better performance map!.setStyle(style, { diff: false }); // Changed to false - full style reload is more reliable for theme changes }); diff --git a/src/lib/managers/frontpage.manager.svelte.ts b/src/lib/managers/frontpage.manager.svelte.ts index d320ff4..4bbe66a 100644 --- a/src/lib/managers/frontpage.manager.svelte.ts +++ b/src/lib/managers/frontpage.manager.svelte.ts @@ -5,8 +5,12 @@ class FrontPageManager { selectedTab = $state('all'); constructor() { - this.loadDefaultGroup(); //only set once here since it only applies on-open - this.loadFavorites(); + this.load(); + } + + async load() { + await this.loadDefaultGroup(); //only set once here since it only applies on-open + await this.loadFavorites(); } async loadDefaultGroup() { diff --git a/src/lib/utils/prefs.ts b/src/lib/utils/prefs.ts index 4322363..336c0c5 100644 --- a/src/lib/utils/prefs.ts +++ b/src/lib/utils/prefs.ts @@ -1,45 +1,87 @@ -import { Capacitor } from '@capacitor/core'; +import { Capacitor, registerPlugin } from '@capacitor/core'; import { Directory, Filesystem } from '@capacitor/filesystem'; import { Preferences } from '@capacitor/preferences'; -import { setMode } from 'mode-watcher'; +import { modeStorageKey, setMode } from 'mode-watcher'; + +const PREFS_VERSION = 3; + +// for android react native move +interface LegacyPrefsPlugin { + getLegacyPrefs(): Promise<{ available: boolean; entries: Record }>; +} +const LegacyPrefs = registerPlugin('LegacyPrefs'); + +async function readLegacyPrefs(): Promise | null> { + const platform = Capacitor.getPlatform(); + + if (platform === 'ios') { + try { + const iosRCTPrefs = await Filesystem.readFile({ + directory: Directory.Library, + path: 'Application Support/com.bwees.reveille-rides/RCTAsyncLocalStorage_V1/manifest.json', + }); + + return JSON.parse(atob(iosRCTPrefs.data as string)); + } catch (e) { + console.log('Error migrating iOS RCTAsyncLocalStorage_V1 manifest:', e); + return null; + } + } + + if (platform === 'android') { + try { + const { available, entries } = await LegacyPrefs.getLegacyPrefs(); + return available ? entries : null; + } catch (e) { + console.log('Error migrating Android RKStorage database:', e); + return null; + } + } + + return null; +} + +async function importLegacyPrefs( + legacy: Record, + { fillOnly = false }: { fillOnly?: boolean } = {}, +) { + const favorites = legacy['favorites']; + if (favorites) { + const current = (await Preferences.get({ key: 'favorites' })).value; + const currentIsEmpty = !current || current === '[]'; + + if (!fillOnly || currentIsEmpty) { + await Preferences.set({ key: 'favorites', value: favorites }); + } + } + + // rn stored the default group as an index + const defaultGroup = legacy['default-group'] === '1' ? 'favorites' : 'all'; + const currentGroup = (await Preferences.get({ key: 'defaultGroup' })).value; + + if (!fillOnly || !currentGroup || currentGroup === 'all') { + await Preferences.set({ key: 'defaultGroup', value: defaultGroup }); + } + + const userPickedMode = localStorage.getItem(modeStorageKey.current) !== null; + if (!fillOnly || !userPickedMode) { + let mode = 'system'; + if (legacy['app-theme'] === '1') { + mode = 'light'; + } else if (legacy['app-theme'] === '2') { + mode = 'dark'; + } + setMode(mode as 'system' | 'light' | 'dark'); + } +} export async function migratePrefs() { const currentVersion = Number((await Preferences.get({ key: 'version' })).value ?? 0); + if (currentVersion >= PREFS_VERSION) return; // explicit flow if (currentVersion < 1) { - // initial iOS migration - if (Capacitor.getPlatform() === 'ios') { - try { - const iosRCTPrefs = await Filesystem.readFile({ - directory: Directory.Library, - path: 'Application Support/com.bwees.reveille-rides/RCTAsyncLocalStorage_V1/manifest.json', - }); - - const manifest = JSON.parse(atob(iosRCTPrefs.data as string)); - - await Preferences.set({ - key: 'favorites', - value: manifest['favorites'] ?? '[]', - }); - - await Preferences.set({ - key: 'defaultGroup', - value: manifest['default-group'] ?? '0', - }); - - // Theme watcher handles its own saving of theme preference - // just migrate the old app preference over. - let mode = 'system'; - if (manifest['app-theme'] === '1') { - mode = 'light'; - } else if (manifest['app-theme'] === '2') { - mode = 'dark'; - } - setMode(mode as 'system' | 'light' | 'dark'); - } catch (e) { - console.log('Error migrating iOS RCTAsyncLocalStorage_V1 manifest:', e); - } - } + const legacy = await readLegacyPrefs(); + if (legacy) await importLegacyPrefs(legacy); console.log('Preferences migrated to version 1'); await Preferences.set({ key: 'version', value: '1' }); @@ -57,6 +99,14 @@ export async function migratePrefs() { console.log('Preferences migrated to version 2'); await Preferences.set({ key: 'version', value: '2' }); } + + if (currentVersion < 3) { + const legacy = await readLegacyPrefs(); + if (legacy) await importLegacyPrefs(legacy, { fillOnly: true }); + + console.log('Preferences migrated to version 3'); + await Preferences.set({ key: 'version', value: '3' }); + } } export async function toggleFavorite(routeCode: string) { diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 672a071..e41c170 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -5,6 +5,7 @@ import ThemeWatcher from '$lib/components/ThemeWatcher.svelte'; import Map from '$lib/components/ui/map/Map.svelte'; import MapControls from '$lib/components/ui/map/MapControls.svelte'; + import { frontPageManager } from '$lib/managers/frontpage.manager.svelte'; import { mapManager } from '$lib/managers/map.manager.svelte'; import { installInterceptor } from '$lib/utils/interceptor'; import { migratePrefs } from '$lib/utils/prefs'; @@ -21,7 +22,8 @@ }); onMount(async () => { - migratePrefs(); + await migratePrefs(); //sync to reduce some ui flickering when states change + await frontPageManager.load(); }); installInterceptor(); From cb3d6e32a01e02121ec7d68c6a9b7f82d4b6fdb0 Mon Sep 17 00:00:00 2001 From: michaeljwalla Date: Fri, 14 Aug 2026 23:28:55 -0500 Subject: [PATCH 2/5] incl. some syncing things while testing From 4372611635e4b21aa637364ebc5265ecf1641f95 Mon Sep 17 00:00:00 2001 From: michaeljwalla Date: Sat, 15 Aug 2026 00:19:14 -0500 Subject: [PATCH 3/5] move migration functions to own file, exporting to keep migratePrefs where it was --- src/lib/utils/legacy-migration.ts | 76 ++++++++++++++++++++++++++++ src/lib/utils/prefs.ts | 84 +------------------------------ 2 files changed, 77 insertions(+), 83 deletions(-) create mode 100644 src/lib/utils/legacy-migration.ts diff --git a/src/lib/utils/legacy-migration.ts b/src/lib/utils/legacy-migration.ts new file mode 100644 index 0000000..fdac21f --- /dev/null +++ b/src/lib/utils/legacy-migration.ts @@ -0,0 +1,76 @@ +import { modeStorageKey, setMode } from 'mode-watcher'; +import { Capacitor, registerPlugin } from '@capacitor/core'; +import { Directory, Filesystem } from '@capacitor/filesystem'; +import { Preferences } from '@capacitor/preferences'; + +export const PREFS_VERSION = 2; + +// for android react native move +interface LegacyPrefsPlugin { + getLegacyPrefs(): Promise<{ available: boolean; entries: Record }>; +} +const LegacyPrefs = registerPlugin('LegacyPrefs'); + +export async function readLegacyPrefs(): Promise | null> { + const platform = Capacitor.getPlatform(); + + if (platform === 'ios') { + try { + const iosRCTPrefs = await Filesystem.readFile({ + directory: Directory.Library, + path: 'Application Support/com.bwees.reveille-rides/RCTAsyncLocalStorage_V1/manifest.json', + }); + + return JSON.parse(atob(iosRCTPrefs.data as string)); + } catch (e) { + console.log('Error migrating iOS RCTAsyncLocalStorage_V1 manifest:', e); + return null; + } + } + + if (platform === 'android') { + try { + const { available, entries } = await LegacyPrefs.getLegacyPrefs(); + return available ? entries : null; + } catch (e) { + console.log('Error migrating Android RKStorage database:', e); + return null; + } + } + + return null; +} + +export async function importLegacyPrefs( + legacy: Record, + { fillOnly = false }: { fillOnly?: boolean } = {}, +) { + const favorites = legacy['favorites']; + if (favorites) { + const current = (await Preferences.get({ key: 'favorites' })).value; + const currentIsEmpty = !current || current === '[]'; + + if (!fillOnly || currentIsEmpty) { + await Preferences.set({ key: 'favorites', value: favorites }); + } + } + + // rn stored the default group as an index + const defaultGroup = legacy['default-group'] === '1' ? 'favorites' : 'all'; + const currentGroup = (await Preferences.get({ key: 'defaultGroup' })).value; + + if (!fillOnly || !currentGroup || currentGroup === 'all') { + await Preferences.set({ key: 'defaultGroup', value: defaultGroup }); + } + + const userPickedMode = localStorage.getItem(modeStorageKey.current) !== null; + if (!fillOnly || !userPickedMode) { + let mode = 'system'; + if (legacy['app-theme'] === '1') { + mode = 'light'; + } else if (legacy['app-theme'] === '2') { + mode = 'dark'; + } + setMode(mode as 'system' | 'light' | 'dark'); + } +} diff --git a/src/lib/utils/prefs.ts b/src/lib/utils/prefs.ts index 336c0c5..9fd2ea1 100644 --- a/src/lib/utils/prefs.ts +++ b/src/lib/utils/prefs.ts @@ -1,79 +1,5 @@ -import { Capacitor, registerPlugin } from '@capacitor/core'; -import { Directory, Filesystem } from '@capacitor/filesystem'; import { Preferences } from '@capacitor/preferences'; -import { modeStorageKey, setMode } from 'mode-watcher'; - -const PREFS_VERSION = 3; - -// for android react native move -interface LegacyPrefsPlugin { - getLegacyPrefs(): Promise<{ available: boolean; entries: Record }>; -} -const LegacyPrefs = registerPlugin('LegacyPrefs'); - -async function readLegacyPrefs(): Promise | null> { - const platform = Capacitor.getPlatform(); - - if (platform === 'ios') { - try { - const iosRCTPrefs = await Filesystem.readFile({ - directory: Directory.Library, - path: 'Application Support/com.bwees.reveille-rides/RCTAsyncLocalStorage_V1/manifest.json', - }); - - return JSON.parse(atob(iosRCTPrefs.data as string)); - } catch (e) { - console.log('Error migrating iOS RCTAsyncLocalStorage_V1 manifest:', e); - return null; - } - } - - if (platform === 'android') { - try { - const { available, entries } = await LegacyPrefs.getLegacyPrefs(); - return available ? entries : null; - } catch (e) { - console.log('Error migrating Android RKStorage database:', e); - return null; - } - } - - return null; -} - -async function importLegacyPrefs( - legacy: Record, - { fillOnly = false }: { fillOnly?: boolean } = {}, -) { - const favorites = legacy['favorites']; - if (favorites) { - const current = (await Preferences.get({ key: 'favorites' })).value; - const currentIsEmpty = !current || current === '[]'; - - if (!fillOnly || currentIsEmpty) { - await Preferences.set({ key: 'favorites', value: favorites }); - } - } - - // rn stored the default group as an index - const defaultGroup = legacy['default-group'] === '1' ? 'favorites' : 'all'; - const currentGroup = (await Preferences.get({ key: 'defaultGroup' })).value; - - if (!fillOnly || !currentGroup || currentGroup === 'all') { - await Preferences.set({ key: 'defaultGroup', value: defaultGroup }); - } - - const userPickedMode = localStorage.getItem(modeStorageKey.current) !== null; - if (!fillOnly || !userPickedMode) { - let mode = 'system'; - if (legacy['app-theme'] === '1') { - mode = 'light'; - } else if (legacy['app-theme'] === '2') { - mode = 'dark'; - } - setMode(mode as 'system' | 'light' | 'dark'); - } -} +import { PREFS_VERSION, readLegacyPrefs, importLegacyPrefs } from './legacy-migration'; export async function migratePrefs() { const currentVersion = Number((await Preferences.get({ key: 'version' })).value ?? 0); @@ -99,14 +25,6 @@ export async function migratePrefs() { console.log('Preferences migrated to version 2'); await Preferences.set({ key: 'version', value: '2' }); } - - if (currentVersion < 3) { - const legacy = await readLegacyPrefs(); - if (legacy) await importLegacyPrefs(legacy, { fillOnly: true }); - - console.log('Preferences migrated to version 3'); - await Preferences.set({ key: 'version', value: '3' }); - } } export async function toggleFavorite(routeCode: string) { From 3be599026a2ae8d1002adb9fb3e1eef85fef8e9f Mon Sep 17 00:00:00 2001 From: michaeljwalla Date: Sat, 15 Aug 2026 09:46:21 -0500 Subject: [PATCH 4/5] ... --- src/lib/utils/legacy-migration.ts | 40 +------------------------------ src/lib/utils/prefs.ts | 38 ++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/lib/utils/legacy-migration.ts b/src/lib/utils/legacy-migration.ts index fdac21f..f2bb324 100644 --- a/src/lib/utils/legacy-migration.ts +++ b/src/lib/utils/legacy-migration.ts @@ -1,9 +1,5 @@ -import { modeStorageKey, setMode } from 'mode-watcher'; import { Capacitor, registerPlugin } from '@capacitor/core'; import { Directory, Filesystem } from '@capacitor/filesystem'; -import { Preferences } from '@capacitor/preferences'; - -export const PREFS_VERSION = 2; // for android react native move interface LegacyPrefsPlugin { @@ -39,38 +35,4 @@ export async function readLegacyPrefs(): Promise | null> } return null; -} - -export async function importLegacyPrefs( - legacy: Record, - { fillOnly = false }: { fillOnly?: boolean } = {}, -) { - const favorites = legacy['favorites']; - if (favorites) { - const current = (await Preferences.get({ key: 'favorites' })).value; - const currentIsEmpty = !current || current === '[]'; - - if (!fillOnly || currentIsEmpty) { - await Preferences.set({ key: 'favorites', value: favorites }); - } - } - - // rn stored the default group as an index - const defaultGroup = legacy['default-group'] === '1' ? 'favorites' : 'all'; - const currentGroup = (await Preferences.get({ key: 'defaultGroup' })).value; - - if (!fillOnly || !currentGroup || currentGroup === 'all') { - await Preferences.set({ key: 'defaultGroup', value: defaultGroup }); - } - - const userPickedMode = localStorage.getItem(modeStorageKey.current) !== null; - if (!fillOnly || !userPickedMode) { - let mode = 'system'; - if (legacy['app-theme'] === '1') { - mode = 'light'; - } else if (legacy['app-theme'] === '2') { - mode = 'dark'; - } - setMode(mode as 'system' | 'light' | 'dark'); - } -} +} \ No newline at end of file diff --git a/src/lib/utils/prefs.ts b/src/lib/utils/prefs.ts index 9fd2ea1..de2d572 100644 --- a/src/lib/utils/prefs.ts +++ b/src/lib/utils/prefs.ts @@ -1,5 +1,41 @@ import { Preferences } from '@capacitor/preferences'; -import { PREFS_VERSION, readLegacyPrefs, importLegacyPrefs } from './legacy-migration'; +import { readLegacyPrefs } from './legacy-migration'; +import { modeStorageKey, setMode } from 'mode-watcher'; + +export const PREFS_VERSION = 2; +export async function importLegacyPrefs( + legacy: Record, + { fillOnly = false }: { fillOnly?: boolean } = {}, +) { + const favorites = legacy['favorites']; + if (favorites) { + const current = (await Preferences.get({ key: 'favorites' })).value; + const currentIsEmpty = !current || current === '[]'; + + if (!fillOnly || currentIsEmpty) { + await Preferences.set({ key: 'favorites', value: favorites }); + } + } + + // rn stored the default group as an index + const defaultGroup = legacy['default-group'] === '1' ? 'favorites' : 'all'; + const currentGroup = (await Preferences.get({ key: 'defaultGroup' })).value; + + if (!fillOnly || !currentGroup || currentGroup === 'all') { + await Preferences.set({ key: 'defaultGroup', value: defaultGroup }); + } + + const userPickedMode = localStorage.getItem(modeStorageKey.current) !== null; + if (!fillOnly || !userPickedMode) { + let mode = 'system'; + if (legacy['app-theme'] === '1') { + mode = 'light'; + } else if (legacy['app-theme'] === '2') { + mode = 'dark'; + } + setMode(mode as 'system' | 'light' | 'dark'); + } +} export async function migratePrefs() { const currentVersion = Number((await Preferences.get({ key: 'version' })).value ?? 0); From 34fb099ddf1d81cf7e132d7e3281150962e3cb62 Mon Sep 17 00:00:00 2001 From: michaeljwalla Date: Sat, 15 Aug 2026 10:17:29 -0500 Subject: [PATCH 5/5] style --- src/lib/utils/legacy-migration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/utils/legacy-migration.ts b/src/lib/utils/legacy-migration.ts index f2bb324..e31d1ec 100644 --- a/src/lib/utils/legacy-migration.ts +++ b/src/lib/utils/legacy-migration.ts @@ -35,4 +35,4 @@ export async function readLegacyPrefs(): Promise | null> } return null; -} \ No newline at end of file +}