diff --git a/src/app/Store.ts b/src/app/Store.ts index 20f356d9..89ea0f85 100644 --- a/src/app/Store.ts +++ b/src/app/Store.ts @@ -21,6 +21,7 @@ export namespace Store { export const ID_TREE_VIEW_MODE = 'misode_tree_view_mode' export const ID_COLORMAP = 'misode_colormap' export const ID_GENERATOR_HISTORY = 'misode_generator_history' + export const ID_WHATS_NEW_SEEN = 'misode_whats_new_seen' export function getLanguage() { return localStorage.getItem(ID_LANGUAGE) ?? 'en' @@ -186,4 +187,22 @@ export namespace Store { history.push(id) localStorage.setItem(ID_GENERATOR_HISTORY, JSON.stringify(history.slice(-50))) } + + export function getWhatsNewSeen(): { id: string, time: string }[] { + return JSON.parse(localStorage.getItem(ID_WHATS_NEW_SEEN) ?? '[]') + } + + export function seeWhatsNew(ids: string[]) { + const now = new Date().toISOString() + const items = getWhatsNewSeen() + for (const id of ids) { + const old = items.find(i => i.id === id) + if (old) { + old.time = now + } else { + items.push({ id, time: now }) + } + } + localStorage.setItem(ID_WHATS_NEW_SEEN, JSON.stringify(items)) + } } diff --git a/src/app/components/whatsnew/WhatsNewEntry.tsx b/src/app/components/whatsnew/WhatsNewEntry.tsx new file mode 100644 index 00000000..bba1d99d --- /dev/null +++ b/src/app/components/whatsnew/WhatsNewEntry.tsx @@ -0,0 +1,16 @@ +import { marked } from 'marked' +import type { WhatsNewItem } from '../../services/DataFetcher.js' +import { WhatsNewTime } from './WhatsNewTime.jsx' + +interface EntryProps { + item: WhatsNewItem, +} +export function WhatsNewEntry({ item }: EntryProps) { + return
+ + +

{item.title}

+
+
+
+} diff --git a/src/app/components/whatsnew/WhatsNewTime.tsx b/src/app/components/whatsnew/WhatsNewTime.tsx new file mode 100644 index 00000000..d8ea2907 --- /dev/null +++ b/src/app/components/whatsnew/WhatsNewTime.tsx @@ -0,0 +1,14 @@ +import { useLocale } from '../../contexts/Locale.jsx' +import type { WhatsNewItem } from '../../services/DataFetcher.js' + +interface Props { + item: WhatsNewItem, + short?: boolean, +} +export function WhatsNewTime({ item, short }: Props) { + const { locale } = useLocale() + return +} diff --git a/src/app/components/whatsnew/index.ts b/src/app/components/whatsnew/index.ts new file mode 100644 index 00000000..4f22dcc5 --- /dev/null +++ b/src/app/components/whatsnew/index.ts @@ -0,0 +1,2 @@ +export * from './WhatsNewEntry.jsx' +export * from './WhatsNewTime.jsx' diff --git a/src/app/pages/Home.tsx b/src/app/pages/Home.tsx index 60ae371b..e8a13280 100644 --- a/src/app/pages/Home.tsx +++ b/src/app/pages/Home.tsx @@ -3,6 +3,7 @@ import contributors from '../../contributors.json' import { Store } from '../Store.js' import { shuffle } from '../Utils.js' import { Card, ChangelogEntry, Footer, GeneratorCard, Giscus, GuideCard, ToolCard, ToolGroup } from '../components/index.js' +import { WhatsNewTime } from '../components/whatsnew/WhatsNewTime.jsx' import { useLocale, useTitle } from '../contexts/index.js' import { useAsync } from '../hooks/useAsync.js' import { useMediaQuery } from '../hooks/useMediaQuery.js' @@ -26,14 +27,15 @@ export function Home({}: Props) {
{smallScreen && } + {smallScreen && } {smallScreen && } {smallScreen && } -
{!smallScreen &&
+
} @@ -142,7 +144,7 @@ function WhatsNew() { const { value: items } = useAsync(fetchWhatsNew) return - {items?.slice(0, 3).map(item => {item.title})} + {items?.slice(0, 3).map(item => }>{item.title})} } diff --git a/src/app/pages/WhatsNew.tsx b/src/app/pages/WhatsNew.tsx index e8226b30..700ccd51 100644 --- a/src/app/pages/WhatsNew.tsx +++ b/src/app/pages/WhatsNew.tsx @@ -1,8 +1,10 @@ -import { marked } from 'marked' +import { useEffect } from 'preact/hooks' +import { Store } from '../Store.js' import { ErrorPanel, Footer } from '../components/index.js' +import { WhatsNewEntry } from '../components/whatsnew/WhatsNewEntry.jsx' import { useLocale, useTitle } from '../contexts/index.js' +import { useActiveTimeout } from '../hooks/useActiveTimout.js' import { useAsync } from '../hooks/useAsync.js' -import type { WhatsNewItem } from '../services/DataFetcher.js' import { fetchWhatsNew } from '../services/DataFetcher.js' interface Props { @@ -14,6 +16,18 @@ export function WhatsNew({}: Props) { const { value: items, error } = useAsync(fetchWhatsNew) + const [storeTime, startStoreTime] = useActiveTimeout() + useEffect(() => { + if (items !== undefined) { + startStoreTime() + } + }, [items]) + useEffect(() => { + if (items !== undefined && storeTime) { + Store.seeWhatsNew(items.map(i => i.id)) + } + }, [items, storeTime]) + return

{locale('whats_new.description')}

@@ -23,16 +37,3 @@ export function WhatsNew({}: Props) {
} - -interface EntryProps { - item: WhatsNewItem, -} -function WhatsNewEntry({ item }: EntryProps) { - return
- - -

{item.title}

-
-
-
-} diff --git a/src/app/services/DataFetcher.ts b/src/app/services/DataFetcher.ts index 2b4fb4c4..a7d477c8 100644 --- a/src/app/services/DataFetcher.ts +++ b/src/app/services/DataFetcher.ts @@ -1,5 +1,6 @@ import type { CollectionRegistry } from '@mcschema/core' import config from '../Config.js' +import { Store } from '../Store.js' import { message } from '../Utils.js' import type { BlockStateRegistry, VersionId } from './Schemas.js' @@ -287,11 +288,19 @@ export interface WhatsNewItem { body: string, url: string, createdAt: string, + seenAt?: string, } export async function fetchWhatsNew(): Promise { try { const whatsNew = await cachedFetch(whatsNewUrl, { refresh: true }) + const seenState = Store.getWhatsNewSeen() + for (const { id, time } of seenState) { + const item = whatsNew.find(i => i.id === id) + if (item) { + item.seenAt = time + } + } return whatsNew } catch (e) { throw new Error(`Error occured while fetching what's new: ${message(e)}`) diff --git a/src/locales/en.json b/src/locales/en.json index 173df914..130a044b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -255,6 +255,7 @@ "weight": "Weight", "whats_new": "What's new?", "whats_new.description": "Stay informed about all the latest development on misode.github.io. Read below to find out which features have recently been added.", + "whats_new.new": "NEW", "world": "World Settings", "worldgen": "Worldgen", "worldgen/biome": "Biome", diff --git a/src/styles/global.css b/src/styles/global.css index 9659e1d1..0ae6b454 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -2417,6 +2417,10 @@ hr { font-size: 15px; } +.whats-new-entry time .new-badge { + margin-left: 6px; +} + .whats-new-entry h2 { color: var(--text-1); padding: 10px 0 15px; @@ -2433,6 +2437,15 @@ hr { display: block; } +.new-badge { + background-color: var(--accent-primary); + color: var(--background-1); + font-size: 14px; + font-weight: bold; + border-radius: 1000px; + padding: 1px 4px; +} + .ace_editor, .ace_gutter, .ace_gutter .ace_layer,