feature/#215-dexie-caching-implementation
This commit is contained in:
parent
279b9bc7a3
commit
df19c1094c
13
package-lock.json
generated
13
package-lock.json
generated
@ -11,6 +11,7 @@
|
||||
"@vueuse/core": "^10.5.0",
|
||||
"@vueuse/integrations": "^10.5.0",
|
||||
"axios": "^1.7.7",
|
||||
"dexie": "^4.0.8",
|
||||
"phaser": "^3.86.0",
|
||||
"pinia": "^2.1.6",
|
||||
"socket.io-client": "^4.8.0",
|
||||
@ -3549,6 +3550,12 @@
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/dexie": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.8.tgz",
|
||||
"integrity": "sha512-1G6cJevS17KMDK847V3OHvK2zei899GwpDiqfEXHP1ASvme6eWJmAp9AU4s1son2TeGkWmC0g3y8ezOBPnalgQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/didyoumean": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||
@ -3884,9 +3891,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-vue": {
|
||||
"version": "9.29.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.29.0.tgz",
|
||||
"integrity": "sha512-hamyjrBhNH6Li6R1h1VF9KHfshJlKgKEg3ARbGTn72CMNDSMhWbgC7NdkRDEh25AFW+4SDATzyNM+3gWuZii8g==",
|
||||
"version": "9.29.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.29.1.tgz",
|
||||
"integrity": "sha512-MH/MbVae4HV/tM8gKAVWMPJbYgW04CK7SuzYRrlNERpxbO0P3+Zdsa2oAcFBW6xNu7W6lIkGOsFAMCRTYmrlWQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -18,6 +18,7 @@
|
||||
"@vueuse/core": "^10.5.0",
|
||||
"@vueuse/integrations": "^10.5.0",
|
||||
"axios": "^1.7.7",
|
||||
"dexie": "^4.0.8",
|
||||
"phaser": "^3.86.0",
|
||||
"pinia": "^2.1.6",
|
||||
"socket.io-client": "^4.8.0",
|
||||
|
BIN
public/assets/music/click-btn.mp3
Normal file
BIN
public/assets/music/click-btn.mp3
Normal file
Binary file not shown.
BIN
public/assets/music/login.mp3
Normal file
BIN
public/assets/music/login.mp3
Normal file
Binary file not shown.
@ -15,6 +15,7 @@ import GmPanel from '@/components/gameMaster/GmPanel.vue'
|
||||
import Login from '@/screens/Login.vue'
|
||||
import Characters from '@/screens/Characters.vue'
|
||||
import Game from '@/screens/Game.vue'
|
||||
import Loading from '@/screens/Loading.vue'
|
||||
import ZoneEditor from '@/screens/ZoneEditor.vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
@ -22,16 +23,11 @@ const gameStore = useGameStore()
|
||||
const zoneEditorStore = useZoneEditorStore()
|
||||
|
||||
const currentScreen = computed(() => {
|
||||
if (!gameStore.isAssetsLoaded) return Loading
|
||||
if (!gameStore.connection) return Login
|
||||
if (!gameStore.token) return Login
|
||||
if (!gameStore.character) return Characters
|
||||
if (zoneEditorStore.active) return ZoneEditor
|
||||
return Game
|
||||
})
|
||||
|
||||
// Disable right click
|
||||
addEventListener('contextmenu', (event) => event.preventDefault())
|
||||
|
||||
// Disable pinch zoom
|
||||
addEventListener('gesturestart', (event) => event.preventDefault())
|
||||
</script>
|
||||
|
@ -14,11 +14,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeMount, onUnmounted, ref } from 'vue'
|
||||
import { useScene } from 'phavuer'
|
||||
import { onUnmounted, ref } from 'vue'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
|
||||
import { loadAssets } from '@/composables/zoneComposable'
|
||||
import { type Zone } from '@/types'
|
||||
|
||||
// Components
|
||||
@ -32,7 +30,6 @@ import Tiles from '@/components/gameMaster/zoneEditor/Tiles.vue'
|
||||
import Objects from '@/components/gameMaster/zoneEditor/Objects.vue'
|
||||
import EventTiles from '@/components/gameMaster/zoneEditor/EventTiles.vue'
|
||||
|
||||
const scene = useScene()
|
||||
const gameStore = useGameStore()
|
||||
const zoneEditorStore = useZoneEditorStore()
|
||||
|
||||
@ -62,11 +59,6 @@ function save() {
|
||||
})
|
||||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await gameStore.fetchAllZoneAssets()
|
||||
await loadAssets(scene)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
zoneEditorStore.reset()
|
||||
})
|
||||
|
@ -5,19 +5,16 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useScene } from 'phavuer'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { useZoneStore } from '@/stores/zoneStore'
|
||||
import { onBeforeMount, onBeforeUnmount, ref } from 'vue'
|
||||
import { onBeforeUnmount, ref } from 'vue'
|
||||
import type { Character as CharacterT, Zone as ZoneT, ExtendedCharacter as ExtendedCharacterT } from '@/types'
|
||||
import Tiles from '@/components/zone/Tiles.vue'
|
||||
import Objects from '@/components/zone/Objects.vue'
|
||||
import Characters from '@/components/zone/Characters.vue'
|
||||
import { loadAssets } from '@/composables/zoneComposable'
|
||||
|
||||
const gameStore = useGameStore()
|
||||
const zoneStore = useZoneStore()
|
||||
const scene = useScene()
|
||||
|
||||
const tileMap = ref(null as Phaser.Tilemaps.Tilemap | null)
|
||||
|
||||
@ -28,13 +25,6 @@ type zoneLoadData = {
|
||||
|
||||
// Event listeners
|
||||
gameStore.connection!.on('zone:character:teleport', async (data: zoneLoadData) => {
|
||||
/**
|
||||
* This is the cause of the bug
|
||||
*/
|
||||
// Fetch assets for new zone
|
||||
await gameStore.fetchZoneAssets(data.zone.id)
|
||||
await loadAssets(scene)
|
||||
|
||||
/**
|
||||
* @TODO : Update character via global event server-side, remove this and listen for it somewhere not here
|
||||
*/
|
||||
@ -61,16 +51,9 @@ gameStore.connection!.on('character:move', (data: ExtendedCharacterT) => {
|
||||
zoneStore.updateCharacter(data)
|
||||
})
|
||||
|
||||
onBeforeMount(() => {
|
||||
gameStore.connection!.emit('zone:character:join', async (response: zoneLoadData) => {
|
||||
// Fetch assets for new zone
|
||||
await gameStore.fetchZoneAssets(response.zone.id)
|
||||
await loadAssets(scene)
|
||||
|
||||
// Set zone and characters
|
||||
zoneStore.setZone(response.zone)
|
||||
zoneStore.setCharacters(response.characters)
|
||||
})
|
||||
gameStore.connection!.emit('zone:character:join', async (response: zoneLoadData) => {
|
||||
zoneStore.setZone(response.zone)
|
||||
zoneStore.setCharacters(response.characters)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
|
@ -4,6 +4,7 @@ import TilemapLayer = Phaser.Tilemaps.TilemapLayer
|
||||
import Tileset = Phaser.Tilemaps.Tileset
|
||||
import Tile = Phaser.Tilemaps.Tile
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { useAssetManager } from '@/utilities/assets'
|
||||
|
||||
export function getTile(layer: TilemapLayer | Tilemap, x: number, y: number): Tile | undefined {
|
||||
const tile = layer.getTileAtWorldXY(x, y)
|
||||
@ -89,17 +90,35 @@ export const clearAssets = (scene: Phaser.Scene) => {
|
||||
|
||||
export const loadAssets = (scene: Phaser.Scene): Promise<void> => {
|
||||
return new Promise((resolve) => {
|
||||
const gameStore = useGameStore()
|
||||
let assetManager = useAssetManager
|
||||
let addedLoad = false
|
||||
|
||||
gameStore.assets.forEach((asset) => {
|
||||
if (scene.load.textureManager.exists(asset.key)) return
|
||||
addedLoad = true
|
||||
if (asset.group === 'sprite_animations') {
|
||||
scene.load.spritesheet(asset.key, config.server_endpoint + asset.url, { frameWidth: asset.frameWidth ?? 0, frameHeight: asset.frameHeight ?? 0 })
|
||||
} else {
|
||||
scene.load.image(asset.key, config.server_endpoint + asset.url)
|
||||
}
|
||||
assetManager.getAssetsByGroup('tiles').then((assets) => {
|
||||
assets.forEach((asset) => {
|
||||
if (scene.load.textureManager.exists(asset.key)) return
|
||||
addedLoad = true
|
||||
scene.textures.addBase64(asset.key, asset.data)
|
||||
})
|
||||
})
|
||||
|
||||
assetManager.getAssetsByGroup('objects').then((assets) => {
|
||||
assets.forEach((asset) => {
|
||||
if (scene.load.textureManager.exists(asset.key)) return
|
||||
addedLoad = true
|
||||
scene.textures.addBase64(asset.key, asset.data)
|
||||
})
|
||||
})
|
||||
|
||||
assetManager.getAssetsByGroup('sprites').then((assets) => {
|
||||
assets.forEach((asset) => {
|
||||
if (scene.load.textureManager.exists(asset.key)) return
|
||||
let img = new Image();
|
||||
img.onload = () => {
|
||||
scene.textures.addSpriteSheet(asset.key, img, { frameWidth: asset.frameWidth ?? 0, frameHeight: asset.frameHeight ?? 0 })
|
||||
};
|
||||
img.src = asset.data;
|
||||
addedLoad = true
|
||||
})
|
||||
})
|
||||
|
||||
if (addedLoad) {
|
||||
|
@ -114,13 +114,7 @@ gameStore.connection?.on('character:list', (data: any) => {
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
/**
|
||||
* Fetch sprite assets from the server
|
||||
* This is done here because phaser needs it in createScene in Game.vue.
|
||||
*/
|
||||
await gameStore.fetchSpriteAssets()
|
||||
|
||||
// wait 0.5 sec
|
||||
// wait 0.75 sec
|
||||
setTimeout(() => {
|
||||
gameStore.connection?.emit('character:list')
|
||||
isLoading.value = false
|
||||
|
@ -19,11 +19,12 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup lang="ts" async>
|
||||
import config from '@/config'
|
||||
import 'phaser'
|
||||
import { ref, onBeforeUnmount } from 'vue'
|
||||
import { Game, Scene } from 'phavuer'
|
||||
import { useAssetManager } from '@/utilities/assets'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import Menu from '@/components/gui/Menu.vue'
|
||||
import ExpBar from '@/components/gui/ExpBar.vue'
|
||||
@ -36,6 +37,7 @@ import Effects from '@/components/Effects.vue'
|
||||
import { loadAssets } from '@/composables/zoneComposable'
|
||||
import Minimap from '@/components/gui/Minimap.vue'
|
||||
|
||||
const assetManager = useAssetManager
|
||||
const gameStore = useGameStore()
|
||||
const isLoaded = ref(false)
|
||||
|
||||
@ -120,16 +122,16 @@ const createScene = async (scene: Phaser.Scene) => {
|
||||
* Create sprite animations
|
||||
* This is done here because phaser forces us to
|
||||
*/
|
||||
gameStore.assets.forEach((asset) => {
|
||||
if (asset.group !== 'sprite_animations') return
|
||||
|
||||
scene.anims.create({
|
||||
key: asset.key,
|
||||
frameRate: 7,
|
||||
frames: scene.anims.generateFrameNumbers(asset.key, { start: 0, end: asset.frameCount! - 1 }),
|
||||
repeat: -1
|
||||
})
|
||||
})
|
||||
// assetManager.getAssetsByGroup('sprite_animations').then((assets) => {
|
||||
// assets.forEach((asset) => {
|
||||
// scene.anims.create({
|
||||
// key: asset.key,
|
||||
// frameRate: 7,
|
||||
// frames: scene.anims.generateFrameNumbers(asset.key, { start: 0, end: asset.frameCount! - 1 }),
|
||||
// repeat: -1
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
|
78
src/screens/Loading.vue
Normal file
78
src/screens/Loading.vue
Normal file
@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div class="flex flex-col justify-center items-center h-dvh relative">
|
||||
<div v-if="!isLoaded" class="w-20 h-20 rounded-full border-4 border-solid border-gray-300 border-t-transparent animate-spin"></div>
|
||||
<button v-else @click="continueBtnClick" class="w-20 h-20 rounded-full bg-gray-500 flex items-center justify-center hover:bg-gray-600 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
<div v-if="!isLoaded" class="text-center mt-6">
|
||||
<h1 class="text-2xl font-bold">Loading...</h1>
|
||||
<p class="text-gray-400">Please wait while we load the assets.</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" async>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import config from '@/config'
|
||||
import type { AssetT as ServerAsset } from '@/types'
|
||||
import { useAssetManager } from '@/utilities/assets'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
|
||||
const gameStore = useGameStore();
|
||||
const assetManager = useAssetManager;
|
||||
const isLoaded = ref(false)
|
||||
|
||||
async function getAssets() {
|
||||
return fetch(config.server_endpoint + '/assets/')
|
||||
.then((response) => {
|
||||
if (!response.ok) throw new Error('Failed to fetch assets')
|
||||
console.log(response)
|
||||
return response.json()
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error fetching assets:', error)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
async function loadAssetsIntoAssetManager(assets: ServerAsset[]): Promise<void> {
|
||||
for (const asset of assets) {
|
||||
// Check if the asset is already loaded
|
||||
const existingAsset = await assetManager.getAsset(asset.key);
|
||||
|
||||
// Check if the asset needs to be updated
|
||||
if (!existingAsset || new Date(asset.updatedAt) > new Date(existingAsset.updatedAt)) {
|
||||
|
||||
// Check if the asset is already loaded, if so, delete it
|
||||
if (existingAsset) {
|
||||
await assetManager.deleteAsset(asset.key);
|
||||
}
|
||||
|
||||
// Add the asset to the asset manager
|
||||
await assetManager.addAsset(
|
||||
asset.key,
|
||||
asset.url,
|
||||
asset.group,
|
||||
asset.updatedAt,
|
||||
asset.frameCount,
|
||||
asset.frameWidth,
|
||||
asset.frameHeight
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function continueBtnClick() {
|
||||
gameStore.isAssetsLoaded = true
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const assets = await getAssets()
|
||||
if (assets) {
|
||||
await loadAssetsIntoAssetManager(assets)
|
||||
isLoaded.value = true
|
||||
}
|
||||
})
|
||||
</script>
|
@ -16,7 +16,6 @@ import { Game, Scene } from 'phavuer'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
|
||||
import ZoneEditor from '@/components/gameMaster/zoneEditor/ZoneEditor.vue'
|
||||
import { loadAssets } from '@/composables/zoneComposable'
|
||||
|
||||
const gameStore = useGameStore()
|
||||
const zoneEditorStore = useZoneEditorStore()
|
||||
@ -93,11 +92,6 @@ const preloadScene = async (scene: Phaser.Scene) => {
|
||||
scene.load.image('TELEPORT', '/assets/zone/tp_tile.png')
|
||||
scene.load.image('blank_tile', '/assets/zone/blank_tile.png')
|
||||
scene.load.image('waypoint', '/assets/waypoint.png')
|
||||
|
||||
/**
|
||||
* Load the assets into the Phaser scene
|
||||
*/
|
||||
await loadAssets(scene)
|
||||
}
|
||||
|
||||
const createScene = async (scene: Phaser.Scene) => {
|
||||
|
@ -1,9 +1,8 @@
|
||||
import axios from 'axios'
|
||||
import config from '@/config'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { useCookies } from '@vueuse/integrations/useCookies'
|
||||
|
||||
export async function register(username: string, password: string, gameStore = useGameStore()) {
|
||||
export async function register(username: string, password: string) {
|
||||
try {
|
||||
const response = await axios.post(`${config.server_endpoint}/register`, { username, password })
|
||||
useCookies().set('token', response.data.token as string)
|
||||
@ -13,13 +12,13 @@ export async function register(username: string, password: string, gameStore = u
|
||||
}
|
||||
}
|
||||
|
||||
export async function login(username: string, password: string, gameStore = useGameStore()) {
|
||||
export async function login(username: string, password: string) {
|
||||
try {
|
||||
const response = await axios.post(`${config.server_endpoint}/login`, { username, password })
|
||||
useCookies().set('token', response.data.token as string, {
|
||||
// for whole domain
|
||||
// @TODO : #190
|
||||
domain: window.location.hostname.split('.').slice(-2).join('.')
|
||||
// domain: window.location.hostname.split('.').slice(-2).join('.')
|
||||
})
|
||||
return { success: true, token: response.data.token }
|
||||
} catch (error: any) {
|
||||
|
@ -7,9 +7,8 @@ import { useCookies } from '@vueuse/integrations/useCookies'
|
||||
export const useGameStore = defineStore('game', {
|
||||
state: () => {
|
||||
return {
|
||||
loginMessage: null as string | null,
|
||||
notifications: [] as Notification[],
|
||||
assets: [] as Asset[],
|
||||
isAssetsLoaded: false,
|
||||
token: '' as string | null,
|
||||
connection: null as Socket | null,
|
||||
user: null as User | null,
|
||||
@ -47,54 +46,6 @@ export const useGameStore = defineStore('game', {
|
||||
removeNotification(id: string) {
|
||||
this.notifications = this.notifications.filter((notification: Notification) => notification.id !== id)
|
||||
},
|
||||
setAssets(assets: Asset[]) {
|
||||
this.assets = assets
|
||||
},
|
||||
addAsset(asset: Asset) {
|
||||
this.assets.push(asset)
|
||||
},
|
||||
addAssets(assets: Asset[]) {
|
||||
this.assets = this.assets.concat(assets)
|
||||
},
|
||||
async fetchSpriteAssets() {
|
||||
return fetch(config.server_endpoint + '/assets/sprites')
|
||||
.then((response) => response.json())
|
||||
.then((assets) => {
|
||||
// Only add the sprites that are not already in the store
|
||||
this.addAssets(assets.filter((asset: Asset) => !this.getAssetByKey(asset.key)))
|
||||
return true
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error fetching assets:', error)
|
||||
return false
|
||||
})
|
||||
},
|
||||
async fetchZoneAssets(zoneId: number) {
|
||||
return fetch(config.server_endpoint + '/assets/zone/' + zoneId)
|
||||
.then((response) => response.json())
|
||||
.then((assets) => {
|
||||
// Only add the zones that are not already in the store
|
||||
this.addAssets(assets.filter((asset: Asset) => !this.getAssetByKey(asset.key)))
|
||||
return true
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error fetching assets:', error)
|
||||
return false
|
||||
})
|
||||
},
|
||||
async fetchAllZoneAssets() {
|
||||
return fetch(config.server_endpoint + '/assets/zone')
|
||||
.then((response) => response.json())
|
||||
.then((assets) => {
|
||||
// Only add the zones that are not already in the store
|
||||
this.addAssets(assets.filter((asset: Asset) => !this.getAssetByKey(asset.key)))
|
||||
return true
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error fetching assets:', error)
|
||||
return false
|
||||
})
|
||||
},
|
||||
setToken(token: string) {
|
||||
this.token = token
|
||||
},
|
||||
@ -152,15 +103,15 @@ export const useGameStore = defineStore('game', {
|
||||
})
|
||||
},
|
||||
disconnectSocket() {
|
||||
if (this.connection) this.connection.disconnect()
|
||||
this.connection?.disconnect()
|
||||
|
||||
useCookies().remove('token', {
|
||||
// for whole domain
|
||||
// @TODO : #190
|
||||
domain: window.location.hostname.split('.').slice(-2).join('.')
|
||||
// domain: window.location.hostname.split('.').slice(-2).join('.')
|
||||
})
|
||||
|
||||
this.assets = []
|
||||
// this.isAssetsLoaded = false
|
||||
this.connection = null
|
||||
this.token = null
|
||||
this.user = null
|
||||
|
@ -4,10 +4,11 @@ export type Notification = {
|
||||
message?: string
|
||||
}
|
||||
|
||||
export type Asset = {
|
||||
export type AssetT = {
|
||||
key: string
|
||||
url: string
|
||||
group: 'tiles' | 'objects' | 'sprites' | 'sprite_animations' | 'sound' | 'music' | 'ui' | 'font' | 'other'
|
||||
updatedAt: Date
|
||||
frameCount?: number
|
||||
frameWidth?: number
|
||||
frameHeight?: number
|
||||
|
101
src/utilities/assets.ts
Normal file
101
src/utilities/assets.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import config from '@/config';
|
||||
import Dexie from 'dexie';
|
||||
|
||||
interface Asset {
|
||||
key: string;
|
||||
data: Blob;
|
||||
group: string;
|
||||
updatedAt: Date;
|
||||
frameCount?: number;
|
||||
frameWidth?: number;
|
||||
frameHeight?: number;
|
||||
}
|
||||
|
||||
interface AssetWithUrl extends Omit<Asset, 'data'> {
|
||||
data: string;
|
||||
}
|
||||
|
||||
class AssetManager extends Dexie {
|
||||
assets!: Dexie.Table<Asset, string>;
|
||||
|
||||
constructor() {
|
||||
super('AssetManager');
|
||||
this.version(1).stores({
|
||||
assets: 'key, group'
|
||||
});
|
||||
}
|
||||
|
||||
async getAssetCount(): Promise<number> {
|
||||
try {
|
||||
const count = await this.assets.count();
|
||||
return count;
|
||||
} catch (error) {
|
||||
console.error('Failed to get asset count:', error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
async addAsset(
|
||||
key: string,
|
||||
url: string,
|
||||
group: string,
|
||||
updatedAt: Date,
|
||||
frameCount?: number,
|
||||
frameWidth?: number,
|
||||
frameHeight?: number
|
||||
): Promise<void> {
|
||||
try {
|
||||
const response = await fetch(config.server_endpoint + url);
|
||||
const blob = await response.blob();
|
||||
await this.assets.put({ key, data: blob, group, updatedAt, frameCount, frameWidth, frameHeight });
|
||||
} catch (error) {
|
||||
console.error(`Failed to add asset ${key}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
async getAsset(key: string): Promise<AssetWithUrl | null> {
|
||||
try {
|
||||
const asset = await this.assets.get(key);
|
||||
if (asset) {
|
||||
return {
|
||||
...asset,
|
||||
data: URL.createObjectURL(asset.data)
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to retrieve asset ${key}:`, error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async getAssetsByGroup(group: string): Promise<AssetWithUrl[]> {
|
||||
try {
|
||||
const assets = await this.assets.where('group').equals(group).toArray();
|
||||
return assets.map(asset => ({
|
||||
...asset,
|
||||
data: URL.createObjectURL(asset.data)
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error(`Failed to retrieve assets for group ${group}:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async clearAssets(): Promise<void> {
|
||||
try {
|
||||
await this.assets.clear();
|
||||
} catch (error) {
|
||||
console.error('Failed to clear assets:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteAsset(key: string): Promise<void> {
|
||||
try {
|
||||
await this.assets.delete(key);
|
||||
} catch (error) {
|
||||
console.error(`Failed to delete asset ${key}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const useAssetManager = new AssetManager();
|
Loading…
x
Reference in New Issue
Block a user