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,