diff --git a/api/utils.ts b/api/utils.ts index 5ac7b92..b26b3ea 100644 --- a/api/utils.ts +++ b/api/utils.ts @@ -97,24 +97,47 @@ ajax.interceptors.response.use((ctx) => { export function replacePximgUrlsInString(str: string): string { if (!str.includes('pximg.net')) return str return str - .replace(/https:\/\/i\.pximg\.net\//g, PXIMG_BASEURL_I) - .replace(/https:\/\/s\.pximg\.net\//g, PXIMG_BASEURL_S) + .replaceAll('https://i.pximg.net/', PXIMG_BASEURL_I) + .replaceAll('https://s.pximg.net/', PXIMG_BASEURL_S) } export function replacePximgUrlsInObject( obj: Record | string ): Record | string { if (typeof obj === 'string') return replacePximgUrlsInString(obj) - if (['arraybuffer', 'blob'].includes(obj.constructor.name.toLowerCase())) { - return obj - } - return JSON.parse(safelyStringify(obj), (key: string, val: any) => { - if (typeof val === 'string' && val.includes('pximg.net')) { - return replacePximgUrlsInString(val) + return deepReplaceString(obj, replacePximgUrlsInString) +} + +function isObject(value: any): value is Record { + return typeof value === 'object' && value !== null +} + +export function deepReplaceString( + obj: T, + replacer: (value: string) => string +): T { + if (Array.isArray(obj)) { + return obj.map((value) => + deepReplaceString(value, replacer) + ) as unknown as T + } else if (isObject(obj)) { + if ( + ['arraybuffer', 'blob', 'formdata'].includes( + obj.constructor.name.toLowerCase() + ) + ) { + return obj } - return val - }) + const result: Record = {} + for (const [key, value] of Object.entries(obj)) { + result[key] = deepReplaceString(value, replacer) + } + return result as T + } else if (typeof obj === 'string') { + return replacer(obj) as unknown as T + } + return obj } export function safelyStringify(value: any, space?: number) { diff --git a/components.d.ts b/components.d.ts index 134580e..b2548a5 100644 --- a/components.d.ts +++ b/components.d.ts @@ -25,18 +25,21 @@ declare module 'vue' { LazyLoad: typeof import('./src/components/LazyLoad.vue')['default'] ListLink: typeof import('./src/components/SideNav/ListLink.vue')['default'] NaiveuiProvider: typeof import('./src/components/NaiveuiProvider.vue')['default'] + NAlert: typeof import('naive-ui')['NAlert'] NButton: typeof import('naive-ui')['NButton'] NCard: typeof import('naive-ui')['NCard'] + NCountdown: typeof import('naive-ui')['NCountdown'] NEmpty: typeof import('naive-ui')['NEmpty'] NFlex: typeof import('naive-ui')['NFlex'] + NLi: typeof import('naive-ui')['NLi'] NPagination: typeof import('naive-ui')['NPagination'] - NPaginator: typeof import('naive-ui')['NPaginator'] NProgress: typeof import('./src/components/NProgress.vue')['default'] NSpace: typeof import('naive-ui')['NSpace'] + NStatistic: typeof import('naive-ui')['NStatistic'] NTabPane: typeof import('naive-ui')['NTabPane'] NTabs: typeof import('naive-ui')['NTabs'] - NTabsPane: typeof import('naive-ui')['NTabsPane'] NTag: typeof import('naive-ui')['NTag'] + NUl: typeof import('naive-ui')['NUl'] Placeholder: typeof import('./src/components/Placeholder.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] @@ -45,6 +48,7 @@ declare module 'vue' { SideNav: typeof import('./src/components/SideNav/SideNav.vue')['default'] SiteFooter: typeof import('./src/components/SiteFooter.vue')['default'] SiteHeader: typeof import('./src/components/SiteHeader.vue')['default'] + SiteNoticeBanner: typeof import('./src/components/SiteNoticeBanner.vue')['default'] UgoiraViewer: typeof import('./src/components/UgoiraViewer.vue')['default'] } } diff --git a/src/App.vue b/src/App.vue index 693889d..0782169 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,11 +1,13 @@ @@ -51,7 +53,7 @@ onMounted(async () => { flex-direction: column main - padding-top: 50px + // padding-top: 50px position: relative flex: 1 article diff --git a/src/components/SiteHeader.vue b/src/components/SiteHeader.vue index e920f2b..2ef5a36 100644 --- a/src/components/SiteHeader.vue +++ b/src/components/SiteHeader.vue @@ -142,7 +142,7 @@ onMounted(() => { color: var(--theme-background-color) display: flex align-items: center - position: fixed + position: sticky height: 50px width: 100% box-sizing: border-box diff --git a/src/components/SiteNoticeBanner.vue b/src/components/SiteNoticeBanner.vue new file mode 100644 index 0000000..aa3c56c --- /dev/null +++ b/src/components/SiteNoticeBanner.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/src/plugins/router.ts b/src/plugins/router.ts index 154413a..2b20b72 100644 --- a/src/plugins/router.ts +++ b/src/plugins/router.ts @@ -61,6 +61,11 @@ const routes: RouteRecordRaw[] = [ name: 'about-us', component: () => import('@/view/about.vue'), }, + { + path: '/notifications/2024-04-26', + name: 'notification-2024-04-26', + component: () => import('@/view/notifications/2024-04-26.vue'), + }, { path: '/:pathMatch(.*)*', name: 'not-found', diff --git a/src/view/notifications/2024-04-26.vue b/src/view/notifications/2024-04-26.vue new file mode 100644 index 0000000..74d3b5f --- /dev/null +++ b/src/view/notifications/2024-04-26.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/tsconfig.json b/tsconfig.json index 0ccd2dc..8e35003 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,6 +20,7 @@ "types": ["unplugin-icons/types/vue"] }, "include": [ + "api/**/*.ts", "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx",