fix(stage-tamagotchi): bring back onboarding dialog (#1104)
This commit is contained in:
@@ -31,6 +31,7 @@ import { setupChatWindowReusableFunc } from './windows/chat'
|
||||
import { setupDevtoolsWindow } from './windows/devtools'
|
||||
import { setupMainWindow } from './windows/main'
|
||||
import { setupNoticeWindowManager } from './windows/notice'
|
||||
import { setupOnboardingWindowManager } from './windows/onboarding'
|
||||
import { setupSettingsWindowReusableFunc } from './windows/settings'
|
||||
import { setupWidgetsWindowManager } from './windows/widgets'
|
||||
|
||||
@@ -107,6 +108,8 @@ app.whenReady().then(async () => {
|
||||
const beatSync = injeca.provide('windows:beat-sync', () => setupBeatSync())
|
||||
|
||||
const devtoolsMarkdownStressWindow = injeca.provide('windows:devtools:markdown-stress', () => setupDevtoolsWindow())
|
||||
|
||||
const onboardingWindow = injeca.provide('windows:onboarding', () => setupOnboardingWindowManager())
|
||||
const noticeWindow = injeca.provide('windows:notice', {
|
||||
dependsOn: { i18n, serverChannel },
|
||||
build: ({ dependsOn }) => setupNoticeWindowManager(dependsOn),
|
||||
@@ -152,6 +155,12 @@ app.whenReady().then(async () => {
|
||||
callback: noop,
|
||||
})
|
||||
|
||||
// Show onboarding window if not completed
|
||||
injeca.invoke({
|
||||
dependsOn: { onboardingWindow },
|
||||
callback: ({ onboardingWindow }) => { onboardingWindow.showIfNeeded() },
|
||||
})
|
||||
|
||||
injeca.start().catch(err => console.error(err))
|
||||
|
||||
// Lifecycle
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
import { join, resolve } from 'node:path'
|
||||
|
||||
import { is } from '@electron-toolkit/utils'
|
||||
import { defineInvokeHandler } from '@moeru/eventa'
|
||||
import { createContext } from '@moeru/eventa/adapters/electron/main'
|
||||
import { BrowserWindow, ipcMain, shell } from 'electron'
|
||||
|
||||
import icon from '../../../../resources/icon.png?asset'
|
||||
|
||||
import { electronOnboardingClose, electronOnboardingCompleted, electronOnboardingSkipped, electronOpenOnboarding } from '../../../shared/eventa'
|
||||
import { baseUrl, getElectronMainDirname, load, withHashRoute } from '../../libs/electron/location'
|
||||
|
||||
export interface OnboardingWindowManager {
|
||||
getWindow: () => BrowserWindow | null
|
||||
showIfNeeded: () => Promise<boolean>
|
||||
close: () => void
|
||||
markCompleted: () => void
|
||||
markSkipped: () => void
|
||||
}
|
||||
|
||||
export function setupOnboardingWindowManager(): OnboardingWindowManager {
|
||||
let window: BrowserWindow | undefined
|
||||
|
||||
const close = () => {
|
||||
if (window && !window.isDestroyed()) {
|
||||
window.close()
|
||||
}
|
||||
}
|
||||
|
||||
const markCompleted = () => {
|
||||
close()
|
||||
}
|
||||
|
||||
const markSkipped = () => {
|
||||
close()
|
||||
}
|
||||
|
||||
const createWindow = async () => {
|
||||
if (window && !window.isDestroyed()) {
|
||||
return window
|
||||
}
|
||||
|
||||
const newWindow = new BrowserWindow({
|
||||
title: 'Welcome to AIRI',
|
||||
width: 500,
|
||||
height: 700,
|
||||
minWidth: 400,
|
||||
minHeight: 500,
|
||||
show: false,
|
||||
icon,
|
||||
resizable: true,
|
||||
frame: false,
|
||||
titleBarStyle: 'hidden',
|
||||
transparent: false,
|
||||
backgroundColor: '#0f0f0f',
|
||||
webPreferences: {
|
||||
preload: join(getElectronMainDirname(), '../preload/index.mjs'),
|
||||
sandbox: false,
|
||||
},
|
||||
})
|
||||
|
||||
// NOTICE: in development mode, open devtools by default
|
||||
if (is.dev) {
|
||||
try {
|
||||
newWindow.webContents.openDevTools({ mode: 'detach' })
|
||||
}
|
||||
catch (err) {
|
||||
console.error('failed to open devtools:', err)
|
||||
}
|
||||
}
|
||||
|
||||
newWindow.on('ready-to-show', () => newWindow.show())
|
||||
newWindow.webContents.setWindowOpenHandler((details) => {
|
||||
shell.openExternal(details.url)
|
||||
return { action: 'deny' }
|
||||
})
|
||||
|
||||
newWindow.on('closed', () => {
|
||||
window = undefined
|
||||
})
|
||||
|
||||
await load(newWindow, withHashRoute(baseUrl(resolve(getElectronMainDirname(), '..', 'renderer')), '/onboarding'))
|
||||
|
||||
// Setup IPC handlers for this window
|
||||
const { context } = createContext(ipcMain, newWindow)
|
||||
|
||||
defineInvokeHandler(context, electronOnboardingClose, () => close())
|
||||
defineInvokeHandler(context, electronOnboardingCompleted, () => markCompleted())
|
||||
defineInvokeHandler(context, electronOnboardingSkipped, () => markSkipped())
|
||||
defineInvokeHandler(context, electronOpenOnboarding, async () => {
|
||||
await showIfNeeded()
|
||||
return true
|
||||
})
|
||||
|
||||
window = newWindow
|
||||
return newWindow
|
||||
}
|
||||
|
||||
async function showIfNeeded() {
|
||||
if (window && !window.isDestroyed()) {
|
||||
window.show()
|
||||
window.focus()
|
||||
return true
|
||||
}
|
||||
|
||||
await createWindow()
|
||||
return true
|
||||
}
|
||||
|
||||
return {
|
||||
getWindow: () => window ?? null,
|
||||
showIfNeeded,
|
||||
close,
|
||||
markCompleted,
|
||||
markSkipped,
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import { useElectronEventaInvoke } from '@proj-airi/electron-vueuse'
|
||||
import { OnboardingScreen } from '@proj-airi/stage-ui/components'
|
||||
import { useOnboardingStore } from '@proj-airi/stage-ui/stores/onboarding'
|
||||
import { useTheme } from '@proj-airi/ui'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import {
|
||||
electronOnboardingClose,
|
||||
electronOnboardingCompleted,
|
||||
electronOnboardingSkipped,
|
||||
} from '../../shared/eventa'
|
||||
|
||||
// const window = useTauriWindow()
|
||||
const onboardingStore = useOnboardingStore()
|
||||
const { isDark } = useTheme()
|
||||
|
||||
function handleSkipped() {
|
||||
// window.closeWindow()
|
||||
const bgClass = computed(() => isDark.value ? 'bg-[#0f0f0f]' : 'bg-white')
|
||||
|
||||
const closeWindow = useElectronEventaInvoke(electronOnboardingClose)
|
||||
const markCompleted = useElectronEventaInvoke(electronOnboardingCompleted)
|
||||
const markSkipped = useElectronEventaInvoke(electronOnboardingSkipped)
|
||||
|
||||
async function handleSkipped() {
|
||||
onboardingStore.markSetupSkipped()
|
||||
await markSkipped()
|
||||
await closeWindow()
|
||||
}
|
||||
|
||||
function handleConfigured() {
|
||||
async function handleConfigured() {
|
||||
onboardingStore.markSetupCompleted()
|
||||
// window.closeWindow()
|
||||
await markCompleted()
|
||||
await closeWindow()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div h-full w-full flex flex-col overflow-x-hidden overflow-y-hidden overscroll-none>
|
||||
<div bg="white dark:#181818" w="100dvw" min-h="12" w-full flex-shrink-0 select-none data-tauri-drag-region />
|
||||
<div w-full flex-1 overflow-y-scroll px-3>
|
||||
<div h-full py-3>
|
||||
<div class="onboarding-root" h-full w-full flex flex-col overflow-x-hidden overflow-y-auto overscroll-none :class="bgClass">
|
||||
<div :class="bgClass" w="100dvw" min-h="12" w-full flex-shrink-0 select-none data-tauri-drag-region />
|
||||
<div class="onboarding-scroll" w-full flex-1 px-3>
|
||||
<div class="onboarding-content" h-full>
|
||||
<OnboardingScreen @skipped="handleSkipped" @configured="handleConfigured" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.onboarding-root {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.onboarding-root::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.onboarding-content {
|
||||
padding: 8px 0 20px 0;
|
||||
}
|
||||
|
||||
.onboarding-scroll {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 20px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<route lang="yaml">
|
||||
meta:
|
||||
layout: plain
|
||||
|
||||
@@ -206,6 +206,12 @@ export const widgetsRemoveEvent = defineEventa<{ id: string }>('eventa:event:ele
|
||||
export const widgetsClearEvent = defineEventa('eventa:event:electron:windows:widgets:clear')
|
||||
export const widgetsUpdateEvent = defineEventa<{ id: string, componentProps?: Record<string, any> }>('eventa:event:electron:windows:widgets:update')
|
||||
|
||||
// Onboarding window events
|
||||
export const electronOnboardingClose = defineInvokeEventa('eventa:invoke:electron:windows:onboarding:close')
|
||||
export const electronOnboardingCompleted = defineInvokeEventa('eventa:invoke:electron:windows:onboarding:completed')
|
||||
export const electronOnboardingSkipped = defineInvokeEventa('eventa:invoke:electron:windows:onboarding:skipped')
|
||||
export const electronOpenOnboarding = defineInvokeEventa<boolean>('eventa:invoke:electron:windows:onboarding:open')
|
||||
|
||||
export const i18nSetLocale = defineInvokeEventa<void, Locale>('eventa:invoke:electron:i18n:set-locale')
|
||||
export const i18nGetLocale = defineInvokeEventa<Locale>('eventa:invoke:electron:i18n:get-locale')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user