diff --git a/index.html b/index.html index 48c59fc..db87507 100644 --- a/index.html +++ b/index.html @@ -1,16 +1,18 @@ - - - - - - Solid App - - - -
- - - + + + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/package.json b/package.json index 6fb750b..af7c484 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "kysely": "^0.27.5", "kysely-wasm": "^0.7.0", "lucide-solid": "^0.468.0", + "seroval": "^1.1.1", "solid-js": "^1.9.3", "sql.js": "^1.12.0", "tailwind-merge": "^2.5.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3fcef77..f26ba29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,6 +56,9 @@ importers: lucide-solid: specifier: ^0.468.0 version: 0.468.0(solid-js@1.9.3) + seroval: + specifier: ^1.1.1 + version: 1.1.1 solid-js: specifier: ^1.9.3 version: 1.9.3 diff --git a/src/lib/db-cache.ts b/src/lib/db-cache.ts index ee0827c..888a330 100644 --- a/src/lib/db-cache.ts +++ b/src/lib/db-cache.ts @@ -1,4 +1,5 @@ import { createRoot, on, createDeferred } from "solid-js"; +import { serialize, deserialize } from "seroval"; const DATABASE_HASH_PREFIX = "database"; @@ -64,10 +65,12 @@ class LocalStorageCacheAdapter { this.keys.add(fullKey); try { - localStorage.setItem(fullKey, JSON.stringify(value)); + localStorage.setItem(fullKey, serialize(value)); } catch (error: unknown) { if (error instanceof DOMException && error.name === "QUOTA_EXCEEDED_ERR") { console.error("Storage quota exceeded, not caching new function calls"); + } else { + console.error(error); } } } @@ -80,7 +83,7 @@ class LocalStorageCacheAdapter { get(cacheName: string, key: string): R | undefined { const item = localStorage.getItem(this.#createKey(cacheName, key)); if (item) { - return JSON.parse(item) as R; + return deserialize(item) as R; } } } diff --git a/src/lib/messages.ts b/src/lib/messages.ts index 63e48f3..782581b 100644 --- a/src/lib/messages.ts +++ b/src/lib/messages.ts @@ -1,7 +1,7 @@ -import { createMemo, type Accessor } from "solid-js"; import { getDateList, getHourList, getMonthList, getWeekdayList } from "./date"; import type { MessageOverview, MessageStats, Recipients } from "~/types"; import { isSameDay } from "date-fns"; +import { cached } from "./db-cache"; export const hourNames = getHourList(); @@ -15,16 +15,14 @@ export const weekdayNames = getWeekdayList(); const initialWeekdayMap = [...weekdayNames.keys()]; -export const createMessageStatsSources = ( - messageOverview: Accessor, - recipients: Accessor, -) => { - const initialRecipientMap = () => Object.fromEntries(recipients().map(({ recipientId }) => [recipientId, 0])); +const createMessageStatsSourcesRaw = (messageOverview: MessageOverview, recipients: Recipients) => { + const initialRecipientMap = () => { + return Object.fromEntries(recipients.map(({ recipientId }) => [recipientId, 0])); + }; const dateList = () => { - const currentDmMessagesOverview = messageOverview(); - const firstDate = currentDmMessagesOverview?.at(0)?.messageDate; - const lastDate = currentDmMessagesOverview?.at(-1)?.messageDate; + const firstDate = messageOverview?.at(0)?.messageDate; + const lastDate = messageOverview?.at(-1)?.messageDate; if (firstDate && lastDate) { return getDateList(firstDate, lastDate).map((date) => ({ totalMessages: 0, @@ -34,52 +32,51 @@ export const createMessageStatsSources = ( } }; - return createMemo(() => { - const currentMessageOverview = messageOverview(); - const currentDateList = dateList(); - const currentInitialRecipientMap = initialRecipientMap(); + const currentDateList = dateList(); + const currentInitialRecipientMap = initialRecipientMap(); - const messageStats: MessageStats = { - person: { ...currentInitialRecipientMap }, - month: initialMonthMap.map(() => ({ ...currentInitialRecipientMap })), - date: currentDateList ?? [], - weekday: initialWeekdayMap.map(() => ({ ...currentInitialRecipientMap })), - daytime: initialHoursMap.map(() => ({ ...currentInitialRecipientMap })), - }; + const messageStats: MessageStats = { + person: { ...currentInitialRecipientMap }, + month: initialMonthMap.map(() => ({ ...currentInitialRecipientMap })), + date: currentDateList ?? [], + weekday: initialWeekdayMap.map(() => ({ ...currentInitialRecipientMap })), + daytime: initialHoursMap.map(() => ({ ...currentInitialRecipientMap })), + }; - if (currentMessageOverview && currentDateList) { - const { person, month, date, weekday, daytime } = messageStats; + if (currentDateList) { + const { person, month, date, weekday, daytime } = messageStats; - for (const message of currentMessageOverview) { - const { messageDate } = message; + for (const message of messageOverview) { + const { messageDate } = message; - // increment overall message count of a person - person[message.fromRecipientId] += 1; + // increment overall message count of a person + person[message.fromRecipientId] += 1; - // increment the message count of the message's month for this recipient - month[messageDate.getMonth()][message.fromRecipientId] += 1; + // increment the message count of the message's month for this recipient + month[messageDate.getMonth()][message.fromRecipientId] += 1; - // biome-ignore lint/style/noNonNullAssertion: - const dateStatsEntry = date.find(({ date }) => isSameDay(date, messageDate))!; + // biome-ignore lint/style/noNonNullAssertion: + const dateStatsEntry = date.find(({ date }) => isSameDay(date, messageDate))!; - // increment the message count of the message's date for this recipient - dateStatsEntry[message.fromRecipientId] += 1; + // increment the message count of the message's date for this recipient + dateStatsEntry[message.fromRecipientId] += 1; - // increment the overall message count of the message's date - dateStatsEntry.totalMessages += 1; + // increment the overall message count of the message's date + dateStatsEntry.totalMessages += 1; - const weekdayOfDate = messageDate.getDay(); - // we index starting with monday while the `Date` object indexes starting with Sunday - const weekdayIndex = weekdayOfDate === 0 ? 6 : weekdayOfDate - 1; + const weekdayOfDate = messageDate.getDay(); + // we index starting with monday while the `Date` object indexes starting with Sunday + const weekdayIndex = weekdayOfDate === 0 ? 6 : weekdayOfDate - 1; - // increment the message count of the message's weekday for this recipient - weekday[weekdayIndex][message.fromRecipientId] += 1; + // increment the message count of the message's weekday for this recipient + weekday[weekdayIndex][message.fromRecipientId] += 1; - // increment the message count of the message's daytime for this recipient - daytime[messageDate.getHours()][message.fromRecipientId] += 1; - } + // increment the message count of the message's daytime for this recipient + daytime[messageDate.getHours()][message.fromRecipientId] += 1; } + } - return messageStats; - }); + return messageStats; }; + +export const createMessageStatsSources = cached(createMessageStatsSourcesRaw); diff --git a/src/pages/dm/dm-id.tsx b/src/pages/dm/dm-id.tsx index 235c51e..e7f2e52 100644 --- a/src/pages/dm/dm-id.tsx +++ b/src/pages/dm/dm-id.tsx @@ -1,4 +1,4 @@ -import { type Component, createResource } from "solid-js"; +import { type Component, createMemo, createResource } from "solid-js"; import type { RouteSectionProps } from "@solidjs/router"; import { dmPartnerRecipientQuery, SELF_ID, threadMostUsedWordsQuery, threadSentMessagesOverviewQuery } from "~/db"; @@ -35,7 +35,7 @@ export const DmId: Component = (props) => { } }); - const [dmMessagesOverview] = createResource(async () => { + const [dmMessagesOverview] = createResource(async () => { const dmMessageOverview = await threadSentMessagesOverviewQuery(dmId()); if (dmMessageOverview) { return dmMessageOverview.map((row) => { @@ -61,16 +61,16 @@ export const DmId: Component = (props) => { }, ]; } - - return [ - { - recipientId: SELF_ID, - name: "You", - }, - ]; }; - const dmMessageStats = createMessageStatsSources(dmMessagesOverview, recipients); + const dmMessageStats = createMemo(() => { + const currentDmMessagesOverview = dmMessagesOverview(); + const currentRecipients = recipients(); + + if (currentDmMessagesOverview && currentRecipients) { + return createMessageStatsSources(currentDmMessagesOverview, currentRecipients); + } + }); return ( <> @@ -78,26 +78,26 @@ export const DmId: Component = (props) => {
DM with {dmPartner()?.name} Chat timeline - + Messages per
Person - +
Daytime - +
Month - +
Weekday - +
Word cloud diff --git a/src/pages/dm/dm-messages-per-date.tsx b/src/pages/dm/dm-messages-per-date.tsx index 4cfff39..d87d4ce 100644 --- a/src/pages/dm/dm-messages-per-date.tsx +++ b/src/pages/dm/dm-messages-per-date.tsx @@ -4,14 +4,14 @@ import { LineChart } from "~/components/ui/charts"; import type { MessageStats, Recipients } from "~/types"; export const DmMessagesPerDate: Component<{ - dateStats: MessageStats["date"]; - recipients: Recipients; + dateStats: MessageStats["date"] | undefined; + recipients: Recipients | undefined; }> = (props) => { const dateChartData: Accessor | undefined> = () => { const currentDmMessages = props.dateStats; const currentRecipients = props.recipients; - if (currentDmMessages) { + if (currentDmMessages && currentRecipients) { const currentDmMessagesValues = Object.values(currentDmMessages); return { diff --git a/src/pages/dm/dm-messages-per-daytime.tsx b/src/pages/dm/dm-messages-per-daytime.tsx index 765f070..1c12ff5 100644 --- a/src/pages/dm/dm-messages-per-daytime.tsx +++ b/src/pages/dm/dm-messages-per-daytime.tsx @@ -5,8 +5,8 @@ import type { MessageStats, Recipients } from "~/types"; import { hourNames } from "~/lib/messages"; export const DmMessagesPerDaytime: Component<{ - daytimeStats: MessageStats["daytime"]; - recipients: Recipients; + daytimeStats: MessageStats["daytime"] | undefined; + recipients: Recipients | undefined; }> = (props) => { const daytimeChartData: Accessor | undefined> = () => { const currentMessagesPerHour = props.daytimeStats; diff --git a/src/pages/dm/dm-messages-per-month.tsx b/src/pages/dm/dm-messages-per-month.tsx index bec4f2e..7bcceaf 100644 --- a/src/pages/dm/dm-messages-per-month.tsx +++ b/src/pages/dm/dm-messages-per-month.tsx @@ -5,8 +5,8 @@ import { monthNames } from "~/lib/messages"; import type { MessageStats, Recipients } from "~/types"; export const DmMessagesPerMonth: Component<{ - monthStats: MessageStats["month"]; - recipients: Recipients; + monthStats: MessageStats["month"] | undefined; + recipients: Recipients | undefined; }> = (props) => { const monthChartData: Accessor | undefined> = () => { const currentMessagesPerMonth = props.monthStats; diff --git a/src/pages/dm/dm-messages-per-recipients.tsx b/src/pages/dm/dm-messages-per-recipients.tsx index cd0e8de..8d43409 100644 --- a/src/pages/dm/dm-messages-per-recipients.tsx +++ b/src/pages/dm/dm-messages-per-recipients.tsx @@ -4,8 +4,8 @@ import { PieChart } from "~/components/ui/charts"; import type { MessageStats, Recipients } from "~/types"; export const DmMessagesPerRecipient: Component<{ - personStats: MessageStats["person"]; - recipients: Recipients; + personStats: MessageStats["person"] | undefined; + recipients: Recipients | undefined; }> = (props) => { const recipientChartData: Accessor | undefined> = () => { const currentMessagesPerRecipient = props.personStats; diff --git a/src/pages/dm/dm-messages-per-weekday.tsx b/src/pages/dm/dm-messages-per-weekday.tsx index 4699c8c..4608298 100644 --- a/src/pages/dm/dm-messages-per-weekday.tsx +++ b/src/pages/dm/dm-messages-per-weekday.tsx @@ -5,8 +5,8 @@ import { weekdayNames } from "~/lib/messages"; import type { MessageStats, Recipients } from "~/types"; export const DmMessagesPerWeekday: Component<{ - weekdayStats: MessageStats["weekday"]; - recipients: Recipients; + weekdayStats: MessageStats["weekday"] | undefined; + recipients: Recipients | undefined; }> = (props) => { const weekdayChartData: Accessor | undefined> = () => { const currentMessagesPerWeekday = props.weekdayStats; diff --git a/src/pages/dm/dm-overview.tsx b/src/pages/dm/dm-overview.tsx index 038144c..5232def 100644 --- a/src/pages/dm/dm-overview.tsx +++ b/src/pages/dm/dm-overview.tsx @@ -6,7 +6,7 @@ import { getDistanceBetweenDatesInDays } from "~/lib/date"; import type { MessageOverview } from "~/types"; export const DmOverview: Component<{ - messages: MessageOverview; + messages: MessageOverview | undefined; }> = (props) => { const dmOverview = () => { const firstMessageDate = props.messages?.at(0)?.messageDate; diff --git a/src/pages/home.tsx b/src/pages/home.tsx index 3427c1a..df3898f 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -4,6 +4,7 @@ import { type RouteSectionProps, useNavigate } from "@solidjs/router"; import { setDb, SQL } from "~/db"; import { Portal } from "solid-js/web"; import { Flex } from "~/components/ui/flex"; +import { Title } from "@solidjs/meta"; export const Home: Component = () => { const [isLoadingDb, setIsLoadingDb] = createSignal(false); @@ -38,6 +39,7 @@ export const Home: Component = () => { + Signal stats
diff --git a/src/types.ts b/src/types.ts index 99ee51e..4295bed 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,9 +1,7 @@ -export type MessageOverview = - | { - messageDate: Date; - fromRecipientId: number; - }[] - | undefined; +export type MessageOverview = { + messageDate: Date; + fromRecipientId: number; +}[]; export type Recipients = { recipientId: number;