improve tests, especially for navigation.
This commit is contained in:
@ -1,11 +1,21 @@
|
||||
import * as React from "react";
|
||||
import { NavigationContainer } from "@react-navigation/native";
|
||||
import TTNavStack from "@/components/TTNavStack";
|
||||
import * as ScreenOrientation from "expo-screen-orientation";
|
||||
import { NavigationContainer } from "@react-navigation/native";
|
||||
import { migrateDb } from "./lib/db";
|
||||
import { Text } from "react-native";
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<NavigationContainer>
|
||||
<TTNavStack />
|
||||
</NavigationContainer>
|
||||
);
|
||||
const [loaded, setLoaded] = React.useState<boolean>(true);
|
||||
React.useEffect(() => {
|
||||
(async function () {
|
||||
await migrateDb();
|
||||
await ScreenOrientation.unlockAsync();
|
||||
await ScreenOrientation.lockAsync(
|
||||
ScreenOrientation.OrientationLock.LANDSCAPE_LEFT
|
||||
);
|
||||
setLoaded(true);
|
||||
})();
|
||||
});
|
||||
return loaded ? <TTNavStack /> : <Text>Loading...</Text>;
|
||||
}
|
||||
|
@ -24,16 +24,23 @@ export type language_matrix = {
|
||||
[key:string] : language_matrix_entry
|
||||
}
|
||||
|
||||
export async function fetchWithTimeout(url : string, options : RequestInit, timeout = 5000) : Promise<Response> {
|
||||
return Promise.race([
|
||||
fetch(url, options),
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout))
|
||||
]);
|
||||
}
|
||||
|
||||
export class LanguageServer {
|
||||
constructor(public baseUrl : string) {}
|
||||
|
||||
async fetchLanguages() : Promise<language_matrix> {
|
||||
async fetchLanguages(timeout = 500) : Promise<language_matrix> {
|
||||
let data = {};
|
||||
const res = await fetch(this.baseUrl + "/languages", {
|
||||
const res = await fetchWithTimeout(this.baseUrl + "/languages", {
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
}, timeout);
|
||||
try {
|
||||
data = await res.json();
|
||||
} catch (e) {
|
||||
@ -55,16 +62,20 @@ export class LanguageServer {
|
||||
|
||||
static async getDefault() {
|
||||
const settings = await Settings.getDefault();
|
||||
return new LanguageServer(await settings.getLibretranslateBaseUrl());
|
||||
return new LanguageServer(await settings.getLibretranslateBaseUrl() || LIBRETRANSLATE_BASE_URL);
|
||||
}
|
||||
}
|
||||
|
||||
export class Translator {
|
||||
constructor(public source : language_t, public defaultTarget : string = "en", private languageServer : LanguageServer) {
|
||||
constructor(public source : language_t, public defaultTarget : string = "en", private _languageServer : LanguageServer) {
|
||||
}
|
||||
|
||||
get languageServer() {
|
||||
return this._languageServer;
|
||||
}
|
||||
|
||||
async translate(text : string, target : string|undefined = undefined) {
|
||||
const url = this.languageServer.baseUrl + `/translate`;
|
||||
const url = this._languageServer.baseUrl + `/translate`;
|
||||
const res = await fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
@ -103,4 +114,10 @@ export class CachedTranslator extends Translator {
|
||||
await cache.set(key2, tr2);
|
||||
return tr2;
|
||||
}
|
||||
|
||||
static async getDefault(defaultTarget: string | undefined = undefined) {
|
||||
const settings = await Settings.getDefault();
|
||||
const source = await settings.getHostLanguage();
|
||||
return new CachedTranslator(source, defaultTarget, await LanguageServer.getDefault())
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ export function chooseCountry(lang_a2 : string) {
|
||||
c => c.languages.includes(lang_a3.alpha3)
|
||||
);
|
||||
|
||||
console.log("cc = %x, ", cs.map(c => c.alpha2))
|
||||
// console.log("cc = %x, ", cs.map(c => c.alpha2))
|
||||
|
||||
return cs.filter(cc => Object.keys(LANG_FLAGS).includes(cc.alpha2.toLowerCase())).map(c => c.alpha2.toLowerCase());
|
||||
}
|
||||
|
@ -4,7 +4,9 @@ import _LANGUAGES from "@/assets/languages.min.json"
|
||||
export const LANG_FLAGS = _LANG_FLAGS
|
||||
|
||||
export function longLang(shortLang : string) {
|
||||
return ((LANG_FLAGS as any)[shortLang] as any)["name"] as string
|
||||
const obj = LANG_FLAGS[shortLang];
|
||||
if (!obj) return undefined;
|
||||
return obj["name"] as string;
|
||||
}
|
||||
|
||||
export function lang_a3_a2(a3 : string) {
|
||||
|
@ -43,6 +43,9 @@ export default function Home() {
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Pressable onPress={() => navigation.navigate("Settings")}>
|
||||
<Text>Settings</Text>
|
||||
</Pressable>
|
||||
<LanguageSelection onLangSelected={onLangSelected} />
|
||||
</View>
|
||||
);
|
||||
|
@ -1,12 +1,13 @@
|
||||
import {describe, expect, beforeEach} from '@jest/globals';
|
||||
import {Settings} from '@/app/lib/settings';
|
||||
import { getDb } from '@/app/lib/db';
|
||||
import { getDb, migrateDb } from '@/app/lib/db';
|
||||
|
||||
describe('Settings', () => {
|
||||
let settings: Settings;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Initialize your Settings class here with a fresh database instance
|
||||
await migrateDb();
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Could not get db");
|
||||
settings = new Settings(db);
|
||||
|
@ -31,11 +31,15 @@ export class Message {
|
||||
}
|
||||
}
|
||||
|
||||
type conversation_event_t = "add_message" | "translation_done"
|
||||
type conversation_callback_t = (conversation : Conversation) => any;
|
||||
|
||||
export class Conversation extends Array<Message> {
|
||||
|
||||
public onAddMessage? : (conversation : Conversation) => any;
|
||||
public onTranslationDone? : (conversation : Conversation) => any;
|
||||
|
||||
|
||||
constructor (
|
||||
public translator : Translator,
|
||||
public host : Speaker,
|
||||
@ -44,6 +48,15 @@ export class Conversation extends Array<Message> {
|
||||
super();
|
||||
}
|
||||
|
||||
public on(event : conversation_event_t, callback : conversation_callback_t) {
|
||||
if (event === "add_message") {
|
||||
this.onAddMessage = callback;
|
||||
}
|
||||
if (event === "translation_done") {
|
||||
this.onTranslationDone = callback;
|
||||
}
|
||||
}
|
||||
|
||||
public addMessage(speaker : Speaker, text? : string) {
|
||||
this.push(new Message(this, speaker, text));
|
||||
}
|
||||
|
@ -1,40 +1,26 @@
|
||||
import * as SQLite from 'expo-sqlite';
|
||||
import * as SQLite from "expo-sqlite";
|
||||
import { MIGRATE_UP, MIGRATE_DOWN } from "./migrations";
|
||||
|
||||
export const MIGRATE_UP = {
|
||||
1: [
|
||||
`CREATE TABLE IF NOT EXISTS settings (
|
||||
host_language TEXT,
|
||||
libretranslate_base_url TEXT,
|
||||
ui_direction INTEGER
|
||||
)`,
|
||||
],
|
||||
2: [
|
||||
`CREATE TABLE IF NOT EXISTS whisper_models (
|
||||
model TEXT PRIMARY KEY,
|
||||
bytes_done INTEGER,
|
||||
bytes_total INTEGER,
|
||||
)`,
|
||||
]
|
||||
export async function getDb() {
|
||||
return await SQLite.openDatabaseAsync("translation_terrace");
|
||||
}
|
||||
|
||||
export const MIGRATE_DOWN = {
|
||||
1: [
|
||||
`DROP TABLE IF EXISTS settings`
|
||||
],
|
||||
2: [
|
||||
`DROP TABLE IF EXISTS whisper_models`
|
||||
]
|
||||
}
|
||||
|
||||
export async function getDb(migrationDirection : "up" | "down" = "up") {
|
||||
const db = await SQLite.openDatabaseAsync('translation_terrace');
|
||||
export async function migrateDb(direction: "up" | "down" = "up") {
|
||||
|
||||
for (let [migration, statements] of Object.entries(MIGRATE_UP)) {
|
||||
for (let statement of statements) {
|
||||
console.log(statement)
|
||||
await db.runAsync(statement);
|
||||
}
|
||||
const db = await getDb();
|
||||
|
||||
const m = direction === "up" ? MIGRATE_UP : MIGRATE_DOWN;
|
||||
|
||||
for (let [migration, statements] of Object.entries(m)) {
|
||||
for (let statement of statements) {
|
||||
console.log(statement);
|
||||
try {
|
||||
const result = await db.runAsync(statement);
|
||||
console.log(result);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
}
|
23
app/lib/migrations.ts
Normal file
23
app/lib/migrations.ts
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
export const MIGRATE_UP = {
|
||||
1: [
|
||||
`CREATE TABLE IF NOT EXISTS settings (
|
||||
host_language TEXT,
|
||||
libretranslate_base_url TEXT,
|
||||
ui_direction INTEGER,
|
||||
whisper_model TEXT
|
||||
)`,
|
||||
],
|
||||
2: [
|
||||
`CREATE TABLE IF NOT EXISTS whisper_models (
|
||||
model TEXT PRIMARY KEY,
|
||||
bytes_done INTEGER,
|
||||
bytes_total INTEGER
|
||||
)`,
|
||||
],
|
||||
};
|
||||
|
||||
export const MIGRATE_DOWN = {
|
||||
1: [`DROP TABLE IF EXISTS settings`],
|
||||
2: [`DROP TABLE IF EXISTS whisper_models`],
|
||||
};
|
@ -8,7 +8,7 @@ export class Settings {
|
||||
"host_language",
|
||||
"libretranslate_base_url",
|
||||
'ui_direction',
|
||||
"wisper_model",
|
||||
"whisper_model",
|
||||
]
|
||||
|
||||
constructor(public db: SQLiteDatabase) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Platform } from "react-native";
|
||||
import FileSystem from "expo-file-system";
|
||||
import * as FileSystem from "expo-file-system";
|
||||
import { File, Paths } from 'expo-file-system/next';
|
||||
import { getDb } from "./db";
|
||||
|
||||
@ -145,10 +145,13 @@ export async function downloadWhisperModel(
|
||||
}
|
||||
) {
|
||||
|
||||
console.debug("Starting download of %s", whisper_model);
|
||||
|
||||
if (!WHISPER_MODEL_DIR.exists) {
|
||||
await FileSystem.makeDirectoryAsync(WHISPER_MODEL_PATH, {
|
||||
intermediates: true,
|
||||
})
|
||||
});
|
||||
console.debug("Created %s", WHISPER_MODEL_DIR);
|
||||
}
|
||||
|
||||
const whisperTarget = getWhisperTarget(whisper_model);
|
||||
@ -179,6 +182,7 @@ export async function downloadWhisperModel(
|
||||
data.totalBytesWritten,
|
||||
data.totalBytesExpectedToWrite,
|
||||
];
|
||||
console.log("%s, %s of %s", whisper_model, data.totalBytesWritten, data.totalBytesExpectedToWrite);
|
||||
await db.runAsync(
|
||||
`INSERT OR REPLACE INTO whisper_models (model, bytes_done, bytes_remaining) VALUES (?, ?, ?)`,
|
||||
args
|
||||
@ -187,4 +191,4 @@ export async function downloadWhisperModel(
|
||||
);
|
||||
|
||||
await resumable.downloadAsync();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user