feat: show message when loading the database

This commit is contained in:
Samuel 2024-12-18 17:13:34 +01:00
parent dbd0b12b3d
commit 84b5ef9755
No known key found for this signature in database
12 changed files with 99 additions and 150 deletions

View file

@ -44,6 +44,7 @@
"noUnreachable": "error", "noUnreachable": "error",
"noUnreachableSuper": "error", "noUnreachableSuper": "error",
"noUnsafeFinally": "error", "noUnsafeFinally": "error",
"noUnusedImports": "error",
"noUnsafeOptionalChaining": "error", "noUnsafeOptionalChaining": "error",
"noUnusedLabels": "error", "noUnusedLabels": "error",
"noUnusedPrivateClassMembers": "error", "noUnusedPrivateClassMembers": "error",
@ -107,13 +108,7 @@
} }
} }
}, },
"ignore": [ "ignore": ["dist/**/*.ts", "dist/**", "**/*.mjs", "eslint.config.js", "**/*.js"]
"dist/**/*.ts",
"dist/**",
"**/*.mjs",
"eslint.config.js",
"**/*.js"
]
}, },
"javascript": { "javascript": {
"formatter": { "formatter": {

View file

@ -1,7 +1,5 @@
import { type Component } from "solid-js"; import { type Component } from "solid-js";
import { Route } from "@solidjs/router"; import { Route } from "@solidjs/router";
import { allThreadsOverviewQuery } from "./db";
import { DmId, GroupId, Home, Overview } from "./pages"; import { DmId, GroupId, Home, Overview } from "./pages";
import "./app.css"; import "./app.css";

View file

@ -1,21 +1,21 @@
import type { Component, ComponentProps } from "solid-js" import type { Component, ComponentProps } from "solid-js";
import { mergeProps, splitProps } from "solid-js" import { mergeProps, splitProps } from "solid-js";
import { cn } from "~/lib/utils" import { cn } from "~/lib/utils";
type Cols = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 type Cols = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
type Span = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 type Span = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13;
type GridProps = ComponentProps<"div"> & { type GridProps = ComponentProps<"div"> & {
cols?: Cols cols?: Cols;
colsSm?: Cols colsSm?: Cols;
colsMd?: Cols colsMd?: Cols;
colsLg?: Cols colsLg?: Cols;
} };
const Grid: Component<GridProps> = (rawProps) => { const Grid: Component<GridProps> = (rawProps) => {
const props = mergeProps({ cols: 1 } satisfies GridProps, rawProps) const props = mergeProps({ cols: 1 } satisfies GridProps, rawProps);
const [local, others] = splitProps(props, ["cols", "colsSm", "colsMd", "colsLg", "class"]) const [local, others] = splitProps(props, ["cols", "colsSm", "colsMd", "colsLg", "class"]);
return ( return (
<div <div
@ -25,23 +25,23 @@ const Grid: Component<GridProps> = (rawProps) => {
local.colsSm && gridColsSm[local.colsSm], local.colsSm && gridColsSm[local.colsSm],
local.colsMd && gridColsMd[local.colsMd], local.colsMd && gridColsMd[local.colsMd],
local.colsLg && gridColsLg[local.colsLg], local.colsLg && gridColsLg[local.colsLg],
local.class local.class,
)} )}
{...others} {...others}
/> />
) );
} };
type ColProps = ComponentProps<"div"> & { type ColProps = ComponentProps<"div"> & {
span?: Span span?: Span;
spanSm?: Span spanSm?: Span;
spanMd?: Span spanMd?: Span;
spanLg?: Span spanLg?: Span;
} };
const Col: Component<ColProps> = (rawProps) => { const Col: Component<ColProps> = (rawProps) => {
const props = mergeProps({ span: 1 as Span }, rawProps) const props = mergeProps({ span: 1 as Span }, rawProps);
const [local, others] = splitProps(props, ["span", "spanSm", "spanMd", "spanLg", "class"]) const [local, others] = splitProps(props, ["span", "spanSm", "spanMd", "spanLg", "class"]);
return ( return (
<div <div
@ -50,14 +50,14 @@ const Col: Component<ColProps> = (rawProps) => {
local.spanSm && colSpanSm[local.spanSm], local.spanSm && colSpanSm[local.spanSm],
local.spanMd && colSpanMd[local.spanMd], local.spanMd && colSpanMd[local.spanMd],
local.spanLg && colSpanLg[local.spanLg], local.spanLg && colSpanLg[local.spanLg],
local.class local.class,
)} )}
{...others} {...others}
/> />
) );
} };
export { Grid, Col } export { Grid, Col };
const gridCols: { [key in Cols]: string } = { const gridCols: { [key in Cols]: string } = {
0: "grid-cols-none", 0: "grid-cols-none",
@ -72,8 +72,8 @@ const gridCols: { [key in Cols]: string } = {
9: "grid-cols-9", 9: "grid-cols-9",
10: "grid-cols-10", 10: "grid-cols-10",
11: "grid-cols-11", 11: "grid-cols-11",
12: "grid-cols-12" 12: "grid-cols-12",
} };
const gridColsSm: { [key in Cols]: string } = { const gridColsSm: { [key in Cols]: string } = {
0: "sm:grid-cols-none", 0: "sm:grid-cols-none",
@ -88,8 +88,8 @@ const gridColsSm: { [key in Cols]: string } = {
9: "sm:grid-cols-9", 9: "sm:grid-cols-9",
10: "sm:grid-cols-10", 10: "sm:grid-cols-10",
11: "sm:grid-cols-11", 11: "sm:grid-cols-11",
12: "sm:grid-cols-12" 12: "sm:grid-cols-12",
} };
const gridColsMd: { [key in Cols]: string } = { const gridColsMd: { [key in Cols]: string } = {
0: "md:grid-cols-none", 0: "md:grid-cols-none",
@ -104,8 +104,8 @@ const gridColsMd: { [key in Cols]: string } = {
9: "md:grid-cols-9", 9: "md:grid-cols-9",
10: "md:grid-cols-10", 10: "md:grid-cols-10",
11: "md:grid-cols-11", 11: "md:grid-cols-11",
12: "md:grid-cols-12" 12: "md:grid-cols-12",
} };
const gridColsLg: { [key in Cols]: string } = { const gridColsLg: { [key in Cols]: string } = {
0: "lg:grid-cols-none", 0: "lg:grid-cols-none",
@ -120,8 +120,8 @@ const gridColsLg: { [key in Cols]: string } = {
9: "lg:grid-cols-9", 9: "lg:grid-cols-9",
10: "lg:grid-cols-10", 10: "lg:grid-cols-10",
11: "lg:grid-cols-11", 11: "lg:grid-cols-11",
12: "lg:grid-cols-12" 12: "lg:grid-cols-12",
} };
const colSpan: { [key in Span]: string } = { const colSpan: { [key in Span]: string } = {
1: "col-span-1", 1: "col-span-1",
@ -136,8 +136,8 @@ const colSpan: { [key in Span]: string } = {
10: "col-span-10", 10: "col-span-10",
11: "col-span-11", 11: "col-span-11",
12: "col-span-12", 12: "col-span-12",
13: "col-span-13" 13: "col-span-13",
} };
const colSpanSm: { [key in Span]: string } = { const colSpanSm: { [key in Span]: string } = {
1: "sm:col-span-1", 1: "sm:col-span-1",
@ -152,8 +152,8 @@ const colSpanSm: { [key in Span]: string } = {
10: "sm:col-span-10", 10: "sm:col-span-10",
11: "sm:col-span-11", 11: "sm:col-span-11",
12: "sm:col-span-12", 12: "sm:col-span-12",
13: "sm:col-span-13" 13: "sm:col-span-13",
} };
const colSpanMd: { [key in Span]: string } = { const colSpanMd: { [key in Span]: string } = {
1: "md:col-span-1", 1: "md:col-span-1",
@ -168,8 +168,8 @@ const colSpanMd: { [key in Span]: string } = {
10: "md:col-span-10", 10: "md:col-span-10",
11: "md:col-span-11", 11: "md:col-span-11",
12: "md:col-span-12", 12: "md:col-span-12",
13: "md:col-span-13" 13: "md:col-span-13",
} };
const colSpanLg: { [key in Span]: string } = { const colSpanLg: { [key in Span]: string } = {
1: "lg:col-span-1", 1: "lg:col-span-1",
@ -184,5 +184,5 @@ const colSpanLg: { [key in Span]: string } = {
10: "lg:col-span-10", 10: "lg:col-span-10",
11: "lg:col-span-11", 11: "lg:col-span-11",
12: "lg:col-span-12", 12: "lg:col-span-12",
13: "lg:col-span-13" 13: "lg:col-span-13",
} };

View file

@ -1,12 +1,4 @@
import { import { type Accessor, createEffect, createMemo, createRoot, createSignal, DEV, type Setter } from "solid-js";
type Accessor,
createEffect,
createMemo,
createRoot,
createSignal,
DEV,
type Setter,
} from "solid-js";
import { Kysely, type NotNull, sql } from "kysely"; import { Kysely, type NotNull, sql } from "kysely";
import type { DB } from "kysely-codegen"; import type { DB } from "kysely-codegen";
@ -79,19 +71,13 @@ const allThreadsOverviewQueryRaw = kyselyDb()
(eb) => (eb) =>
eb eb
.selectFrom("message") .selectFrom("message")
.select((eb) => [ .select((eb) => ["message.thread_id", eb.fn.countAll().as("message_count")])
"message.thread_id",
eb.fn.countAll().as("message_count"),
])
.where((eb) => { .where((eb) => {
return eb.and([ return eb.and([eb("message.body", "is not", null), eb("message.body", "is not", "")]);
eb("message.body", "is not", null),
eb("message.body", "is not", ""),
]);
}) })
.groupBy("message.thread_id") .groupBy("message.thread_id")
.as("message"), .as("message"),
(join) => join.onRef("message.thread_id", "=", "thread._id") (join) => join.onRef("message.thread_id", "=", "thread._id"),
) )
.innerJoin("recipient", "thread.recipient_id", "recipient._id") .innerJoin("recipient", "thread.recipient_id", "recipient._id")
.leftJoin("groups", "recipient._id", "groups.recipient_id") .leftJoin("groups", "recipient._id", "groups.recipient_id")
@ -114,9 +100,7 @@ const allThreadsOverviewQueryRaw = kyselyDb()
}>() }>()
.compile(); .compile();
export const allThreadsOverviewQuery = cached(() => export const allThreadsOverviewQuery = cached(() => kyselyDb().executeQuery(allThreadsOverviewQueryRaw));
kyselyDb().executeQuery(allThreadsOverviewQueryRaw)
);
const overallSentMessagesQueryRaw = (recipientId: number) => const overallSentMessagesQueryRaw = (recipientId: number) =>
kyselyDb() kyselyDb()
@ -127,7 +111,7 @@ const overallSentMessagesQueryRaw = (recipientId: number) =>
eb("message.from_recipient_id", "=", recipientId), eb("message.from_recipient_id", "=", recipientId),
eb("message.body", "is not", null), eb("message.body", "is not", null),
eb("message.body", "!=", ""), eb("message.body", "!=", ""),
]) ]),
) )
.executeTakeFirst(); .executeTakeFirst();
@ -143,9 +127,7 @@ const dmPartnerRecipientQueryRaw = (dmId: number) =>
"recipient.nickname_joined_name", "recipient.nickname_joined_name",
]) ])
.innerJoin("thread", "recipient._id", "thread.recipient_id") .innerJoin("thread", "recipient._id", "thread.recipient_id")
.where((eb) => .where((eb) => eb.and([eb("thread._id", "=", dmId), eb("recipient._id", "!=", SELF_ID)]))
eb.and([eb("thread._id", "=", dmId), eb("recipient._id", "!=", SELF_ID)])
)
.$narrowType<{ .$narrowType<{
_id: number; _id: number;
}>() }>()
@ -156,25 +138,12 @@ export const dmPartnerRecipientQuery = cached(dmPartnerRecipientQueryRaw);
const threadSentMessagesOverviewQueryRaw = (threadId: number) => const threadSentMessagesOverviewQueryRaw = (threadId: number) =>
kyselyDb() kyselyDb()
.selectFrom("message") .selectFrom("message")
.select([ .select(["from_recipient_id", sql<string>`datetime(date_sent / 1000, 'unixepoch')`.as("message_datetime")])
"from_recipient_id",
sql<string>`datetime(date_sent / 1000, 'unixepoch')`.as(
"message_datetime"
),
])
.orderBy(["message_datetime"]) .orderBy(["message_datetime"])
.where((eb) => .where((eb) => eb.and([eb("body", "is not", null), eb("body", "!=", ""), eb("thread_id", "=", threadId)]))
eb.and([
eb("body", "is not", null),
eb("body", "!=", ""),
eb("thread_id", "=", threadId),
])
)
.execute(); .execute();
export const threadSentMessagesOverviewQuery = cached( export const threadSentMessagesOverviewQuery = cached(threadSentMessagesOverviewQueryRaw);
threadSentMessagesOverviewQueryRaw
);
const threadMostUsedWordsQueryRaw = (threadId: number, limit = 10) => const threadMostUsedWordsQueryRaw = (threadId: number, limit = 10) =>
kyselyDb() kyselyDb()
@ -185,16 +154,12 @@ const threadMostUsedWordsQueryRaw = (threadId: number, limit = 10) =>
sql`LOWER(substr(body, 1, instr(body || " ", " ") - 1))`.as("word"), sql`LOWER(substr(body, 1, instr(body || " ", " ") - 1))`.as("word"),
sql`(substr(body, instr(body || " ", " ") + 1))`.as("rest"), sql`(substr(body, instr(body || " ", " ") + 1))`.as("rest"),
]) ])
.where((eb) => .where((eb) => eb.and([eb("body", "is not", null), eb("thread_id", "=", threadId)]))
eb.and([eb("body", "is not", null), eb("thread_id", "=", threadId)])
)
.unionAll((ebInner) => { .unionAll((ebInner) => {
return ebInner return ebInner
.selectFrom("words") .selectFrom("words")
.select([ .select([
sql`LOWER(substr(rest, 1, instr(rest || " ", " ") - 1))`.as( sql`LOWER(substr(rest, 1, instr(rest || " ", " ") - 1))`.as("word"),
"word"
),
sql`(substr(rest, instr(rest || " ", " ") + 1))`.as("rest"), sql`(substr(rest, instr(rest || " ", " ") + 1))`.as("rest"),
]) ])
.where("rest", "<>", ""); .where("rest", "<>", "");

View file

@ -10,18 +10,14 @@ export const getDistanceBetweenDatesInDays = (a: Date, b: Date) => {
}; };
// https://dev.to/pretaporter/how-to-get-month-list-in-your-language-4lfb // https://dev.to/pretaporter/how-to-get-month-list-in-your-language-4lfb
export const getMonthList = ( export const getMonthList = (locales?: string | string[], format: "long" | "short" = "long"): string[] => {
locales?: string | string[],
format: "long" | "short" = "long"
): string[] => {
const year = new Date().getFullYear(); // 2020 const year = new Date().getFullYear(); // 2020
const monthList = [...Array(12).keys()]; // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] const monthList = [...Array(12).keys()]; // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
const formatter = new Intl.DateTimeFormat(locales, { const formatter = new Intl.DateTimeFormat(locales, {
month: format, month: format,
}); });
const getMonthName = (monthIndex: number) => const getMonthName = (monthIndex: number) => formatter.format(new Date(year, monthIndex));
formatter.format(new Date(year, monthIndex));
return monthList.map(getMonthName); return monthList.map(getMonthName);
}; };
@ -48,10 +44,7 @@ export const getDateList = (startDate: Date, endDate: Date): Date[] => {
return dateArray; return dateArray;
}; };
export const getHourList = ( export const getHourList = (locales?: string | string[], format: "numeric" | "2-digit" = "numeric"): string[] => {
locales?: string | string[],
format: "numeric" | "2-digit" = "numeric"
): string[] => {
const now = new Date(); const now = new Date();
const year = now.getFullYear(); const year = now.getFullYear();
const month = now.getMonth(); const month = now.getMonth();
@ -63,16 +56,12 @@ export const getHourList = (
hourCycle: "h11", hourCycle: "h11",
}); });
const getHourName = (hourIndex: number) => const getHourName = (hourIndex: number) => formatter.format(new Date(year, month, day, hourIndex));
formatter.format(new Date(year, month, day, hourIndex));
return hourList.map(getHourName); return hourList.map(getHourName);
}; };
export const getWeekdayList = ( export const getWeekdayList = (locales?: string | string[], format: "long" | "short" | "narrow" = "long"): string[] => {
locales?: string | string[],
format: "long" | "short" | "narrow" = "long"
): string[] => {
const monday = new Date(); const monday = new Date();
// set day to monday (w/o +1 it would be sunday) // set day to monday (w/o +1 it would be sunday)
monday.setDate(monday.getDate() - monday.getDay() + 1); monday.setDate(monday.getDate() - monday.getDay() + 1);
@ -86,8 +75,7 @@ export const getWeekdayList = (
weekday: format, weekday: format,
}); });
const getWeekDayName = (weekDayIndex: number) => const getWeekDayName = (weekDayIndex: number) => formatter.format(new Date(year, month, mondayDate + weekDayIndex));
formatter.format(new Date(year, month, mondayDate + weekDayIndex));
return hourList.map(getWeekDayName); return hourList.map(getWeekDayName);
}; };

