mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 22:20:51 +01:00
Fix workspace background feature for Android
- Add missing filesystem permissions in capabilities - fs:allow-applocaldata-read-recursive - fs:allow-applocaldata-write-recursive - fs:allow-write-file - fs:allow-mkdir - fs:allow-exists - fs:allow-remove - Fix Android photo picker URI handling - Detect file type from binary signature (PNG, JPEG, WebP) - Use manual path construction to avoid path joining issues - Works with Android photo picker content:// URIs - Improve error handling with detailed toast messages - Show specific error at each step (read, mkdir, write, db) - Better debugging on Android where console is unavailable - Fix window activation behavior - Restore minimized windows when activated - Remove unused imports in launcher component
This commit is contained in:
@ -18,8 +18,14 @@
|
|||||||
"fs:allow-appconfig-write-recursive",
|
"fs:allow-appconfig-write-recursive",
|
||||||
"fs:allow-appdata-read-recursive",
|
"fs:allow-appdata-read-recursive",
|
||||||
"fs:allow-appdata-write-recursive",
|
"fs:allow-appdata-write-recursive",
|
||||||
|
"fs:allow-applocaldata-read-recursive",
|
||||||
|
"fs:allow-applocaldata-write-recursive",
|
||||||
"fs:allow-read-file",
|
"fs:allow-read-file",
|
||||||
|
"fs:allow-write-file",
|
||||||
"fs:allow-read-dir",
|
"fs:allow-read-dir",
|
||||||
|
"fs:allow-mkdir",
|
||||||
|
"fs:allow-exists",
|
||||||
|
"fs:allow-remove",
|
||||||
"fs:allow-resource-read-recursive",
|
"fs:allow-resource-read-recursive",
|
||||||
"fs:allow-resource-write-recursive",
|
"fs:allow-resource-write-recursive",
|
||||||
"fs:allow-download-read-recursive",
|
"fs:allow-download-read-recursive",
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
{"default":{"identifier":"default","description":"Capability for the main window","local":true,"windows":["main"],"permissions":["core:default","core:webview:allow-create-webview-window","core:webview:allow-create-webview","core:webview:allow-webview-show","core:webview:default","core:window:allow-create","core:window:allow-get-all-windows","core:window:allow-show","core:window:default","dialog:default","fs:allow-appconfig-read-recursive","fs:allow-appconfig-write-recursive","fs:allow-appdata-read-recursive","fs:allow-appdata-write-recursive","fs:allow-read-file","fs:allow-read-dir","fs:allow-resource-read-recursive","fs:allow-resource-write-recursive","fs:allow-download-read-recursive","fs:allow-download-write-recursive","fs:default",{"identifier":"fs:scope","allow":[{"path":"**"}]},"http:allow-fetch-send","http:allow-fetch","http:default","notification:allow-create-channel","notification:allow-list-channels","notification:allow-notify","notification:allow-is-permission-granted","notification:default","opener:allow-open-url","opener:default","os:allow-hostname","os:default","store:default"]}}
|
{"default":{"identifier":"default","description":"Capability for the main window","local":true,"windows":["main"],"permissions":["core:default","core:webview:allow-create-webview-window","core:webview:allow-create-webview","core:webview:allow-webview-show","core:webview:default","core:window:allow-create","core:window:allow-get-all-windows","core:window:allow-show","core:window:default","dialog:default","fs:allow-appconfig-read-recursive","fs:allow-appconfig-write-recursive","fs:allow-appdata-read-recursive","fs:allow-appdata-write-recursive","fs:allow-applocaldata-read-recursive","fs:allow-applocaldata-write-recursive","fs:allow-read-file","fs:allow-write-file","fs:allow-read-dir","fs:allow-mkdir","fs:allow-exists","fs:allow-remove","fs:allow-resource-read-recursive","fs:allow-resource-write-recursive","fs:allow-download-read-recursive","fs:allow-download-write-recursive","fs:default",{"identifier":"fs:scope","allow":[{"path":"**"}]},"http:allow-fetch-send","http:allow-fetch","http:default","notification:allow-create-channel","notification:allow-list-channels","notification:allow-notify","notification:allow-is-permission-granted","notification:default","opener:allow-open-url","opener:default","os:allow-hostname","os:default","store:default"]}}
|
||||||
@ -43,7 +43,6 @@
|
|||||||
draggable="true"
|
draggable="true"
|
||||||
@click="openItem(item)"
|
@click="openItem(item)"
|
||||||
@dragstart="handleDragStart($event, item)"
|
@dragstart="handleDragStart($event, item)"
|
||||||
@dragend="handleDragEnd"
|
|
||||||
/>
|
/>
|
||||||
</UContextMenu>
|
</UContextMenu>
|
||||||
|
|
||||||
|
|||||||
@ -47,7 +47,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="h-full"/>
|
<div class="h-full" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -55,8 +55,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Locale } from 'vue-i18n'
|
import type { Locale } from 'vue-i18n'
|
||||||
import { open } from '@tauri-apps/plugin-dialog'
|
import { open } from '@tauri-apps/plugin-dialog'
|
||||||
import { readFile, writeFile, mkdir, exists, remove } from '@tauri-apps/plugin-fs'
|
import {
|
||||||
import { appLocalDataDir, join } from '@tauri-apps/api/path'
|
readFile,
|
||||||
|
writeFile,
|
||||||
|
mkdir,
|
||||||
|
exists,
|
||||||
|
remove,
|
||||||
|
} from '@tauri-apps/plugin-fs'
|
||||||
|
import { appLocalDataDir } from '@tauri-apps/api/path'
|
||||||
|
|
||||||
const { t, setLocale } = useI18n()
|
const { t, setLocale } = useI18n()
|
||||||
|
|
||||||
@ -120,38 +126,115 @@ const selectBackgroundImage = async () => {
|
|||||||
try {
|
try {
|
||||||
const selected = await open({
|
const selected = await open({
|
||||||
multiple: false,
|
multiple: false,
|
||||||
filters: [{
|
filters: [
|
||||||
|
{
|
||||||
name: 'Images',
|
name: 'Images',
|
||||||
extensions: ['png', 'jpg', 'jpeg', 'webp']
|
extensions: ['png', 'jpg', 'jpeg', 'webp'],
|
||||||
}]
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
if (selected && typeof selected === 'string') {
|
if (!selected || typeof selected !== 'string') {
|
||||||
const fileData = await readFile(selected)
|
return
|
||||||
|
|
||||||
// Create files directory if it doesn't exist
|
|
||||||
const appDataPath = await appLocalDataDir()
|
|
||||||
const filesDir = await join(appDataPath, 'files')
|
|
||||||
|
|
||||||
if (!await exists(filesDir)) {
|
|
||||||
await mkdir(filesDir, { recursive: true })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate unique filename for the background image
|
// Read the selected file (works with Android photo picker URIs)
|
||||||
const ext = selected.split('.').pop()?.toLowerCase() || 'png'
|
let fileData: Uint8Array
|
||||||
const fileName = `workspace-${currentWorkspace.value.id}-background.${ext}`
|
try {
|
||||||
const targetPath = await join(filesDir, fileName)
|
fileData = await readFile(selected)
|
||||||
|
} catch (readError) {
|
||||||
|
add({
|
||||||
|
description: `Fehler beim Lesen: ${readError instanceof Error ? readError.message : String(readError)}`,
|
||||||
|
color: 'error',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Copy file to app data directory
|
// Detect file type from file signature
|
||||||
|
let ext = 'jpg' // default
|
||||||
|
if (fileData.length > 4) {
|
||||||
|
// PNG signature: 89 50 4E 47
|
||||||
|
if (
|
||||||
|
fileData[0] === 0x89 &&
|
||||||
|
fileData[1] === 0x50 &&
|
||||||
|
fileData[2] === 0x4e &&
|
||||||
|
fileData[3] === 0x47
|
||||||
|
) {
|
||||||
|
ext = 'png'
|
||||||
|
}
|
||||||
|
// JPEG signature: FF D8 FF
|
||||||
|
else if (
|
||||||
|
fileData[0] === 0xff &&
|
||||||
|
fileData[1] === 0xd8 &&
|
||||||
|
fileData[2] === 0xff
|
||||||
|
) {
|
||||||
|
ext = 'jpg'
|
||||||
|
}
|
||||||
|
// WebP signature: RIFF xxxx WEBP
|
||||||
|
else if (
|
||||||
|
fileData[0] === 0x52 &&
|
||||||
|
fileData[1] === 0x49 &&
|
||||||
|
fileData[2] === 0x46 &&
|
||||||
|
fileData[3] === 0x46
|
||||||
|
) {
|
||||||
|
ext = 'webp'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get app local data directory
|
||||||
|
const appDataPath = await appLocalDataDir()
|
||||||
|
|
||||||
|
// Construct target path manually to avoid path joining issues
|
||||||
|
const fileName = `workspace-${currentWorkspace.value.id}-background.${ext}`
|
||||||
|
const targetPath = `${appDataPath}/files/${fileName}`
|
||||||
|
|
||||||
|
// Create parent directory if it doesn't exist
|
||||||
|
const parentDir = `${appDataPath}/files`
|
||||||
|
try {
|
||||||
|
if (!(await exists(parentDir))) {
|
||||||
|
await mkdir(parentDir, { recursive: true })
|
||||||
|
}
|
||||||
|
} catch (mkdirError) {
|
||||||
|
add({
|
||||||
|
description: `Fehler beim Erstellen des Ordners: ${mkdirError instanceof Error ? mkdirError.message : String(mkdirError)}`,
|
||||||
|
color: 'error',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write file to app data directory
|
||||||
|
try {
|
||||||
await writeFile(targetPath, fileData)
|
await writeFile(targetPath, fileData)
|
||||||
|
} catch (writeError) {
|
||||||
|
add({
|
||||||
|
description: `Fehler beim Schreiben: ${writeError instanceof Error ? writeError.message : String(writeError)}`,
|
||||||
|
color: 'error',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Store the absolute file path in database
|
// Store the absolute file path in database
|
||||||
await updateWorkspaceBackgroundAsync(currentWorkspace.value.id, targetPath)
|
try {
|
||||||
add({ description: t('workspaceBackground.update.success'), color: 'success' })
|
await updateWorkspaceBackgroundAsync(
|
||||||
|
currentWorkspace.value.id,
|
||||||
|
targetPath,
|
||||||
|
)
|
||||||
|
add({
|
||||||
|
description: t('workspaceBackground.update.success'),
|
||||||
|
color: 'success',
|
||||||
|
})
|
||||||
|
} catch (dbError) {
|
||||||
|
add({
|
||||||
|
description: `Fehler beim DB-Update: ${dbError instanceof Error ? dbError.message : String(dbError)}`,
|
||||||
|
color: 'error',
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error selecting background:', error)
|
console.error('Error selecting background:', error)
|
||||||
add({ description: t('workspaceBackground.update.error'), color: 'error' })
|
add({
|
||||||
|
description: `${t('workspaceBackground.update.error')}: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
color: 'error',
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +256,10 @@ const removeBackgroundImage = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await updateWorkspaceBackgroundAsync(currentWorkspace.value.id, null)
|
await updateWorkspaceBackgroundAsync(currentWorkspace.value.id, null)
|
||||||
add({ description: t('workspaceBackground.remove.success'), color: 'success' })
|
add({
|
||||||
|
description: t('workspaceBackground.remove.success'),
|
||||||
|
color: 'success',
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error removing background:', error)
|
console.error('Error removing background:', error)
|
||||||
add({ description: t('workspaceBackground.remove.error'), color: 'error' })
|
add({ description: t('workspaceBackground.remove.error'), color: 'error' })
|
||||||
|
|||||||
@ -300,6 +300,7 @@ export const useWindowManagerStore = defineStore('windowManager', () => {
|
|||||||
const window = windows.value.find((w) => w.id === windowId)
|
const window = windows.value.find((w) => w.id === windowId)
|
||||||
if (window) {
|
if (window) {
|
||||||
window.zIndex = nextZIndex.value++
|
window.zIndex = nextZIndex.value++
|
||||||
|
window.isMinimized = false
|
||||||
activeWindowId.value = windowId
|
activeWindowId.value = windowId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user