feat: cache stats sources

This commit is contained in:
Samuel 2024-12-18 19:03:44 +01:00
parent d87d9fb301
commit b97fa88893
No known key found for this signature in database
14 changed files with 98 additions and 92 deletions

View file

@ -1,16 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" />
<title>Solid App</title>
<!-- <link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" /> -->
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="/src/index.tsx" type="module"></script>
</body>
</html>

View file

@ -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",

3
pnpm-lock.yaml generated
View file

@ -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

View file

@ -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<R>(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;
}
}
}

View file

@ -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<MessageOverview>,
recipients: Accessor<Recipients>,
) => {
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,8 +32,6 @@ export const createMessageStatsSources = (
}
};
return createMemo(() => {
const currentMessageOverview = messageOverview();
const currentDateList = dateList();
const currentInitialRecipientMap = initialRecipientMap();
@ -47,10 +43,10 @@ export const createMessageStatsSources = (
daytime: initialHoursMap.map(() => ({ ...currentInitialRecipientMap })),
};
if (currentMessageOverview && currentDateList) {
if (currentDateList) {
const { person, month, date, weekday, daytime } = messageStats;
for (const message of currentMessageOverview) {
for (const message of messageOverview) {
const { messageDate } = message;
// increment overall message count of a person
@ -81,5 +77,6 @@ export const createMessageStatsSources = (
}
return messageStats;
});
};
export const createMessageStatsSources = cached(createMessageStatsSourcesRaw);

View file

@ -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<RouteSectionProps> = (props) => {
}
});
const [dmMessagesOverview] = createResource<MessageOverview>(async () => {
const [dmMessagesOverview] = createResource<MessageOverview | undefined>(async () => {
const dmMessageOverview = await threadSentMessagesOverviewQuery(dmId());
if (dmMessageOverview) {
return dmMessageOverview.map((row) => {
@ -61,16 +61,16 @@ export const DmId: Component<RouteSectionProps> = (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<RouteSectionProps> = (props) => {
<div class="flex flex-col items-center">
<Heading level={1}>DM with {dmPartner()?.name}</Heading>
<Heading level={2}>Chat timeline</Heading>
<DmMessagesPerDate dateStats={dmMessageStats().date} recipients={recipients()} />
<DmMessagesPerDate dateStats={dmMessageStats()?.date} recipients={recipients()} />
<DmOverview messages={dmMessagesOverview()} />
<Heading level={2}>Messages per</Heading>
<Grid cols={1} colsMd={2} class="gap-x-16 gap-y-16">
<div>
<Heading level={3}>Person</Heading>
<DmMessagesPerRecipient personStats={dmMessageStats().person} recipients={recipients()} />
<DmMessagesPerRecipient personStats={dmMessageStats()?.person} recipients={recipients()} />
</div>
<div>
<Heading level={3}>Daytime</Heading>
<DmMessagesPerDaytime daytimeStats={dmMessageStats().daytime} recipients={recipients()} />
<DmMessagesPerDaytime daytimeStats={dmMessageStats()?.daytime} recipients={recipients()} />
</div>
<div>
<Heading level={3}>Month</Heading>
<DmMessagesPerMonth monthStats={dmMessageStats().month} recipients={recipients()} />
<DmMessagesPerMonth monthStats={dmMessageStats()?.month} recipients={recipients()} />
</div>
<div>
<Heading level={3}>Weekday</Heading>
<DmMessagesPerWeekday weekdayStats={dmMessageStats().weekday} recipients={recipients()} />
<DmMessagesPerWeekday weekdayStats={dmMessageStats()?.weekday} recipients={recipients()} />
</div>
</Grid>
<Heading level={2}>Word cloud</Heading>

View file

@ -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<ChartData<"line"> | undefined> = () => {
const currentDmMessages = props.dateStats;
const currentRecipients = props.recipients;
if (currentDmMessages) {
if (currentDmMessages && currentRecipients) {
const currentDmMessagesValues = Object.values(currentDmMessages);
return {

View file

@ -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<ChartData<"bar"> | undefined> = () => {
const currentMessagesPerHour = props.daytimeStats;

View file

@ -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<ChartData<"radar"> | undefined> = () => {
const currentMessagesPerMonth = props.monthStats;

View file

@ -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<ChartData<"pie"> | undefined> = () => {
const currentMessagesPerRecipient = props.personStats;

View file

@ -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<ChartData<"radar"> | undefined> = () => {
const currentMessagesPerWeekday = props.weekdayStats;

View file

@ -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;

View file

@ -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<RouteSectionProps> = () => {
const [isLoadingDb, setIsLoadingDb] = createSignal(false);
@ -38,6 +39,7 @@ export const Home: Component<RouteSectionProps> = () => {
</Flex>
</Show>
</Portal>
<Title>Signal stats</Title>
<div>
<input type="file" accept=".sqlite" onChange={onFileChange}></input>
</div>

View file

@ -1,9 +1,7 @@
export type MessageOverview =
| {
export type MessageOverview = {
messageDate: Date;
fromRecipientId: number;
}[]
| undefined;
}[];
export type Recipients = {
recipientId: number;