mirror of
https://github.com/colanode/colanode.git
synced 2025-12-25 16:09:31 +01:00
Improve assets loading
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -155,8 +155,6 @@ src/scripts/icons/temp/
|
||||
# Ignore desktop assets
|
||||
apps/desktop/assets/emojis.db
|
||||
apps/desktop/assets/icons.db
|
||||
apps/desktop/assets/emojis.svg
|
||||
apps/desktop/assets/icons.svg
|
||||
apps/desktop/assets/fonts/neotrax.otf
|
||||
apps/desktop/assets/colanode-logo-black.png
|
||||
apps/desktop/assets/colanode-logo-black.ico
|
||||
@@ -173,6 +171,9 @@ apps/web/public/assets/colanode-logo-black-512.png
|
||||
|
||||
# Ignore mobile assets
|
||||
apps/mobile/assets/ui/index.html
|
||||
apps/mobile/assets/emojis.db
|
||||
apps/mobile/assets/icons.db
|
||||
apps/mobile/assets/fonts/neotrax.otf
|
||||
|
||||
.expo
|
||||
web-build/
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
globalShortcut,
|
||||
dialog,
|
||||
} from 'electron';
|
||||
import path from 'path';
|
||||
|
||||
import started from 'electron-squirrel-startup';
|
||||
import { updateElectronApp, UpdateSourceType } from 'update-electron-app';
|
||||
@@ -56,9 +57,9 @@ const createWindow = async () => {
|
||||
fullscreenable: true,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
icon: app.path.join(app.path.assets, 'colanode-logo-black.png'),
|
||||
icon: path.join(app.path.assets, 'colanode-logo-black.png'),
|
||||
webPreferences: {
|
||||
preload: app.path.join(__dirname, 'preload.js'),
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
},
|
||||
autoHideMenuBar: true,
|
||||
titleBarStyle: 'hiddenInset',
|
||||
@@ -104,10 +105,7 @@ const createWindow = async () => {
|
||||
// mainWindow.webContents.openDevTools();
|
||||
} else {
|
||||
mainWindow.loadFile(
|
||||
app.path.join(
|
||||
__dirname,
|
||||
`../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`
|
||||
)
|
||||
path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -154,4 +154,8 @@ export class DesktopPathService implements PathService {
|
||||
public get iconsDatabase(): string {
|
||||
return this.nativePath.join(this.getAssetsSourcePath(), 'icons.db');
|
||||
}
|
||||
|
||||
public font(name: string): string {
|
||||
return this.nativePath.join(this.getAssetsSourcePath(), 'fonts', name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,6 @@
|
||||
"web": {
|
||||
"favicon": "./assets/favicon.png"
|
||||
},
|
||||
"plugins": [
|
||||
"expo-asset",
|
||||
"expo-sqlite"
|
||||
]
|
||||
"plugins": ["expo-asset", "expo-sqlite"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
import { Asset } from 'expo-asset';
|
||||
import { modelName } from 'expo-device';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { View, ActivityIndicator, Platform } from 'react-native';
|
||||
import { WebView, WebViewMessageEvent } from 'react-native-webview';
|
||||
|
||||
import { eventBus } from '@colanode/client/lib';
|
||||
import { AppMeta, AppService } from '@colanode/client/services';
|
||||
import { generateId, IdType } from '@colanode/core';
|
||||
import { copyAssets, indexHtmlAsset } from '@colanode/mobile/lib/assets';
|
||||
import { Message } from '@colanode/mobile/lib/types';
|
||||
import { app } from '@colanode/mobile/services/app-service';
|
||||
|
||||
import indexHtml from '../assets/ui/index.html';
|
||||
import { MobileFileSystem } from '@colanode/mobile/services/file-system';
|
||||
import { MobileKyselyService } from '@colanode/mobile/services/kysely-service';
|
||||
import { MobilePathService } from '@colanode/mobile/services/path-service';
|
||||
|
||||
export const App = () => {
|
||||
const windowId = useRef<string>(generateId(IdType.Window));
|
||||
const webViewRef = useRef<WebView>(null);
|
||||
const app = useRef<AppService | null>(null);
|
||||
const appInitialized = useRef<boolean>(false);
|
||||
|
||||
const [uri, setUri] = useState<string | null>(null);
|
||||
const [baseDir, setBaseDir] = useState<string | null>(null);
|
||||
const webViewRef = useRef<WebView>(null);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const indexAsset = Asset.fromModule(indexHtml);
|
||||
await indexAsset.downloadAsync(); // no-op in prod
|
||||
const indexAsset = Asset.fromModule(indexHtmlAsset);
|
||||
await indexAsset.downloadAsync();
|
||||
const localUri = indexAsset.localUri ?? indexAsset.uri;
|
||||
const dir = localUri.replace(/index\.html$/, '');
|
||||
setUri(localUri);
|
||||
@@ -27,28 +33,91 @@ export const App = () => {
|
||||
})();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
const paths = new MobilePathService();
|
||||
await copyAssets(paths);
|
||||
|
||||
const appMeta: AppMeta = {
|
||||
type: 'mobile',
|
||||
platform: modelName ?? 'unknown',
|
||||
};
|
||||
|
||||
app.current = new AppService(
|
||||
appMeta,
|
||||
new MobileFileSystem(),
|
||||
new MobileKyselyService(),
|
||||
paths
|
||||
);
|
||||
|
||||
await app.current.migrate();
|
||||
await app.current.init();
|
||||
appInitialized.current = true;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const handleMessage = useCallback(async (e: WebViewMessageEvent) => {
|
||||
const message = JSON.parse(e.nativeEvent.data) as Message;
|
||||
if (message.type === 'console') {
|
||||
console.log(
|
||||
`[WebView ${message.level.toUpperCase()}] ${message.timestamp} ${message.message}`
|
||||
);
|
||||
if (message.level === 'log') {
|
||||
console.log(
|
||||
`[WebView ${message.level.toUpperCase()}] ${message.timestamp} ${message.message}`
|
||||
);
|
||||
} else if (message.level === 'warn') {
|
||||
console.warn(
|
||||
`[WebView ${message.level.toUpperCase()}] ${message.timestamp} ${message.message}`
|
||||
);
|
||||
} else if (message.level === 'error') {
|
||||
console.error(
|
||||
`[WebView ${message.level.toUpperCase()}] ${message.timestamp} ${message.message}`
|
||||
);
|
||||
} else if (message.level === 'info') {
|
||||
console.info(
|
||||
`[WebView ${message.level.toUpperCase()}] ${message.timestamp} ${message.message}`
|
||||
);
|
||||
} else if (message.level === 'debug') {
|
||||
console.debug(
|
||||
`[WebView ${message.level.toUpperCase()}] ${message.timestamp} ${message.message}`
|
||||
);
|
||||
}
|
||||
} else if (message.type === 'init') {
|
||||
await app.migrate();
|
||||
await app.init();
|
||||
let count = 0;
|
||||
while (!appInitialized.current) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
count++;
|
||||
if (count > 100) {
|
||||
throw new Error('App initialization timed out');
|
||||
}
|
||||
}
|
||||
sendMessage({ type: 'init_result' });
|
||||
} else if (message.type === 'mutation') {
|
||||
const result = await app.mediator.executeMutation(message.input);
|
||||
if (!app.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await app.current.mediator.executeMutation(message.input);
|
||||
sendMessage({
|
||||
type: 'mutation_result',
|
||||
mutationId: message.mutationId,
|
||||
result,
|
||||
});
|
||||
} else if (message.type === 'query') {
|
||||
const result = await app.mediator.executeQuery(message.input);
|
||||
if (!app.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await app.current.mediator.executeQuery(message.input);
|
||||
sendMessage({ type: 'query_result', queryId: message.queryId, result });
|
||||
} else if (message.type === 'query_and_subscribe') {
|
||||
const result = await app.mediator.executeQueryAndSubscribe(
|
||||
if (!app.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await app.current.mediator.executeQueryAndSubscribe(
|
||||
message.key,
|
||||
message.windowId,
|
||||
message.input
|
||||
@@ -61,7 +130,11 @@ export const App = () => {
|
||||
result,
|
||||
});
|
||||
} else if (message.type === 'query_unsubscribe') {
|
||||
app.mediator.unsubscribeQuery(message.key, message.windowId);
|
||||
if (!app.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.current.mediator.unsubscribeQuery(message.key, message.windowId);
|
||||
} else if (message.type === 'event') {
|
||||
eventBus.publish(message.event);
|
||||
}
|
||||
|
||||
51
apps/mobile/src/lib/assets.ts
Normal file
51
apps/mobile/src/lib/assets.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Asset } from 'expo-asset';
|
||||
import { Directory, File } from 'expo-file-system';
|
||||
|
||||
import { PathService } from '@colanode/client/services';
|
||||
|
||||
import emojisDatabaseAsset from '../../assets/emojis.db';
|
||||
import neotraxFontAsset from '../../assets/fonts/neotrax.otf';
|
||||
import iconsDatabaseAsset from '../../assets/icons.db';
|
||||
import indexHtmlAsset from '../../assets/ui/index.html';
|
||||
|
||||
export {
|
||||
indexHtmlAsset,
|
||||
emojisDatabaseAsset,
|
||||
iconsDatabaseAsset,
|
||||
neotraxFontAsset,
|
||||
};
|
||||
|
||||
export const copyAssets = async (paths: PathService) => {
|
||||
try {
|
||||
const assetsDir = new Directory(paths.assets);
|
||||
assetsDir.create({ intermediates: true, idempotent: true });
|
||||
|
||||
const fontsDir = new Directory(paths.fonts);
|
||||
fontsDir.create({ intermediates: true, idempotent: true });
|
||||
|
||||
await copyAsset(
|
||||
Asset.fromModule(emojisDatabaseAsset),
|
||||
paths.emojisDatabase
|
||||
);
|
||||
await copyAsset(Asset.fromModule(iconsDatabaseAsset), paths.iconsDatabase);
|
||||
await copyAsset(
|
||||
Asset.fromModule(neotraxFontAsset),
|
||||
paths.font('neotrax.otf')
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const copyAsset = async (asset: Asset, path: string) => {
|
||||
await asset.downloadAsync();
|
||||
const localUri = asset.localUri ?? asset.uri;
|
||||
|
||||
const dest = new File(path);
|
||||
if (dest.exists) {
|
||||
dest.delete();
|
||||
}
|
||||
|
||||
const assetFile = new File(localUri);
|
||||
assetFile.copy(dest);
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
import { modelName } from 'expo-device';
|
||||
|
||||
import { AppMeta, AppService } from '@colanode/client/services';
|
||||
import { MobileFileSystem } from '@colanode/mobile/services/file-system';
|
||||
import { MobileKyselyService } from '@colanode/mobile/services/kysely-service';
|
||||
import { MobilePathService } from '@colanode/mobile/services/path-service';
|
||||
|
||||
const appMeta: AppMeta = {
|
||||
type: 'mobile',
|
||||
platform: modelName ?? 'unknown',
|
||||
};
|
||||
|
||||
export const app = new AppService(
|
||||
appMeta,
|
||||
new MobileFileSystem(),
|
||||
new MobileKyselyService(),
|
||||
new MobilePathService()
|
||||
);
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
|
||||
import { KyselyBuildOptions, KyselyService } from '@colanode/client/services';
|
||||
import { MobileFileSystem } from '@colanode/mobile/services/file-system';
|
||||
import { MobilePathService } from '@colanode/mobile/services/path-service';
|
||||
|
||||
export class MobileKyselyService implements KyselyService {
|
||||
private readonly fs = new MobileFileSystem();
|
||||
@@ -87,9 +88,17 @@ class ExpoSqliteDriver implements Driver {
|
||||
class ExpoSqliteConnection implements DatabaseConnection {
|
||||
private readonly database: SQLiteDatabase;
|
||||
private readonly options: KyselyBuildOptions;
|
||||
private readonly paths: MobilePathService = new MobilePathService();
|
||||
|
||||
constructor(options: KyselyBuildOptions) {
|
||||
this.database = openDatabaseSync(options.path);
|
||||
const databaseName = this.paths.filename(options.path);
|
||||
const databaseDirectory = this.paths.dirname(options.path);
|
||||
|
||||
this.database = openDatabaseSync(
|
||||
databaseName,
|
||||
undefined,
|
||||
databaseDirectory
|
||||
);
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,66 +1,65 @@
|
||||
import { Paths } from 'expo-file-system';
|
||||
import { Paths, File, Directory } from 'expo-file-system';
|
||||
|
||||
import { PathService } from '@colanode/client/services';
|
||||
|
||||
export class MobilePathService implements PathService {
|
||||
private readonly appPath = Paths.document.uri;
|
||||
private readonly appDatabasePath = this.join(this.appPath, 'app.db');
|
||||
private readonly accountsDirectoryPath = this.join(this.appPath, 'accounts');
|
||||
private readonly accountsDirectoryPath = new Directory(
|
||||
Paths.document,
|
||||
'accounts'
|
||||
);
|
||||
|
||||
private getAccountDirectoryPath(accountId: string): string {
|
||||
return this.join(this.accountsDirectoryPath, accountId);
|
||||
return new Directory(this.accountsDirectoryPath, accountId).uri;
|
||||
}
|
||||
|
||||
private getWorkspaceDirectoryPath(
|
||||
accountId: string,
|
||||
workspaceId: string
|
||||
): string {
|
||||
return this.join(
|
||||
return new Directory(
|
||||
this.getAccountDirectoryPath(accountId),
|
||||
'workspaces',
|
||||
workspaceId
|
||||
);
|
||||
).uri;
|
||||
}
|
||||
|
||||
private getWorkspaceFilesDirectoryPath(
|
||||
accountId: string,
|
||||
workspaceId: string
|
||||
): string {
|
||||
return this.join(
|
||||
return new Directory(
|
||||
this.getWorkspaceDirectoryPath(accountId, workspaceId),
|
||||
'files'
|
||||
);
|
||||
).uri;
|
||||
}
|
||||
|
||||
private getAccountAvatarsDirectoryPath(accountId: string): string {
|
||||
return this.join(this.getAccountDirectoryPath(accountId), 'avatars');
|
||||
return new Directory(this.getAccountDirectoryPath(accountId), 'avatars')
|
||||
.uri;
|
||||
}
|
||||
|
||||
private getAssetsSourcePath(): string {
|
||||
// In React Native/Expo, we should copy bundled assets to document directory
|
||||
// for file system access, or use Asset.fromModule for bundled assets
|
||||
// For now, we'll use a path in the document directory where assets will be copied
|
||||
return this.join(this.appPath, 'bundled-assets');
|
||||
return new Directory(Paths.document, 'assets').uri;
|
||||
}
|
||||
|
||||
public get app(): string {
|
||||
return this.appPath;
|
||||
return Paths.document.uri;
|
||||
}
|
||||
|
||||
public get appDatabase(): string {
|
||||
return this.appDatabasePath;
|
||||
return new File(Paths.document, 'app.db').uri;
|
||||
}
|
||||
|
||||
public get accounts(): string {
|
||||
return this.accountsDirectoryPath;
|
||||
return this.accountsDirectoryPath.uri;
|
||||
}
|
||||
|
||||
public get temp(): string {
|
||||
return this.join(this.appPath, 'temp');
|
||||
return new Directory(Paths.document, 'temp').uri;
|
||||
}
|
||||
|
||||
public tempFile(name: string): string {
|
||||
return this.join(this.appPath, 'temp', name);
|
||||
return new File(Paths.document, 'temp', name).uri;
|
||||
}
|
||||
|
||||
public account(accountId: string): string {
|
||||
@@ -68,7 +67,7 @@ export class MobilePathService implements PathService {
|
||||
}
|
||||
|
||||
public accountDatabase(accountId: string): string {
|
||||
return this.join(this.getAccountDirectoryPath(accountId), 'account.db');
|
||||
return new File(this.getAccountDirectoryPath(accountId), 'account.db').uri;
|
||||
}
|
||||
|
||||
public workspace(accountId: string, workspaceId: string): string {
|
||||
@@ -76,10 +75,10 @@ export class MobilePathService implements PathService {
|
||||
}
|
||||
|
||||
public workspaceDatabase(accountId: string, workspaceId: string): string {
|
||||
return this.join(
|
||||
return new File(
|
||||
this.getWorkspaceDirectoryPath(accountId, workspaceId),
|
||||
'workspace.db'
|
||||
);
|
||||
).uri;
|
||||
}
|
||||
|
||||
public workspaceFiles(accountId: string, workspaceId: string): string {
|
||||
@@ -92,10 +91,10 @@ export class MobilePathService implements PathService {
|
||||
fileId: string,
|
||||
extension: string
|
||||
): string {
|
||||
return this.join(
|
||||
return new File(
|
||||
this.getWorkspaceFilesDirectoryPath(accountId, workspaceId),
|
||||
fileId + extension
|
||||
);
|
||||
).uri;
|
||||
}
|
||||
|
||||
public accountAvatars(accountId: string): string {
|
||||
@@ -103,62 +102,30 @@ export class MobilePathService implements PathService {
|
||||
}
|
||||
|
||||
public accountAvatar(accountId: string, avatarId: string): string {
|
||||
return this.join(
|
||||
return new File(
|
||||
this.getAccountAvatarsDirectoryPath(accountId),
|
||||
avatarId + '.jpeg'
|
||||
);
|
||||
).uri;
|
||||
}
|
||||
|
||||
public dirname(dir: string): string {
|
||||
// Remove trailing slash if present
|
||||
const normalizedPath = dir.replace(/\/+$/, '');
|
||||
const lastSlashIndex = normalizedPath.lastIndexOf('/');
|
||||
if (lastSlashIndex === -1) {
|
||||
return '.';
|
||||
public dirname(path: string): string {
|
||||
const info = Paths.info(path);
|
||||
if (info.isDirectory) {
|
||||
return path;
|
||||
}
|
||||
if (lastSlashIndex === 0) {
|
||||
return '/';
|
||||
}
|
||||
return normalizedPath.substring(0, lastSlashIndex);
|
||||
|
||||
const file = new File(path);
|
||||
return file.parentDirectory.uri;
|
||||
}
|
||||
|
||||
public filename(file: string): string {
|
||||
const basename = file.substring(file.lastIndexOf('/') + 1);
|
||||
const lastDotIndex = basename.lastIndexOf('.');
|
||||
if (lastDotIndex === -1 || lastDotIndex === 0) {
|
||||
return basename;
|
||||
}
|
||||
return basename.substring(0, lastDotIndex);
|
||||
}
|
||||
|
||||
public join(...paths: string[]): string {
|
||||
if (paths.length === 0) return '.';
|
||||
|
||||
// Filter out empty strings and normalize paths
|
||||
const normalizedPaths = paths
|
||||
.filter((path) => path && path.length > 0)
|
||||
.map((path) => path.replace(/\/+$/, '')); // Remove trailing slashes
|
||||
|
||||
if (normalizedPaths.length === 0) return '.';
|
||||
|
||||
// Join with single slashes
|
||||
const result = normalizedPaths.join('/');
|
||||
|
||||
// Handle absolute paths (starting with /)
|
||||
if (paths[0] && paths[0].startsWith('/')) {
|
||||
return '/' + result.replace(/^\/+/, '');
|
||||
}
|
||||
|
||||
return result.replace(/\/+/g, '/'); // Replace multiple slashes with single slash
|
||||
public filename(path: string): string {
|
||||
const file = new File(path);
|
||||
return file.name;
|
||||
}
|
||||
|
||||
public extension(name: string): string {
|
||||
const basename = name.substring(name.lastIndexOf('/') + 1);
|
||||
const lastDotIndex = basename.lastIndexOf('.');
|
||||
if (lastDotIndex === -1 || lastDotIndex === 0) {
|
||||
return '';
|
||||
}
|
||||
return basename.substring(lastDotIndex);
|
||||
const file = new File(name);
|
||||
return file.extension;
|
||||
}
|
||||
|
||||
public get assets(): string {
|
||||
@@ -166,14 +133,18 @@ export class MobilePathService implements PathService {
|
||||
}
|
||||
|
||||
public get fonts(): string {
|
||||
return this.join(this.getAssetsSourcePath(), 'fonts');
|
||||
return new Directory(this.getAssetsSourcePath(), 'fonts').uri;
|
||||
}
|
||||
|
||||
public get emojisDatabase(): string {
|
||||
return this.join(this.getAssetsSourcePath(), 'emojis.db');
|
||||
return new File(this.getAssetsSourcePath(), 'emojis.db').uri;
|
||||
}
|
||||
|
||||
public get iconsDatabase(): string {
|
||||
return this.join(this.getAssetsSourcePath(), 'icons.db');
|
||||
return new File(this.getAssetsSourcePath(), 'icons.db').uri;
|
||||
}
|
||||
|
||||
public font(name: string): string {
|
||||
return new File(this.getAssetsSourcePath(), 'fonts', name).uri;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,9 @@ export class WebFileSystem implements FileSystem {
|
||||
//Ensure the data type is compatible with the File Write API
|
||||
private ensureArrayBuffer(data: Uint8Array): ArrayBuffer {
|
||||
const arrayBuffer: ArrayBuffer =
|
||||
data.buffer instanceof ArrayBuffer ?
|
||||
data.buffer : new ArrayBuffer(data.byteLength);
|
||||
data.buffer instanceof ArrayBuffer
|
||||
? data.buffer
|
||||
: new ArrayBuffer(data.byteLength);
|
||||
|
||||
if (!(data.buffer instanceof ArrayBuffer)) {
|
||||
const view = new Uint8Array<ArrayBuffer>(arrayBuffer);
|
||||
|
||||
@@ -101,4 +101,8 @@ export class WebPathService implements PathService {
|
||||
const parts = path.split('.');
|
||||
return parts.length > 1 ? '.' + parts[parts.length - 1] : '';
|
||||
}
|
||||
|
||||
public font(name: string): string {
|
||||
return this.join(this.assetsSourcePath, 'fonts', name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export class AssetService {
|
||||
path: this.app.path.emojisDatabase,
|
||||
readonly: true,
|
||||
});
|
||||
|
||||
this.icons = this.app.kysely.build<IconDatabaseSchema>({
|
||||
path: this.app.path.iconsDatabase,
|
||||
readonly: true,
|
||||
|
||||
@@ -19,10 +19,10 @@ export interface PathService {
|
||||
accountAvatar: (accountId: string, avatarId: string) => string;
|
||||
dirname: (path: string) => string;
|
||||
filename: (path: string) => string;
|
||||
join: (...paths: string[]) => string;
|
||||
extension: (path: string) => string;
|
||||
assets: string;
|
||||
fonts: string;
|
||||
emojisDatabase: string;
|
||||
iconsDatabase: string;
|
||||
font: (name: string) => string;
|
||||
}
|
||||
|
||||
@@ -389,7 +389,12 @@ export class FileService {
|
||||
}
|
||||
|
||||
private buildFilePath(id: string, extension: string): string {
|
||||
return this.app.path.join(this.filesDir, `${id}${extension}`);
|
||||
return this.app.path.workspaceFile(
|
||||
this.workspace.accountId,
|
||||
this.workspace.id,
|
||||
id,
|
||||
extension
|
||||
);
|
||||
}
|
||||
|
||||
public async cleanupFiles(): Promise<void> {
|
||||
@@ -423,7 +428,15 @@ export class FileService {
|
||||
continue;
|
||||
}
|
||||
|
||||
const filePath = this.app.path.join(this.filesDir, fileIdMap[fileId]!);
|
||||
const fsFile = fileIdMap[fileId]!;
|
||||
const name = this.app.path.filename(fsFile);
|
||||
const extension = this.app.path.extension(fsFile);
|
||||
const filePath = this.app.path.workspaceFile(
|
||||
this.workspace.accountId,
|
||||
this.workspace.id,
|
||||
name,
|
||||
extension
|
||||
);
|
||||
await this.app.fs.delete(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export const Login = () => {
|
||||
|
||||
return (
|
||||
<div className="grid h-screen min-h-screen w-full grid-cols-1 lg:grid-cols-5">
|
||||
<div className="items-center justify-center bg-foreground hidden lg:flex">
|
||||
<div className="items-center justify-center bg-foreground hidden lg:flex lg:col-span-2">
|
||||
<h1 className="font-neotrax text-6xl text-background">colanode</h1>
|
||||
</div>
|
||||
<div className="flex items-center justify-center py-12 lg:col-span-3">
|
||||
|
||||
@@ -18,13 +18,23 @@ export const App = ({ type }: AppProps) => {
|
||||
const [initialized, setInitialized] = useState(false);
|
||||
const [openLogin, setOpenLogin] = useState(false);
|
||||
|
||||
const appMetadataListQuery = useLiveQuery({
|
||||
type: 'app.metadata.list',
|
||||
});
|
||||
const appMetadataListQuery = useLiveQuery(
|
||||
{
|
||||
type: 'app.metadata.list',
|
||||
},
|
||||
{
|
||||
enabled: initialized,
|
||||
}
|
||||
);
|
||||
|
||||
const accountListQuery = useLiveQuery({
|
||||
type: 'account.list',
|
||||
});
|
||||
const accountListQuery = useLiveQuery(
|
||||
{
|
||||
type: 'account.list',
|
||||
},
|
||||
{
|
||||
enabled: initialized,
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
window.colanode.init().then(() => {
|
||||
|
||||
@@ -20,6 +20,7 @@ const FONTS_OTF_PATH = path.resolve(FONTS_DIR, NEOTRAX_FONT_NAME);
|
||||
|
||||
const DESKTOP_ASSETS_DIR = path.resolve('apps', 'desktop', 'assets');
|
||||
const WEB_ASSETS_DIR = path.resolve('apps', 'web', 'public', 'assets');
|
||||
const MOBILE_ASSETS_DIR = path.resolve('apps', 'mobile', 'assets');
|
||||
|
||||
const copyFile = (source: string, target: string | string[]) => {
|
||||
if (!fs.existsSync(source)) {
|
||||
@@ -40,24 +41,19 @@ const copyFile = (source: string, target: string | string[]) => {
|
||||
|
||||
const execute = () => {
|
||||
copyFile(EMOJIS_DB_PATH, path.resolve(DESKTOP_ASSETS_DIR, 'emojis.db'));
|
||||
copyFile(EMOJIS_DB_PATH, path.resolve(MOBILE_ASSETS_DIR, 'emojis.db'));
|
||||
copyFile(EMOJIS_MIN_DB_PATH, path.resolve(WEB_ASSETS_DIR, 'emojis.db'));
|
||||
|
||||
copyFile(EMOJI_SVG_PATH, [
|
||||
path.resolve(DESKTOP_ASSETS_DIR, 'emojis.svg'),
|
||||
path.resolve(WEB_ASSETS_DIR, 'emojis.svg'),
|
||||
]);
|
||||
copyFile(EMOJI_SVG_PATH, path.resolve(WEB_ASSETS_DIR, 'emojis.svg'));
|
||||
|
||||
copyFile(ICONS_DB_PATH, path.resolve(DESKTOP_ASSETS_DIR, 'icons.db'));
|
||||
copyFile(ICONS_DB_PATH, path.resolve(MOBILE_ASSETS_DIR, 'icons.db'));
|
||||
copyFile(ICONS_MIN_DB_PATH, path.resolve(WEB_ASSETS_DIR, 'icons.db'));
|
||||
|
||||
copyFile(ICONS_SVG_PATH, [
|
||||
path.resolve(DESKTOP_ASSETS_DIR, 'icons.svg'),
|
||||
path.resolve(WEB_ASSETS_DIR, 'icons.svg'),
|
||||
]);
|
||||
copyFile(ICONS_SVG_PATH, path.resolve(WEB_ASSETS_DIR, 'icons.svg'));
|
||||
|
||||
copyFile(FONTS_OTF_PATH, [
|
||||
path.resolve(DESKTOP_ASSETS_DIR, 'fonts', NEOTRAX_FONT_NAME),
|
||||
path.resolve(WEB_ASSETS_DIR, 'fonts', NEOTRAX_FONT_NAME),
|
||||
path.resolve(MOBILE_ASSETS_DIR, 'fonts', NEOTRAX_FONT_NAME),
|
||||
]);
|
||||
|
||||
copyFile(
|
||||
|
||||
Reference in New Issue
Block a user