View file

@ -36,9 +36,7 @@ createRoot(() => {
createDeferred( createDeferred(
on(db, (currentDb) => { on(db, (currentDb) => {
if (currentDb) { if (currentDb) {
const newHash = hashString( const newHash = hashString(new TextDecoder().decode(currentDb.export())).toString();
new TextDecoder().decode(currentDb.export())
).toString();
const oldHash = localStorage.getItem(HASH_STORE_KEY); const oldHash = localStorage.getItem(HASH_STORE_KEY);
@ -48,15 +46,13 @@ createRoot(() => {
localStorage.setItem(HASH_STORE_KEY, newHash); localStorage.setItem(HASH_STORE_KEY, newHash);
} }
} }
}) }),
); );
}); });
}); });
class LocalStorageCacheAdapter { class LocalStorageCacheAdapter {
keys = new Set<string>( keys = new Set<string>(Object.keys(localStorage).filter((key) => key.startsWith(this.prefix)));
Object.keys(localStorage).filter((key) => key.startsWith(this.prefix))
);
prefix = "database"; prefix = "database";
#createKey(cacheName: string, key: string): string { #createKey(cacheName: string, key: string): string {
@ -70,10 +66,7 @@ class LocalStorageCacheAdapter {
try { try {
localStorage.setItem(fullKey, JSON.stringify(value)); localStorage.setItem(fullKey, JSON.stringify(value));
} catch (error: unknown) { } catch (error: unknown) {
if ( if (error instanceof DOMException && error.name === "QUOTA_EXCEEDED_ERR") {
error instanceof DOMException &&
error.name === "QUOTA_EXCEEDED_ERR"
) {
console.error("Storage quota exceeded, not caching new function calls"); console.error("Storage quota exceeded, not caching new function calls");
} }
} }
@ -121,10 +114,7 @@ const createHashKey = (...args: unknown[]) => {
return hashString(stringToHash); return hashString(stringToHash);
}; };
export const cached = <T extends unknown[], R, TT>( export const cached = <T extends unknown[], R, TT>(fn: (...args: T) => R, self?: ThisType<TT>): ((...args: T) => R) => {
fn: (...args: T) => R,
self?: ThisType<TT>
): ((...args: T) => R) => {
const cacheName = hashString(fn.toString()).toString(); const cacheName = hashString(fn.toString()).toString();
// important to return a promise on follow-up calls even if the data is immediately available // important to return a promise on follow-up calls even if the data is immediately available

View file

@ -1,7 +1,7 @@
export const getNameFromRecipient = ( export const getNameFromRecipient = (
joinedNickname: string | null, joinedNickname: string | null,
joinedSystemName: string | null, joinedSystemName: string | null,
joinedProfileName: string | null joinedProfileName: string | null,
) => { ) => {
let name = "Could not determine name"; let name = "Could not determine name";

View file

@ -1,6 +1,5 @@
import { createEffect, createMemo, on, type Accessor } from "solid-js"; import { createMemo, type Accessor } from "solid-js";
import { getDateList, getHourList, getMonthList, getWeekdayList } from "./date"; import { getDateList, getHourList, getMonthList, getWeekdayList } from "./date";
import { cached } from "./db-cache";
import type { MessageOverview, MessageStats, Recipients } from "~/types"; import type { MessageOverview, MessageStats, Recipients } from "~/types";
import { isSameDay } from "date-fns"; import { isSameDay } from "date-fns";
@ -18,10 +17,9 @@ const initialWeekdayMap = [...weekdayNames.keys()];
export const createMessageStatsSources = ( export const createMessageStatsSources = (
messageOverview: Accessor<MessageOverview>, messageOverview: Accessor<MessageOverview>,
recipients: Accessor<Recipients> recipients: Accessor<Recipients>,
) => { ) => {
const initialRecipientMap = () => const initialRecipientMap = () => Object.fromEntries(recipients().map(({ recipientId }) => [recipientId, 0]));
Object.fromEntries(recipients().map(({ recipientId }) => [recipientId, 0]));
const dateList = () => { const dateList = () => {
const currentDmMessagesOverview = messageOverview(); const currentDmMessagesOverview = messageOverview();
@ -63,9 +61,7 @@ export const createMessageStatsSources = (
month[messageDate.getMonth() - 1][message.fromRecipientId] += 1; month[messageDate.getMonth() - 1][message.fromRecipientId] += 1;
// biome-ignore lint/style/noNonNullAssertion: <explanation> // biome-ignore lint/style/noNonNullAssertion: <explanation>
const dateStatsEntry = date.find(({ date }) => const dateStatsEntry = date.find(({ date }) => isSameDay(date, messageDate))!;
isSameDay(date, messageDate)
)!;
// increment the message count of the message's date for this recipient // increment the message count of the message's date for this recipient
dateStatsEntry[message.fromRecipientId] += 1; dateStatsEntry[message.fromRecipientId] += 1;

View file

@ -1,4 +1,4 @@
import { type Component, createResource, Show } from "solid-js"; import { type Component, createResource } from "solid-js";
import type { RouteSectionProps } from "@solidjs/router"; import type { RouteSectionProps } from "@solidjs/router";
import { dmPartnerRecipientQuery, SELF_ID, threadMostUsedWordsQuery, threadSentMessagesOverviewQuery } from "~/db"; import { dmPartnerRecipientQuery, SELF_ID, threadMostUsedWordsQuery, threadSentMessagesOverviewQuery } from "~/db";

View file

@ -1,4 +1,4 @@
import { createEffect, Show, type Accessor, type Component } from "solid-js"; import { Show, type Accessor, type Component } from "solid-js";
import type { ChartData } from "chart.js"; import type { ChartData } from "chart.js";
import { LineChart } from "~/components/ui/charts"; import { LineChart } from "~/components/ui/charts";
import type { MessageStats, Recipients } from "~/types"; import type { MessageStats, Recipients } from "~/types";

View file

@ -1,9 +1,12 @@
import { type Component, type JSX } from "solid-js"; import { createSignal, Show, type Component, type JSX } from "solid-js";
import { type RouteSectionProps, useNavigate } from "@solidjs/router"; import { type RouteSectionProps, useNavigate } from "@solidjs/router";
import { setDb, SQL } from "~/db"; import { setDb, SQL } from "~/db";
import { Portal } from "solid-js/web";
import { Flex } from "~/components/ui/flex";
export const Home: Component<RouteSectionProps> = () => { export const Home: Component<RouteSectionProps> = () => {
const [isLoadingDb, setIsLoadingDb] = createSignal(false);
const navigate = useNavigate(); const navigate = useNavigate();
const onFileChange: JSX.ChangeEventHandler<HTMLInputElement, Event> = (event) => { const onFileChange: JSX.ChangeEventHandler<HTMLInputElement, Event> = (event) => {
@ -12,9 +15,14 @@ export const Home: Component<RouteSectionProps> = () => {
const reader = new FileReader(); const reader = new FileReader();
reader.addEventListener("load", () => { reader.addEventListener("load", () => {
const Uints = new Uint8Array(reader.result as ArrayBuffer); setIsLoadingDb(true);
setDb(new SQL.Database(Uints));
navigate("/overview"); setTimeout(() => {
const Uints = new Uint8Array(reader.result as ArrayBuffer);
setDb(new SQL.Database(Uints));
setIsLoadingDb(false);
navigate("/overview");
}, 10);
}); });
reader.readAsArrayBuffer(file); reader.readAsArrayBuffer(file);
@ -22,9 +30,18 @@ export const Home: Component<RouteSectionProps> = () => {
}; };
return ( return (
<div> <>
<input type="file" accept=".sqlite" onChange={onFileChange}></input> <Portal>
</div> <Show when={isLoadingDb()}>
<Flex alignItems="center" justifyContent="center" class="fixed inset-0 backdrop-blur-lg backdrop-filter">
<p class="font-bold text-2xl">Loading database</p>
</Flex>
</Show>
</Portal>
<div>
<input type="file" accept=".sqlite" onChange={onFileChange}></input>
</div>
</>
); );
}; };