Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
48 changes: 39 additions & 9 deletions app/containers/MessageComposer/hooks/useAutocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,17 +139,47 @@ export const useAutocomplete = ({
if (type === '/') {
const db = database.active;
const commandsCollection = db.get('slash_commands');
const appTranslationsCollection = db.get('app_translations');
const likeString = sanitizeLikeString(text);
const commands = await (
await commandsCollection.query(Q.where('id', Q.like(`${likeString}%`))).fetch()
).map(command => ({
id: command.id,
title: command.id,
subtitle: command.description,
type
}));
setItems(commands);

const rawCommands = await commandsCollection.query(Q.where('id', Q.like(`${likeString}%`))).fetch();

const commands = await Promise.all(
rawCommands.map(async command => {
let subtitle = '';
const { description } = command;

if (!description) {
// no description at all — leave empty
subtitle = '';
} else if (command.appId) {
const appLang = I18n.currentLocale().split('-')[0];

// app translation key — look up in WatermelonDB
const translationRecords = await appTranslationsCollection
.query(Q.where('key', description), Q.where('language', appLang))
.fetch();

if (translationRecords.length > 0) {
subtitle = (translationRecords[0] as any).value;
} else {
// not in DB yet — fallback to readable form
subtitle = description.split('.').pop()?.replace(/_/g, ' ') ?? description;
}
} else {
subtitle = description;
}

return {
id: command.id,
title: command.id,
subtitle,
type
};
})
);

setItems(commands);
if (commands.length > 0) {
updateAutocompleteVisible(true);
accessibilityFocusOnInput();
Expand Down
8 changes: 8 additions & 0 deletions app/definitions/rest/v1/appTranslations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type AppsTranslationsEndpoints = {
'apps.translations': {
GET: (params: { language?: string }) => {
language: string;
translations: { [key: string]: string };
};
};
};
4 changes: 3 additions & 1 deletion app/definitions/rest/v1/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { type PushEndpoints } from './push';
import { type DirectoryEndpoint } from './directory';
import { type AutoTranslateEndpoints } from './autotranslate';
import { type ModerationEndpoints } from './moderation';
import { type AppsTranslationsEndpoints } from './appTranslations';

export type Endpoints = ChannelsEndpoints &
ChatEndpoints &
Expand All @@ -44,4 +45,5 @@ export type Endpoints = ChannelsEndpoints &
PushEndpoints &
DirectoryEndpoint &
AutoTranslateEndpoints &
ModerationEndpoints;
ModerationEndpoints &
AppsTranslationsEndpoints;
4 changes: 3 additions & 1 deletion app/lib/database/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import appSchema from './schema/app';
import migrations from './model/migrations';
import serversMigrations from './model/servers/migrations';
import { type TAppDatabase, type TServerDatabase } from './interfaces';
import AppTranslation from './model/AppTranslation';

if (__DEV__) {
console.log(appGroupPath);
Expand Down Expand Up @@ -60,7 +61,8 @@ export const getDatabase = (database = ''): Database => {
Role,
Permission,
SlashCommand,
User
User,
AppTranslation
]
});
};
Expand Down
10 changes: 10 additions & 0 deletions app/lib/database/model/AppTranslation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Model } from '@nozbe/watermelondb';
import { field } from '@nozbe/watermelondb/decorators';

export default class AppTranslation extends Model {
static table = 'app_translations';

@field('key') key!: string;
@field('value') value!: string;
@field('language') language!: string;
}
13 changes: 13 additions & 0 deletions app/lib/database/model/migrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,19 @@ export default schemaMigrations({
]
})
]
},
{
toVersion: 29,
steps: [
createTable({
name: 'app_translations',
columns: [
{ name: 'key', type: 'string', isIndexed: true },
{ name: 'value', type: 'string' },
{ name: 'language', type: 'string', isIndexed: true }
]
})
]
}
]
});
10 changes: 9 additions & 1 deletion app/lib/database/schema/app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { appSchema, tableSchema } from '@nozbe/watermelondb';

export default appSchema({
version: 28,
version: 29,
tables: [
tableSchema({
name: 'subscriptions',
Expand Down Expand Up @@ -285,6 +285,14 @@ export default appSchema({
{ name: 'username', type: 'string', isIndexed: true },
{ name: 'avatar_etag', type: 'string', isOptional: true }
]
}),
tableSchema({
name: 'app_translations',
columns: [
{ name: 'key', type: 'string', isIndexed: true },
{ name: 'value', type: 'string' },
{ name: 'language', type: 'string', isIndexed: true }
]
})
]
});
55 changes: 55 additions & 0 deletions app/lib/methods/getAppTranslations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { Q } from '@nozbe/watermelondb';

import database from '../database';
import log from './helpers/log';
import protectedFunction from './helpers/protectedFunction';
import sdk from '../services/sdk';

async function fetchAndSaveTranslations(language: string, db: any): Promise<void> {
const result = await sdk.get('apps.translations', { language });

if (!result?.success || !result.translations) {
return;
}

await db.write(async () => {
const collection = db.get('app_translations');

const existing = await collection.query(Q.where('language', result.language)).fetch();
const toDelete = existing.map((r: any) => r.prepareDestroyPermanently());

const toCreate = Object.entries(result.translations).map(([key, value]) =>
collection.prepareCreate(
protectedFunction((r: any) => {
r._raw = sanitizedRaw({ id: `${result.language}_${key}` }, collection.schema);
r.key = key;
r.value = value as string;
r.language = result.language;
})
)
);

await db.batch(...toDelete, ...toCreate);
});
}

export async function getAppTranslations(language = 'en'): Promise<void> {
try {
const db = database.active;
const collection = db.get('app_translations');

// check if translations already exist in DB for this language
const existing = await collection.query(Q.where('language', language)).fetchCount();

if (existing > 0) {
// already have translations — skip fetch
return;
}

// nothing in DB — fetch from server and save
await fetchAndSaveTranslations(language, db);
} catch (e) {
log(e);
}
}
7 changes: 7 additions & 0 deletions app/lib/methods/getSlashCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import log from './helpers/log';
import protectedFunction from './helpers/protectedFunction';
import { type ISlashCommandResult, type TSlashCommandModel } from '../../definitions';
import sdk from '../services/sdk';
import { getAppTranslations } from './getAppTranslations';
import I18n from '../../i18n';

export function getSlashCommands() {
const db = database.active;
Expand All @@ -17,6 +19,11 @@ export function getSlashCommands() {
if (!result.success) {
return resolve();
}

// fetch and save app translations if not already in DB
const appLang = I18n.currentLocale();
await getAppTranslations(appLang);

// @ts-ignore
const { commands } = result;
if (commands && commands.length) {
Expand Down
4 changes: 4 additions & 0 deletions app/views/LanguageView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { type SettingsStackParamList } from '../../stacks/types';
import { showErrorAlert } from '../../lib/methods/helpers/info';
import log, { events, logEvent } from '../../lib/methods/helpers/log';
import { saveUserPreferences } from '../../lib/services/restApi';
import { getAppTranslations } from '../../lib/methods/getAppTranslations';

const LanguageView = () => {
const { languageDefault, id } = useAppSelector(state => ({
Expand Down Expand Up @@ -81,6 +82,9 @@ const LanguageView = () => {
logEvent(events.LANG_SET_LANGUAGE_F);
}
});

const appLang = (params.language || 'en').split('-')[0];
await getAppTranslations(appLang);
} catch (e) {
logEvent(events.LANG_SET_LANGUAGE_F);
showErrorAlert(I18n.t('There_was_an_error_while_action', { action: I18n.t('saving_preferences') }));
Expand Down