mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-19 15:20:51 +01:00
polyfill for spa added. works now on android
This commit is contained in:
@ -13,6 +13,12 @@ interface ExtensionRequest {
|
||||
let globalHandlerRegistered = false
|
||||
const iframeRegistry = new Map<HTMLIFrameElement, IHaexHubExtension>()
|
||||
|
||||
// Store context values that need to be accessed outside setup
|
||||
let contextGetters: {
|
||||
getTheme: () => string
|
||||
getLocale: () => string
|
||||
} | null = null
|
||||
|
||||
const registerGlobalMessageHandler = () => {
|
||||
if (globalHandlerRegistered) return
|
||||
|
||||
@ -61,6 +67,8 @@ const registerGlobalMessageHandler = () => {
|
||||
result = await handlePermissionsMethodAsync(request, extension)
|
||||
} else if (request.method.startsWith('context.')) {
|
||||
result = await handleContextMethodAsync(request)
|
||||
} else if (request.method.startsWith('storage.')) {
|
||||
result = await handleStorageMethodAsync(request, extension)
|
||||
} else {
|
||||
throw new Error(`Unknown method: ${request.method}`)
|
||||
}
|
||||
@ -96,6 +104,18 @@ export const useExtensionMessageHandler = (
|
||||
iframeRef: Ref<HTMLIFrameElement | undefined | null>,
|
||||
extension: ComputedRef<IHaexHubExtension | undefined | null>,
|
||||
) => {
|
||||
// Initialize context getters (can use composables here because we're in setup)
|
||||
const { currentTheme } = storeToRefs(useUiStore())
|
||||
const { locale } = useI18n()
|
||||
|
||||
// Store getters for use outside setup context
|
||||
if (!contextGetters) {
|
||||
contextGetters = {
|
||||
getTheme: () => currentTheme.value?.value || 'system',
|
||||
getLocale: () => locale.value,
|
||||
}
|
||||
}
|
||||
|
||||
// Registriere globalen Handler beim ersten Aufruf
|
||||
registerGlobalMessageHandler()
|
||||
|
||||
@ -114,6 +134,28 @@ export const useExtensionMessageHandler = (
|
||||
})
|
||||
}
|
||||
|
||||
// Export Funktion für manuelle IFrame-Registrierung (kein Composable!)
|
||||
export const registerExtensionIFrame = (
|
||||
iframe: HTMLIFrameElement,
|
||||
extension: IHaexHubExtension,
|
||||
) => {
|
||||
// Stelle sicher, dass der globale Handler registriert ist
|
||||
registerGlobalMessageHandler()
|
||||
|
||||
// Warnung wenn Context Getters nicht initialisiert wurden
|
||||
if (!contextGetters) {
|
||||
console.warn(
|
||||
'Context getters not initialized. Make sure useExtensionMessageHandler was called in setup context first.',
|
||||
)
|
||||
}
|
||||
|
||||
iframeRegistry.set(iframe, extension)
|
||||
}
|
||||
|
||||
export const unregisterExtensionIFrame = (iframe: HTMLIFrameElement) => {
|
||||
iframeRegistry.delete(iframe)
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Extension Methods
|
||||
// ==========================================
|
||||
@ -243,14 +285,16 @@ async function handlePermissionsMethodAsync(
|
||||
// ==========================================
|
||||
|
||||
async function handleContextMethodAsync(request: ExtensionRequest) {
|
||||
const { currentTheme } = storeToRefs(useUiStore())
|
||||
const { locale } = useI18n()
|
||||
|
||||
switch (request.method) {
|
||||
case 'context.get':
|
||||
if (!contextGetters) {
|
||||
throw new Error(
|
||||
'Context not initialized. Make sure useExtensionMessageHandler is called in a component.',
|
||||
)
|
||||
}
|
||||
return {
|
||||
theme: currentTheme.value || 'system',
|
||||
locale: locale.value,
|
||||
theme: contextGetters.getTheme(),
|
||||
locale: contextGetters.getLocale(),
|
||||
platform: detectPlatform(),
|
||||
}
|
||||
|
||||
@ -265,3 +309,53 @@ function detectPlatform(): 'desktop' | 'mobile' | 'tablet' {
|
||||
if (width < 1024) return 'tablet'
|
||||
return 'desktop'
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Storage Methods
|
||||
// ==========================================
|
||||
|
||||
async function handleStorageMethodAsync(
|
||||
request: ExtensionRequest,
|
||||
extension: IHaexHubExtension,
|
||||
) {
|
||||
const storageKey = `ext_${extension.id}_`
|
||||
console.log(`[HaexHub Storage] ${request.method} for extension ${extension.id}`)
|
||||
|
||||
switch (request.method) {
|
||||
case 'storage.getItem': {
|
||||
const key = request.params.key as string
|
||||
return localStorage.getItem(storageKey + key)
|
||||
}
|
||||
|
||||
case 'storage.setItem': {
|
||||
const key = request.params.key as string
|
||||
const value = request.params.value as string
|
||||
localStorage.setItem(storageKey + key, value)
|
||||
return null
|
||||
}
|
||||
|
||||
case 'storage.removeItem': {
|
||||
const key = request.params.key as string
|
||||
localStorage.removeItem(storageKey + key)
|
||||
return null
|
||||
}
|
||||
|
||||
case 'storage.clear': {
|
||||
// Remove only extension-specific keys
|
||||
const keys = Object.keys(localStorage).filter(k => k.startsWith(storageKey))
|
||||
keys.forEach(k => localStorage.removeItem(k))
|
||||
return null
|
||||
}
|
||||
|
||||
case 'storage.keys': {
|
||||
// Return only extension-specific keys (without prefix)
|
||||
const keys = Object.keys(localStorage)
|
||||
.filter(k => k.startsWith(storageKey))
|
||||
.map(k => k.substring(storageKey.length))
|
||||
return keys
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown storage method: ${request.method}`)
|
||||
}
|
||||
}
|
||||
|
||||
60
src/composables/useAndroidBackButton.ts
Normal file
60
src/composables/useAndroidBackButton.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||
|
||||
/**
|
||||
* Handles Android back button to navigate within the app instead of closing it
|
||||
* Mimics browser behavior: navigate back if possible, close app if on first page
|
||||
*/
|
||||
export function useAndroidBackButton() {
|
||||
const router = useRouter()
|
||||
const historyStack = ref<string[]>([])
|
||||
let unlisten: (() => void) | null = null
|
||||
|
||||
// Track navigation history manually
|
||||
router.afterEach((to, from) => {
|
||||
console.log('[AndroidBack] Navigation:', { to: to.path, from: from.path, stackSize: historyStack.value.length })
|
||||
|
||||
// If navigating forward (new page)
|
||||
if (from.path && to.path !== from.path && !historyStack.value.includes(to.path)) {
|
||||
historyStack.value.push(from.path)
|
||||
console.log('[AndroidBack] Added to stack:', from.path, 'Stack:', historyStack.value)
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
const os = platform()
|
||||
|
||||
if (os === 'android') {
|
||||
const appWindow = getCurrentWindow()
|
||||
|
||||
// Listen to close requested event (triggered by Android back button)
|
||||
unlisten = await appWindow.onCloseRequested(async (event) => {
|
||||
console.log('[AndroidBack] Back button pressed, stack size:', historyStack.value.length)
|
||||
|
||||
// Check if we have history
|
||||
if (historyStack.value.length > 0) {
|
||||
// Prevent window from closing
|
||||
event.preventDefault()
|
||||
|
||||
// Remove current page from stack
|
||||
historyStack.value.pop()
|
||||
console.log('[AndroidBack] Going back, new stack size:', historyStack.value.length)
|
||||
|
||||
// Navigate back in router
|
||||
router.back()
|
||||
} else {
|
||||
console.log('[AndroidBack] No history, allowing app to close')
|
||||
}
|
||||
// If no history, allow default behavior (app closes)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (unlisten) {
|
||||
unlisten()
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user