diff --git a/packages/plugin/assets/locales/en/translation.json b/packages/plugin/assets/locales/en/translation.json
index 1763228..3cf9ed5 100644
--- a/packages/plugin/assets/locales/en/translation.json
+++ b/packages/plugin/assets/locales/en/translation.json
@@ -52,5 +52,7 @@
"open-showcase-page": "Open Showcase Page",
"open-home-page": "Open Home Page",
"save-page-not-available": "Save Page (Not Available)",
- "language": "Language:"
+ "language": "Language:",
+ "loading-archived-list": "Loading Archived List",
+ "no-archived-pages": "No Archived Pages"
}
diff --git a/packages/plugin/assets/locales/zh-CN/translation.json b/packages/plugin/assets/locales/zh-CN/translation.json
index d56c77b..185dcdb 100644
--- a/packages/plugin/assets/locales/zh-CN/translation.json
+++ b/packages/plugin/assets/locales/zh-CN/translation.json
@@ -52,5 +52,7 @@
"open-showcase-page": "打开 Showcase 页面",
"open-home-page": "打开首页",
"save-page-not-available": "保存页面(不可用)",
- "language": "语言: "
+ "language": "语言: ",
+ "loading-archived-list": "加载已存档列表中",
+ "no-archived-pages": "没有存档页面"
}
diff --git a/packages/plugin/background/background.ts b/packages/plugin/background/background.ts
index cdb2630..532722f 100644
--- a/packages/plugin/background/background.ts
+++ b/packages/plugin/background/background.ts
@@ -206,3 +206,16 @@ onMessage('generate-tag', async ({ data: { title, pageDesc, tagLanguage, preferr
tags,
}
})
+
+onMessage('query-by-url', async ({ data: { pageUrl } }) => {
+ const pages = await request('/pages/query_by_url', {
+ method: 'POST',
+ body: JSON.stringify({ pageUrl }),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+ return {
+ pages,
+ }
+})
diff --git a/packages/plugin/popup/components/PluginHomePage.tsx b/packages/plugin/popup/components/PluginHomePage.tsx
index 2097c9e..c76cda5 100644
--- a/packages/plugin/popup/components/PluginHomePage.tsx
+++ b/packages/plugin/popup/components/PluginHomePage.tsx
@@ -6,6 +6,7 @@ import { isNil } from '@web-archive/shared/utils'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@web-archive/shared/components/tooltip'
import { useTranslation } from 'react-i18next'
import { getCurrentTab } from '../utils/tab'
+import SavedPageList from './SavedPageList'
import { ThemeToggle } from '~/popup/components/ThemeToggle'
import type { PageType } from '~/popup/PopupPage'
@@ -124,6 +125,13 @@ function PluginHomePage({ setActivePage }: PluginHomePageProps) {
!saveAvailabel ? t('save-page-not-available') : t('save-page')
}
+ {
+ saveAvailabel && (
+
+
+ )
+ }
+
)
}
diff --git a/packages/plugin/popup/components/SavedPageList.tsx b/packages/plugin/popup/components/SavedPageList.tsx
new file mode 100644
index 0000000..eacd12d
--- /dev/null
+++ b/packages/plugin/popup/components/SavedPageList.tsx
@@ -0,0 +1,75 @@
+import { isNil } from '@web-archive/shared/utils'
+import { sendMessage } from 'webext-bridge/popup'
+import { useRequest } from 'ahooks'
+import { LoaderCircle } from 'lucide-react'
+import { useTranslation } from 'react-i18next'
+import { getCurrentTab } from '../utils/tab'
+
+async function getSavedPages() {
+ const currentTab = await getCurrentTab()
+ if (isNil(currentTab?.id) || isNil(currentTab.url)) {
+ return []
+ }
+
+ const { pages } = await sendMessage('query-by-url', {
+ pageUrl: currentTab.url,
+ })
+
+ return pages
+}
+
+function formatTime(date: string) {
+ const timestamp = Date.parse(`${date}Z`)
+ const dateObj = new Date(timestamp)
+ return dateObj.toLocaleString()
+}
+
+function SavedPageList() {
+ const { t } = useTranslation()
+ const { data: savedPages, loading } = useRequest(getSavedPages)
+
+ async function handlePageClick(pageId: number) {
+ const { serverUrl } = await sendMessage('get-server-url', {})
+ window.open(`${serverUrl}/#/page/${pageId}`, '_blank')
+ }
+
+ if (loading) {
+ return (
+
+
+
{t('loading-archived-list')}
+
+ )
+ }
+
+ if (!savedPages?.length) {
+ return (
+
+
{t('no-archived-pages')}
+
+ )
+ }
+
+ return (
+
+
+ {
+ savedPages?.map((page) => {
+ return (
+
handlePageClick(page.id)}
+ >
+
{page.title}
+
{formatTime(page.createdAt)}
+
+ )
+ })
+ }
+
+
+ )
+}
+
+export default SavedPageList
diff --git a/packages/plugin/shim.d.ts b/packages/plugin/shim.d.ts
index 2f15346..f162177 100644
--- a/packages/plugin/shim.d.ts
+++ b/packages/plugin/shim.d.ts
@@ -1,5 +1,5 @@
import type { ProtocolWithReturn } from 'webext-bridge'
-import type { AITagConfig, Tag } from '@web-archive/shared/types'
+import type { AITagConfig, Page, Tag } from '@web-archive/shared/types'
import type { SeriableSingleFileTask } from './background/processor'
import type { LoadStage, SingleFileSetting } from '~/utils/singleFile'
@@ -41,5 +41,12 @@ declare module 'webext-bridge' {
'scrape-available': ProtocolWithReturn<{ tabId: number }, { available: boolean }>
'get-ai-tag-config': ProtocolWithReturn<{}, { aiTagConfig: AITagConfig }>
'generate-tag': ProtocolWithReturn
+ 'query-by-url': ProtocolWithReturn<{ pageUrl: string }, {
+ pages: Array<{
+ id: number
+ title: string
+ createdAt: string
+ }>
+ }>
}
}
diff --git a/packages/server/src/api/pages.ts b/packages/server/src/api/pages.ts
index 1f8bd53..baf630f 100644
--- a/packages/server/src/api/pages.ts
+++ b/packages/server/src/api/pages.ts
@@ -1,9 +1,10 @@
import { Hono } from 'hono'
import { validator } from 'hono/validator'
import { isNil, isNotNil, isNumberString } from '@web-archive/shared/utils'
+import { z } from 'zod'
import type { HonoTypeUserInformation } from '~/constants/binding'
import result from '~/utils/result'
-import { clearDeletedPage, deletePageById, getPageById, insertPage, queryDeletedPage, queryPage, queryRecentSavePage, restorePage, selectPageTotalCount, updatePage } from '~/model/page'
+import { clearDeletedPage, deletePageById, getPageById, insertPage, queryDeletedPage, queryPage, queryPageByUrl, queryRecentSavePage, restorePage, selectPageTotalCount, updatePage } from '~/model/page'
import { getFolderById, restoreFolder } from '~/model/folder'
import { getFileFromBucket, saveFileToBucket } from '~/utils/file'
import { updateShowcase } from '~/model/showcase'
@@ -130,6 +131,33 @@ app.post(
},
)
+app.post(
+ '/query_by_url',
+ validator('json', (value, c) => {
+ const errorMsg = {
+ message: 'Page URL is required',
+ }
+ const schema = z.object({
+ pageUrl: z.string(errorMsg).min(1, errorMsg),
+ })
+
+ const parsed = schema.safeParse(value)
+ if (!parsed.success) {
+ if (parsed.error.errors.length > 0) {
+ return c.json(result.error(400, parsed.error.errors[0].message))
+ }
+ return c.json(result.error(400, 'Invalid request'))
+ }
+
+ return parsed.data
+ }),
+ async (c) => {
+ const { pageUrl } = c.req.valid('json')
+ const pages = await queryPageByUrl(c.env.DB, pageUrl)
+ return c.json(result.success(pages))
+ },
+)
+
app.get('/recent_save', async (c) => {
const pages = await queryRecentSavePage(c.env.DB)
return c.json(result.success(pages))
diff --git a/packages/server/src/model/page.ts b/packages/server/src/model/page.ts
index 50c4573..cea9ea5 100644
--- a/packages/server/src/model/page.ts
+++ b/packages/server/src/model/page.ts
@@ -90,6 +90,12 @@ async function queryPage(DB: D1Database, options: { folderId?: number, pageNumbe
return sqlResult.results
}
+async function queryPageByUrl(DB: D1Database, pageUrl: string) {
+ const sql = `SELECT * FROM pages WHERE pageUrl = ? AND isDeleted = 0`
+ const result = await DB.prepare(sql).bind(pageUrl).all()
+ return result.results
+}
+
async function selectDeletedPageTotalCount(DB: D1Database) {
const sql = `
SELECT COUNT(*) as count FROM pages
@@ -240,6 +246,7 @@ async function updatePage(DB: D1Database, options: UpdatePageOptions) {
export {
selectPageTotalCount,
queryPage,
+ queryPageByUrl,
selectDeletedPageTotalCount,
queryDeletedPage,
deletePageById,