Nowa zakladka w dolnym pasku: react-native-pager-view (pion, natywny snap), dwie instancje createVideoPlayer (aktywna + nastepna w buforze), komorka z posterem, podpisem tworcy i tagami, kolumna przyciskow, pasek postepu. Gesty bez reanimated: tap = dzwiek, double-tap = ulubione z wybuchem serca i haptyka, przytrzymanie = 2x, przesuniecie w lewo = arkusz "show less of this" (tworca / tagi / nisze / seen it, plus blokada tworcy). Onboarding 3 tapow z top tagow, AutoSwipe (off/5/10 s), licznik obejrzanych, nowe rozdanie. Telemetria: SwipeTelemetry zbiera zdarzenia i wysyla paczki co 5 s / 10 zdarzen / w tle; przy opuszczeniu strony impression z dwell, skip (<2 s) albo complete (petla lub >=80%). Serwer z tego uczy wagi i uklada 80% nastepnych stron. Pager to modul natywny, wiec import jest miekki (require w try/catch) i zakladka pokazuje sie tylko od runtime 1.4; ten sam bundle leci OTA na 1.2/1.3 z wpisem "pobierz 0.4.0" (onlyBelowRuntime), a wpis o Swipe ma nowe pole minRuntime. Bump: app.json 0.4.0 / runtime 1.4, versionCode 14, AndroidManifest 1.4, publish_update RUNTIME_DEFAULT 1.4, strona goon-foss.org pod 0.4.0. Sprawdzone na emulatorze (APK 0.4.0): zakladka widoczna, onboarding zapisany na serwerze, feed spersonalizowany 8/2, ExoPlayer dekoduje wideo i audio, swipe daje zdarzenia complete/impression, double-tap daje like + ulubione + wagi.
63 lines
2.7 KiB
TypeScript
63 lines
2.7 KiB
TypeScript
import Constants from 'expo-constants';
|
|
import * as Updates from 'expo-updates';
|
|
|
|
/**
|
|
* Aktualna wersja appki, używana do display'u (BugReportFAB, Update dialog)
|
|
* i porównań z `/version` z serwera.
|
|
*
|
|
* Źródła w kolejności preferencji:
|
|
* 1. `Constants.expoConfig?.version`, działa gdy OTA manifest ma `extra.expoClient`
|
|
* (publish_update.py embeduje tam app.json). W release APK przed pierwszym OTA
|
|
* bierze się to z app.json bundled w build.
|
|
* 2. Hardcoded fallback, chroni przed OTA-z-pustego-extra (bug 2026-05-12, gdzie
|
|
* stara wersja publish_update.py wysyłała `extra: {}` → expoConfig=null po
|
|
* reload-zie → version='0.0.0' → fałszywy "Update available" dialog).
|
|
*
|
|
* **Bump przy każdym APK release** żeby APK fallback pasował do realnej wersji.
|
|
* OTA-only releases (silent JS update) NIE bumpują tego, wtedy version w app.json
|
|
* też nie idzie do góry, więc consistency jest zachowana.
|
|
*/
|
|
export const APP_VERSION: string =
|
|
(Constants.expoConfig?.version as string | undefined) || '0.4.0';
|
|
|
|
/**
|
|
* Runtime zapisany w APK (`EXPO_RUNTIME_VERSION` w AndroidManifest). JEDYNY numer, po
|
|
* którym aplikacja pozna, jaką paczkę natywną ma naprawdę zainstalowaną.
|
|
*
|
|
* Po co osobno, skoro jest `APP_VERSION`: ta bierze się z `app.json`, a `app.json`
|
|
* przyjeżdża do aplikacji RAZEM Z PACZKĄ OTA. Po podbiciu wersji na 0.3.0 i wysłaniu tej
|
|
* paczki na stary kanał każdy użytkownik APK 0.2.2 zaczął raportować 0.3.0 i sam siebie
|
|
* uznał za aktualnego: nie dostał ani monitu o nowym APK, ani wpisu w changelogu, który
|
|
* właśnie do niego był kierowany. Runtime siedzi w natywnym manifeście i OTA go nie rusza.
|
|
*/
|
|
export const NATIVE_RUNTIME: string | null = (() => {
|
|
try {
|
|
return Updates.runtimeVersion ?? null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
})();
|
|
|
|
|
|
/** „0.3.0" → [0,3,0]. Człony nieliczbowe (np. „0.3.0-rc1") lecą na 0. */
|
|
function versionParts(v: string): number[] {
|
|
return String(v || '').split('.').map((x) => parseInt(x, 10) || 0);
|
|
}
|
|
|
|
/**
|
|
* Czy `candidate` jest NOWSZA niż `current`.
|
|
*
|
|
* Po co, skoro wcześniej wystarczyło `!==`: serwer trzyma numer najnowszego APK na
|
|
* sztywno, a przy wydaniu 0.3.0 nie został podbity od razu. Porównanie „różne" kazało
|
|
* wtedy KAŻDEMU użytkownikowi 0.3.0 zainstalować 0.2.2, czyli cofnąć się o wersję.
|
|
* Złapane na emulatorze przed publikacją.
|
|
*/
|
|
export function isNewerVersion(candidate: string, current: string): boolean {
|
|
const a = versionParts(candidate);
|
|
const b = versionParts(current);
|
|
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
const d = (a[i] ?? 0) - (b[i] ?? 0);
|
|
if (d !== 0) return d > 0;
|
|
}
|
|
return false;
|
|
}
|