Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 9 additions & 0 deletions src/app/AppConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ function getAppConfigFilePath() {
* Stored in the application data directory.
*/
export type AppConfig = {
// ----------------
// Internal
// ----------------

/**
* Last application version when the app was running
*/
lastAppVersion?: string

// ----------------
// General settings
// ----------------
Expand Down
67 changes: 67 additions & 0 deletions src/app/migration.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*!
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import type { Awaitable } from '../shared/utils.types.ts'

import gt from 'semver/functions/gt.js'
import { version } from '../../package.json' with { type: 'json' }
import { getAppConfig, setAppConfig } from './AppConfig.ts'
import { migrations } from './migrations/migrations.ts'

export type Migration = {
/** Migration name for logging purposes */
name: string

/** Whether to run this migration on the first start of the application after installation */
onFirstStart?: boolean

/** Whether to run this migration on application upgrade */
onUpgrade?: boolean

/** Determine whether the migration should run if it is only needed in certain conditions */
validator?: () => Awaitable<boolean>

/** Migration execution */
up(): Awaitable<void>
}

/**
* Run migration
*
* @param migration - Migration
*/
async function runMigration(migration: Migration): Promise<void> {
const lastAppVersion = getAppConfig('lastAppVersion')

const matchesFirstStart = migration.onFirstStart && !lastAppVersion
const matchesUpgrade = migration.onUpgrade && lastAppVersion && gt(version, lastAppVersion)
if (!matchesFirstStart && !matchesUpgrade) {
return
}

const matchesValidator = await migration.validator?.() ?? true
if (!matchesValidator) {
return
}

try {
console.log(`Running migration "${migration.name}"`)
await migration.up()
} catch (error) {
console.error(`Unexpected exception during migration "${migration.name}":`, (error as Error).message)
}
}

/**
* Run all migrations and update last version
*/
export async function runMigrations() {
for (const migration of migrations) {
await runMigration(migration)
}

// Migrations are complete - now the app is on the new version
setAppConfig('lastAppVersion', version)
}
27 changes: 27 additions & 0 deletions src/app/migrations/01-clearFlatpakFontConfigCache.migration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*!
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import type { Migration } from '../migration.service.ts'

import { clearFlatpakFontConfigCache, isFlatpak } from '../system.utils.ts'

/**
* Manually clear font config cache for Flatpak installations.
* Fixes issues with font rendering like incorrect Emoji font.
Comment thread
nickvergessen marked this conversation as resolved.
*/
export const clearFlatpakFontConfigCacheMigration: Migration = {
name: 'Clear flatpak font config cache',

onFirstStart: true,

validator(): boolean {
// Flatpak specific problem
return isFlatpak
},

async up() {
await clearFlatpakFontConfigCache()
},
}
12 changes: 12 additions & 0 deletions src/app/migrations/migrations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*!
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import type { Migration } from '../migration.service.ts'

import { clearFlatpakFontConfigCacheMigration } from './01-clearFlatpakFontConfigCache.migration.ts'

export const migrations: Migration[] = [
clearFlatpakFontConfigCacheMigration,
]
21 changes: 21 additions & 0 deletions src/app/system.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { rm } from 'node:fs/promises'
import os from 'node:os'
import path from 'node:path'
import { BUILD_CONFIG } from '../shared/build.config.ts'
Expand Down Expand Up @@ -100,3 +101,23 @@ export function isSameExecution(argv0: string, cwd: string) {

return execPath === process.execPath
}

/**
* Manually clear font config cache for Flatpak installations.
* Fixes issues with font rendering like incorrect Emoji font.
*
* @see https://github.com/nextcloud/talk-desktop/issues/1514
*/
export async function clearFlatpakFontConfigCache() {
if (!process.env.XDG_CACHE_HOME) {
console.error('Failed to clear font config cache: $XDG_CACHE_HOME is not defined')
return
}

try {
// Note: clearing with "fc-cache" command did not help with the issue (was tested with many users and colleagues)
await rm(path.join(process.env.XDG_CACHE_HOME, 'fontconfig'), { recursive: true, force: true })
} catch (error) {
console.error(`Failed to remove font config cache: ${(error as Error).message}`)
}
}
4 changes: 3 additions & 1 deletion src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const { openChromeWebRtcInternals } = require('./app/dev.utils.ts')
const { triggerDownloadUrl } = require('./app/downloads.ts')
const { setupReleaseNotificationScheduler } = require('./app/githubReleaseNotification.service.js')
const { initLaunchAtStartupListener } = require('./app/launchAtStartup.config.ts')
const { runMigrations } = require('./app/migration.service.ts')
const { systemInfo, isLinux, isMac, isWindows, isSameExecution } = require('./app/system.utils.ts')
const { applyTheme } = require('./app/theme.config.ts')
const { buildTitle } = require('./app/utils.ts')
Expand Down Expand Up @@ -80,7 +81,6 @@ if (!app.requestSingleInstanceLock()) {
app.quit()
}


ipcMain.on('app:quit', () => app.quit())
ipcMain.handle('app:getSystemInfo', () => systemInfo)
ipcMain.handle('app:buildTitle', (event, title) => buildTitle(title))
Expand Down Expand Up @@ -141,6 +141,8 @@ let isInWindowRelaunch = false

app.whenReady().then(async () => {
await loadAppConfig()
await runMigrations()

applyTheme()
initLaunchAtStartupListener()
registerAppProtocolHandler()
Expand Down
6 changes: 6 additions & 0 deletions src/shared/utils.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*!
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

export type Awaitable<T> = T | PromiseLike<T>
Loading