Skip to content

Commit

Permalink
fix(web): Import from indexedDB on demand instead of full loading
Browse files Browse the repository at this point in the history
  • Loading branch information
Chilfish committed Mar 1, 2024
1 parent ef14d67 commit 3a7c18a
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 90 deletions.
17 changes: 5 additions & 12 deletions apps/web/src/Index.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
<script setup lang="ts">
import type { Post } from '@types'
const postStore = usePostStore()
onBeforeMount(async () => {
const data = await indexDB.getItem<Post[]>('posts')
postStore.set(data ?? [])
const user = postStore.posts[0]?.user
localStorage.setItem('user', JSON.stringify({
uid: user?.id,
name: user?.screen_name,
}))
onMounted(async () => {
const ids = await indexDB.getItem<string[]>('ids')
postStore.ids = ids ?? []
postStore.total = ids?.length ?? 0
})
</script>

Expand All @@ -26,7 +19,7 @@ onBeforeMount(async () => {
</n-dialog> -->

<Preview
v-if="$route.params.page"
v-if="postStore.ids.length > 0"
/>
</main>
</template>
47 changes: 23 additions & 24 deletions packages/core/src/stores/post.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { defineStore } from 'pinia'
import type { Post } from '@types'
import { getMany, set as setDB, setMany } from 'idb-keyval'

export const usePostStore = defineStore('post', () => {
const posts = ref([] as Post[])
const ids = ref([] as string[])

const resultPosts = ref([] as Post[])

const url = document.location
Expand All @@ -15,12 +17,12 @@ export const usePostStore = defineStore('post', () => {
const pageSize = ref(_pageSize || 20) // 每页显示的帖子数量 ppp

// 总帖子数
const total = ref(posts.value.length)
const total = ref(ids.value.length)

// 监听搜索结果, 更新总帖子数
watch(resultPosts, () => {
total.value = resultPosts.value.length === 0
? posts.value.length
? ids.value.length
: resultPosts.value.length
})

Expand All @@ -29,7 +31,6 @@ export const usePostStore = defineStore('post', () => {
})

function reset() {
posts.value = []
resultPosts.value = []
viewImg.value = imgViewSrc
curPage.value = 1
Expand All @@ -41,41 +42,40 @@ export const usePostStore = defineStore('post', () => {
* @param data
* @param replace
*/
function set(
async function set(
data: Post[],
replace = false,
) {
if (!data[0]?.user)
throw new Error('数据格式错误')
throw new Error('数据格式错误,可能要重新导入')

let posts = data
if (!replace)
posts = Array.from(new Set(posts.concat(data)))

const _ids = posts.map(post => `post-${post.mblogid}`)
ids.value = _ids

if (replace) {
posts.value = data
}
else {
posts.value = posts.value.concat(data)
posts.value = Array.from(new Set(posts.value))
}
await setDB('ids', _ids)
await setMany(posts.map(post => [`post-${post.mblogid}`, post]))

total.value = data.length
}

function get(page?: number): Post[] {
async function get(page?: number) {
let p = page
if (!p)
p = curPage.value
const sliceDis = [(p - 1) * pageSize.value, p * pageSize.value]

return resultPosts.value.length === 0
? posts.value.slice(...sliceDis)
: resultPosts.value.slice(...sliceDis)
}

function getById(id: number): Post[] {
return posts.value.filter(post => post.id === id)
return await getMany<Post>(ids.value.slice(...sliceDis))
}

// TODO: 优化
async function searchText(p: string): Promise<Post[]> {
const res = posts.value.filter((post) => {
const posts = await getMany<Post>(ids.value)

const res = posts.filter((post) => {
const word = p.toLowerCase().trim().replace(/ /g, '')
const regex = new RegExp(word, 'igm')
return regex.test(post.text)
Expand All @@ -88,7 +88,7 @@ export const usePostStore = defineStore('post', () => {
}

return {
posts,
ids,
resultPosts,
viewImg,
total,
Expand All @@ -98,7 +98,6 @@ export const usePostStore = defineStore('post', () => {

get,
set,
getById,
reset,

searchText,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/utils/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import { createStorage } from 'unstorage'
import indexedDbDriver from 'unstorage/drivers/indexedb'

export const indexDB = createStorage({
driver: indexedDbDriver({ base: 'app:' }),
driver: indexedDbDriver({ }),
})
18 changes: 8 additions & 10 deletions packages/ui/src/Preview.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
<script setup lang="ts">
const postStore = usePostStore()
import { useRoute } from 'vue-router'
import type { Post } from '@types'
const posts = computed(() => postStore.get())
const loaded = ref(false)
const postStore = usePostStore()
const posts = ref([] as Post[])
const route = useRoute()
watch(posts, async () => {
if (posts.value.length === 0 || !loaded.value)
return
await delay(4000)
})
watchImmediate(() => route.params, async () => {
posts.value = await postStore.get()
onMounted(() => {
loaded.value = true
await delay(3000)
})
</script>

Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/Profile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ const props = defineProps<{
user: User
}>()
const me = useStorage('meta', { uid: '' })
const isMe = computed(() => {
const user = JSON.parse(localStorage.getItem('user')!)
return user.uid === props.user.id
return me.value.uid === props.user.id
})
const avatar = computed(() => {
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/post/Card.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { CardInfo } from '@core'
import type { CardInfo } from '@types'
defineProps<{
card: CardInfo
Expand Down
105 changes: 65 additions & 40 deletions packages/ui/src/settings/base.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<script setup lang="ts">
import type { UploadCustomRequestOptions } from 'naive-ui'
import type { Post } from '@types'
import { clear as clearDB, getMany } from 'idb-keyval'
import { destr } from 'destr'
import { saveAs } from 'file-saver'
Expand All @@ -20,10 +22,15 @@ function onImportData({ file }: UploadCustomRequestOptions) {
const data = content.replace('export const _ = ', '')
try {
postStore.set(destr(data, { strict: true }), coverMode.value)
await indexDB.setItem('posts', postStore.posts)
const posts = destr<Post[]>(data, { strict: true })
await postStore.set(posts, coverMode.value)
message.success('导入成功')
useStorage('meta', {
uid: posts[0]?.user?.id,
name: posts[0]?.user?.screen_name,
})
message.success(`导入成功,共导入 ${posts.length} 条数据`)
}
catch (e) {
message.error('导入失败,请检查文件内容是否正确')
Expand All @@ -32,21 +39,26 @@ function onImportData({ file }: UploadCustomRequestOptions) {
}
}
function exportData() {
const data = postStore.posts
async function exportData() {
const data = await getMany(postStore.ids).then(res => res)
if (!data[0]) {
message.warning('没有数据可以导出')
return
}
const blob = new Blob(
[`export const _ = ${JSON.stringify(data)}`],
[JSON.stringify(data)],
{ type: 'text/plain' },
)
saveAs(blob, 'data.mjs')
saveAs(blob, 'weibo-data.json')
message.success('导出成功')
}
</script>

<template>
<main
class="flex flex-col items-start gap-4 overflow-auto"
class="flex flex-col items-start gap-4"
>
<n-form>
<n-form-item
Expand Down Expand Up @@ -87,48 +99,61 @@ function exportData() {
</n-form-item>
</n-form>

<div class="flex flex-col">
<div class="w-full flex flex-col">
<p class="settings-title">
导入数据
</p>
<p class="text-3.5 text-gray">
导入从脚本导出的 <code>data.mjs</code> 数据文件
导入/导出 数据
</p>

<div class="my-4">
<span>
{{ coverMode ? '覆盖模式(将覆盖本地所有数据)' : '合并模式(只追加合并新数据)' }}
<div class="my-4 min-w-fit">
<span class="mr-4">
导入方式:{{ coverMode ? '覆盖模式(将覆盖本地所有数据)' : '合并模式(只追加合并新数据)' }}
</span>
<n-switch
v-model:value="coverMode"
/>
</div>
<n-upload
:custom-request="onImportData"
:max="1"
accept=".mjs,.json"
directory-dnd
>
<n-button>
点击导入
</n-button>
</n-upload>
</div>

<div class="flex flex-col">
<p class="settings-title">
导出数据
</p>
<p class="text-3.5 text-gray">
导出当前数据到 <code>data.mjs</code> 数据文件
</p>
<div class="flex flex-wrap items-center gap-6">
<n-upload
:custom-request="onImportData"
:show-file-list="false"
accept=".mjs,.json"
directory-dnd
class="w-fit"
>
<n-button>
点击导入
</n-button>
</n-upload>

<n-button
class="w-fit"
@click="exportData"
>
点击导出
</n-button>

<n-button
class="mt-4 w-fit"
@click="exportData"
>
点击导出
</n-button>
<n-popconfirm
@positive-click="async () => {
clearDB().then(() => {
message.success('清空成功')
}).catch((e) => {
console.error(`清空失败: ${e}`)
message.error('清空失败')
})
}"
>
<template #trigger>
<n-button
class="w-fit bg-red"
type="error"
>
清空本地数据
</n-button>
</template>
确认清空本地数据?你仍可以通过导入功能恢复数据。
</n-popconfirm>
</div>
</div>

<slot />
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/src/shared.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@

.n-dialog.n-modal {
width: auto !important;
max-height: 90dvh;
overflow-y: auto;
/* min-width: 446px; */
}

Expand Down
1 change: 1 addition & 0 deletions types/auto-components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ declare module 'vue' {
NInput: typeof import('naive-ui')['NInput']
NModalProvider: typeof import('naive-ui')['NModalProvider']
NPagination: typeof import('naive-ui')['NPagination']
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
NRadio: typeof import('naive-ui')['NRadio']
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
NSwitch: typeof import('naive-ui')['NSwitch']
Expand Down

0 comments on commit 3a7c18a

Please sign in to comment.