diff --git a/app/static/app-icons/gba-emulator.png b/app/static/app-icons/gba-emulator.png
new file mode 100644
index 0000000..39ede5f
Binary files /dev/null and b/app/static/app-icons/gba-emulator.png differ
diff --git a/client/src/lib/Apps/GBAEmulator/GBAEmulator.svelte b/client/src/lib/Apps/GBAEmulator/GBAEmulator.svelte
new file mode 100644
index 0000000..a4295c6
--- /dev/null
+++ b/client/src/lib/Apps/GBAEmulator/GBAEmulator.svelte
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/client/src/lib/Apps/MusicBrowser/MusicBrowser.svelte b/client/src/lib/Apps/MusicBrowser/MusicBrowser.svelte
index 2651963..f31fc83 100644
--- a/client/src/lib/Apps/MusicBrowser/MusicBrowser.svelte
+++ b/client/src/lib/Apps/MusicBrowser/MusicBrowser.svelte
@@ -11,7 +11,7 @@
import { musicPlayerBootupSequence } from '../../Sequences/sequences'
onMount(() => {
- setTimeout(musicPlayerBootupSequence, 5000)
+ musicPlayerBootupSequence()
})
diff --git a/client/src/lib/Apps/appList.ts b/client/src/lib/Apps/appList.ts
index dd4f7e0..2a8da94 100644
--- a/client/src/lib/Apps/appList.ts
+++ b/client/src/lib/Apps/appList.ts
@@ -2,20 +2,45 @@ import Camera from './Camera/Camera.svelte'
import MusicBrowser from './MusicBrowser/MusicBrowser.svelte'
import Settings from './Settings/Settings.svelte'
-export const appList = {
+type Format = 'png' | 'jpg' | 'webp'
+const resolveIconPath = (slug: keyof typeof appList, format: Format) => {
+ return `/static/app-icons/${slug}.${format}`
+}
+
+import GBAEmulator from './GBAEmulator/GBAEmulator.svelte'
+
+interface AppList {
+ [key: string]: {
+ name: string
+ component: any
+ icon: string
+ external: boolean
+ }
+}
+
+export const appList: AppList = {
'camera': {
name: 'Camera',
component: Camera,
- icon: '/static/app-icons/camera.png',
+ icon: resolveIconPath('camera', 'png'),
+ external: false,
},
'media-player': {
name: 'Media Player',
component: MusicBrowser,
- icon: '/static/app-icons/media-player.png',
+ icon: resolveIconPath('media-player', 'png'),
+ external: false,
},
'settings': {
name: 'Settings',
component: Settings,
- icon: '/static/app-icons/settings.webp',
+ icon: resolveIconPath('settings', 'webp'),
+ external: false,
+ },
+ 'gba-emulator': {
+ name: 'GBA Emulator',
+ component: GBAEmulator,
+ icon: resolveIconPath('gba-emulator', 'png'),
+ external: true,
},
}
diff --git a/client/src/lib/Notifications/notifications.ts b/client/src/lib/Notifications/notifications.ts
index f58265a..c1c0296 100644
--- a/client/src/lib/Notifications/notifications.ts
+++ b/client/src/lib/Notifications/notifications.ts
@@ -20,7 +20,7 @@ export class Notifications {
const sendToast = (duration: number) => {
toast.success(message, {
style:
- 'padding: 25px; font-size: 1.5rem; background-color: #15803d; color: #fafafa;',
+ 'padding: 25px; font-size: 1.5rem; background-color: #15803d; color: #fafafa; gap: 0.5rem;',
duration,
...options,
})
@@ -45,7 +45,7 @@ export class Notifications {
const sendToast = (duration: number) => {
toast.error(message, {
style:
- 'padding: 25px; font-size: 1.5rem; background-color: #dc2626; color: #fafafa;',
+ 'padding: 25px; font-size: 1.5rem; background-color: #dc2626; color: #fafafa; gap: 0.5rem;',
duration,
...options,
})
@@ -66,7 +66,7 @@ export class Notifications {
public static info(message: string, options?: NotificationOptions) {
const sendToast = (duration: number) => {
toast(message, {
- style: 'padding: 25px; font-size: 1.5rem;',
+ style: 'padding: 25px; font-size: 1.5rem; gap: 0.5rem;',
icon: InfoIcon,
duration,
...options,
@@ -88,7 +88,7 @@ export class Notifications {
const sendToast = (duration: number) => {
toast(message, {
style:
- 'padding: 25px; font-size: 1.5rem; background-color: #f59e0b; color: #fafafa;',
+ 'padding: 25px; font-size: 1.5rem; background-color: #f59e0b; color: #fafafa; gap: 0.5rem;',
icon: WarnIcon,
duration,
...options,
diff --git a/client/src/lib/Sequences/sequences.ts b/client/src/lib/Sequences/sequences.ts
index 937722f..e07712a 100644
--- a/client/src/lib/Sequences/sequences.ts
+++ b/client/src/lib/Sequences/sequences.ts
@@ -196,6 +196,25 @@ export const infotainmentBootupSequence = async () => {
}, 3000)
}
+/**
+ * Waits for the infotainment system to boot up before executing the given sequence.
+ * Designed to be used by apps who want to play a bootup sequence but not overlap with the default one.
+ * If it's already booted, the sequence will be executed immediately.
+ *
+ * @param sequence - The sequence to execute after infotainment bootup, or immediately it already booted.
+ * @param delay? - The delay in milliseconds to wait if infotainment system is currently booting. Defaults to 5000ms
+ */
+const waitForInfotainmentBootup = (
+ sequence: () => void,
+ delay: number = 5000
+) => {
+ if (!get(sequenceStore).infotainmentStartedFirstTime) {
+ setTimeout(sequence, delay)
+ } else {
+ sequence()
+ }
+}
+
export const musicPlayerBootupSequence = async () => {
if (
get(sequenceStore).musicStartedFirstTime ||
@@ -207,8 +226,28 @@ export const musicPlayerBootupSequence = async () => {
sequenceStore.update('musicStartedFirstTime', true)
- Notifications.info('Downloading copyrighted music...', {
- withAudio: true,
- src: getVoicePath('downloading-copyrighted-music', 'en'),
+ waitForInfotainmentBootup(() => {
+ Notifications.info('Downloading copyrighted music...', {
+ withAudio: true,
+ src: getVoicePath('downloading-copyrighted-music', 'en'),
+ })
+ })
+}
+
+export const gbaEmulatorBootupSequence = async () => {
+ if (
+ get(sequenceStore).gbaEmulatorStartedFirstTime ||
+ get(settingsStore).disableAnnoyances
+ )
+ return
+
+ await tick()
+ sequenceStore.update('gbaEmulatorStartedFirstTime', true)
+
+ waitForInfotainmentBootup(() => {
+ Notifications.info('Loading pirated Nintendo ROMs', {
+ withAudio: true,
+ src: getVoicePath('loading-pirated-nintendo', 'en'),
+ })
})
}
diff --git a/client/src/lib/stores/sequenceStore.ts b/client/src/lib/stores/sequenceStore.ts
index eec35b0..fd17ad3 100644
--- a/client/src/lib/stores/sequenceStore.ts
+++ b/client/src/lib/stores/sequenceStore.ts
@@ -5,11 +5,13 @@ import { writable } from 'svelte/store'
interface SequenceStoreData {
infotainmentStartedFirstTime: boolean
musicStartedFirstTime: boolean
+ gbaEmulatorStartedFirstTime: boolean
}
let defaults: SequenceStoreData = {
infotainmentStartedFirstTime: false, // for infotainment bootup sequence
musicStartedFirstTime: false,
+ gbaEmulatorStartedFirstTime: false,
}
const createSequenceStore = () => {