diff --git a/src/db.ts b/src/db.ts index bca0a76..940a7cc 100644 --- a/src/db.ts +++ b/src/db.ts @@ -177,7 +177,7 @@ const threadSentMessagesPerPersonOverviewQueryRaw = (threadId: number) => .selectFrom("message") .select((eb) => [ "from_recipient_id", - sql`DATE(datetime(message.date_sent / 1000, 'unixepoch'))`.as( + sql`DATE(datetime(message.date_sent / 1000, 'unixepoch'))`.as( "message_date" ), eb.fn.countAll().as("message_count"), @@ -200,6 +200,27 @@ export const dmSentMessagesPerPersonOverviewQuery = cached( threadSentMessagesPerPersonOverviewQueryRaw ); +const threadSentMessagesOverviewQueryRaw = (threadId: number) => + kyselyDb() + .selectFrom("message") + .select([ + "from_recipient_id", + sql`datetime(date_sent / 1000, 'unixepoch')`.as("message_datetime"), + ]) + .orderBy(["message_datetime"]) + .where((eb) => + eb.and([ + eb("body", "is not", null), + eb("body", "!=", ""), + eb("thread_id", "=", threadId), + ]) + ) + .execute(); + +export const threadSentMessagesOverviewQuery = cached( + threadSentMessagesOverviewQueryRaw +); + const threadMostUsedWordsQueryRaw = (threadId: number, limit = 10) => kyselyDb() .withRecursive("words", (eb) => { diff --git a/src/pages/dm/dm-id.tsx b/src/pages/dm/dm-id.tsx index 79a7de8..2361dcd 100644 --- a/src/pages/dm/dm-id.tsx +++ b/src/pages/dm/dm-id.tsx @@ -1,9 +1,9 @@ -import { type Accessor, type Component, createResource, Show } from "solid-js"; +import { type Accessor, type Component, createEffect, createResource, Show } from "solid-js"; import type { RouteSectionProps } from "@solidjs/router"; import { type ChartData } from "chart.js"; -import { LineChart, WordCloudChart } from "~/components/ui/charts"; +import { LineChart, RadarChart, WordCloudChart } from "~/components/ui/charts"; import { dmOverviewQuery, @@ -11,6 +11,7 @@ import { dmSentMessagesPerPersonOverviewQuery, SELF_ID, threadMostUsedWordsQuery, + threadSentMessagesOverviewQuery, } from "~/db"; import { getNameFromRecipient } from "~/lib/get-name-from-recipient"; import { Heading } from "~/components/ui/heading"; @@ -19,6 +20,29 @@ import { Flex } from "~/components/ui/flex"; import { CalendarArrowUp, CalendarArrowDown, CalendarClock, MessagesSquare } from "lucide-solid"; import { getDistanceBetweenDatesInDays } from "~/lib/date"; +type MonthIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; + +const monthNames: Record = { + 1: "January", + 2: "February", + 3: "March", + 4: "April", + 5: "May", + 6: "June", + 7: "July", + 8: "August", + 9: "September", + 10: "October", + 11: "November", + 12: "December", +}; + +const initialMonthMap = Object.fromEntries( + Array(12) + .fill(0) + .map((_value, index) => [index + 1, 0]), +) as Record; + export const DmId: Component = (props) => { const dmId = () => Number(props.params.dmid); @@ -52,16 +76,46 @@ export const DmId: Component = (props) => { const [dmMessagesPerPerson] = createResource(() => dmSentMessagesPerPersonOverviewQuery(dmId())); + const [dmMessagesOverview] = createResource(async () => { + const dmMessageOverview = await threadSentMessagesOverviewQuery(dmId()); + if (dmMessageOverview) { + return dmMessageOverview.map((row) => { + return { + messageDate: new Date(row.message_datetime), + recipientId: row.from_recipient_id, + }; + }); + } + }); + + const dmMessagesPerMonth = () => { + const currentDmMessagesOverview = dmMessagesOverview(); + + if (currentDmMessagesOverview) { + return currentDmMessagesOverview.reduce>( + (prev, curr) => { + const month = curr.messageDate.getMonth() as MonthIndex; + + prev[month as MonthIndex] += 1; + + return prev; + }, + { ...initialMonthMap }, + ); + } + }; + // maps all the message counts to dates - const dmMessages = () => { + const dmMessagesPerDateOverview = () => { return dmMessagesPerPerson()?.reduce< { + rawDate: string; date: Date; totalMessages: number; [recipientId: number]: number; }[] >((prev, curr) => { - const existingDate = prev.find(({ date }) => date === curr.message_date); + const existingDate = prev.find(({ rawDate }) => rawDate === curr.message_date); if (existingDate) { existingDate[curr.from_recipient_id] = curr.message_count; @@ -69,7 +123,8 @@ export const DmId: Component = (props) => { existingDate.totalMessages += curr.message_count; } else { prev.push({ - date: curr.message_date, + rawDate: curr.message_date, + date: new Date(curr.message_date), totalMessages: curr.message_count, [curr.from_recipient_id]: curr.message_count, }); @@ -128,12 +183,12 @@ export const DmId: Component = (props) => { }; const dateChartData: Accessor | undefined> = () => { - const currentDmMessages = dmMessages(); + const currentDmMessages = dmMessagesPerDateOverview(); const currentRecipients = recipients(); if (currentDmMessages) { return { - labels: currentDmMessages.map((row) => row.date), + labels: currentDmMessages.map((row) => row.date.toDateString()), datasets: [ { label: "Total number of messages", @@ -162,6 +217,22 @@ export const DmId: Component = (props) => { } }; + const monthChartData: Accessor | undefined> = () => { + const currentMessagesPerMonth = dmMessagesPerMonth(); + + if (currentMessagesPerMonth) { + return { + labels: Object.values(monthNames), + datasets: [ + { + label: "Number of messages", + data: Object.values(currentMessagesPerMonth), + }, + ], + }; + } + }; + return (
DM with {dmPartner()?.name} @@ -198,33 +269,33 @@ export const DmId: Component = (props) => { - + Your first message is from {(currentDmOverview) => ( - {currentDmOverview().firstMessageDate.toLocaleDateString()} + {currentDmOverview().firstMessageDate.toDateString()} )} - + Your last message is from {(currentDmOverview) => ( - {currentDmOverview().lastMessageDate.toLocaleDateString()} + {currentDmOverview().lastMessageDate.toDateString()} )} - + You have been chatting for @@ -256,6 +327,24 @@ export const DmId: Component = (props) => { + Messages per +
+ Month + + + {(currentMonthChartData) => ( + + )} + + +
Word cloud {(currentMostUsedWordChartData) => (