feat(ui): basic dark mode, better day overview chart
This commit is contained in:
parent
91a670c72a
commit
bba7c3554d
8 changed files with 405 additions and 106 deletions
94
src/App.tsx
94
src/App.tsx
|
@ -1,18 +1,104 @@
|
|||
import { Route } from "@solidjs/router";
|
||||
import { type Component } from "solid-js";
|
||||
import { A, Route, Router, useNavigate } from "@solidjs/router";
|
||||
import { createEffect, Show, Suspense, type Component } from "solid-js";
|
||||
import { DmId, GroupId, Home, Overview, preloadDmId, Privacy } from "./pages";
|
||||
|
||||
import "./app.css";
|
||||
import { MetaProvider } from "@solidjs/meta";
|
||||
import { Portal } from "solid-js/web";
|
||||
import { Callout, CalloutTitle, CalloutContent } from "./components/ui/callout";
|
||||
import { dbLoaded } from "./db";
|
||||
import { hasCashedData } from "./lib/db-cache";
|
||||
import { isWasmSupported } from "./lib/utils";
|
||||
import { ColorModeProvider, ColorModeScript, createLocalStorageManager } from "@kobalte/core";
|
||||
import { ModeToggle } from "./components/ui/mode-toggle";
|
||||
|
||||
const NO_DATA_NEEDED_PAGES = ["/", "/privacy"];
|
||||
|
||||
const App: Component = () => {
|
||||
const storageManager = createLocalStorageManager("vite-ui-theme");
|
||||
return (
|
||||
<div class="mx-auto max-w-(--breakpoint-2xl)">
|
||||
<MetaProvider>
|
||||
<Router
|
||||
root={(props) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
createEffect(() => {
|
||||
if (!dbLoaded() && !hasCashedData() && !NO_DATA_NEEDED_PAGES.includes(props.location.pathname)) {
|
||||
navigate("/");
|
||||
}
|
||||
});
|
||||
|
||||
const wasmSupport = isWasmSupported();
|
||||
|
||||
return (
|
||||
<>
|
||||
<ColorModeScript storageType={storageManager.type} />
|
||||
<ColorModeProvider storageManager={storageManager}>
|
||||
<header class="flex justify-end bg-accent p-4">
|
||||
<ModeToggle />
|
||||
</header>
|
||||
<Show when={!wasmSupport}>
|
||||
<Portal>
|
||||
<div class="fixed inset-0 mx-4 flex flex-col items-center justify-center backdrop-blur-lg">
|
||||
<Callout variant="error">
|
||||
Your browser does not support WebAssembly, which is required for this site to work with the
|
||||
big amount of data a signal backup contains.
|
||||
<br />
|
||||
Please try a different browser.
|
||||
</Callout>
|
||||
</div>
|
||||
</Portal>
|
||||
</Show>
|
||||
<Show
|
||||
when={props.location.pathname !== "/" && !dbLoaded() && hasCashedData()}
|
||||
fallback={
|
||||
<Show when={!dbLoaded() && hasCashedData()}>
|
||||
<Callout variant="default" class="m-4">
|
||||
There is currently no backup database loaded, but you can watch statistics that have been
|
||||
cached, meaning only chats you already opened or chats that were preloaded.
|
||||
<br />
|
||||
<A
|
||||
href="/overview"
|
||||
onClick={() => {
|
||||
umami.track("Watch cached statistics");
|
||||
}}
|
||||
>
|
||||
Watch cached statistics
|
||||
</A>
|
||||
</Callout>
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<Callout variant="warning" class="m-4">
|
||||
<CalloutTitle>You are watching cached statistics</CalloutTitle>
|
||||
<CalloutContent>
|
||||
Currently there is no backup database loaded. You can only watch statistics that have been
|
||||
cached, meaning only chats you already opened or chats that were preloaded.
|
||||
<br />
|
||||
<A href="/">Load a backup</A>
|
||||
</CalloutContent>
|
||||
</Callout>
|
||||
</Show>
|
||||
<main class="px-4 md:px-0">
|
||||
<Suspense>{props.children}</Suspense>
|
||||
</main>
|
||||
<footer class="mt-4 flex flex-row justify-end bg-muted p-8">
|
||||
<A href="/privacy">Privacy policy</A>
|
||||
</footer>
|
||||
</ColorModeProvider>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Route path="/" component={Home} />
|
||||
<Route path="/overview" component={Overview} />
|
||||
<Route path="/dm/:dmid" component={DmId} preload={preloadDmId} />
|
||||
<Route path="/group/:groupid" component={GroupId} />
|
||||
<Route path="/privacy" component={Privacy} />
|
||||
</>
|
||||
<Route path="/privacy" component={Privacy} />{" "}
|
||||
</Router>
|
||||
</MetaProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
--accent: 226, 90%, 85%;
|
||||
--accent-foreground: 226, 90%, 57%;
|
||||
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
|
@ -59,8 +59,8 @@
|
|||
--muted: 240 3.7% 15.9%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
|
||||
--accent: 240 3.7% 15.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--accent: 226, 90%, 20%;
|
||||
--accent-foreground: 226, 90%, 57%;
|
||||
|
||||
--popover: 240 10% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
|
|
260
src/components/ui/dropdown-menu.tsx
Normal file
260
src/components/ui/dropdown-menu.tsx
Normal file
|
@ -0,0 +1,260 @@
|
|||
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js"
|
||||
import { splitProps } from "solid-js"
|
||||
|
||||
import * as DropdownMenuPrimitive from "@kobalte/core/dropdown-menu"
|
||||
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
|
||||
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
|
||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub
|
||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
|
||||
|
||||
const DropdownMenu: Component<DropdownMenuPrimitive.DropdownMenuRootProps> = (props) => {
|
||||
return <DropdownMenuPrimitive.Root gutter={4} {...props} />
|
||||
}
|
||||
|
||||
type DropdownMenuContentProps<T extends ValidComponent = "div"> =
|
||||
DropdownMenuPrimitive.DropdownMenuContentProps<T> & {
|
||||
class?: string | undefined
|
||||
}
|
||||
|
||||
const DropdownMenuContent = <T extends ValidComponent = "div">(
|
||||
props: PolymorphicProps<T, DropdownMenuContentProps<T>>
|
||||
) => {
|
||||
const [, rest] = splitProps(props as DropdownMenuContentProps, ["class"])
|
||||
return (
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
class={cn(
|
||||
"z-50 min-w-32 origin-[var(--kb-menu-content-transform-origin)] animate-content-hide overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[expanded]:animate-content-show",
|
||||
props.class
|
||||
)}
|
||||
{...rest}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
type DropdownMenuItemProps<T extends ValidComponent = "div"> =
|
||||
DropdownMenuPrimitive.DropdownMenuItemProps<T> & {
|
||||
class?: string | undefined
|
||||
}
|
||||
|
||||
const DropdownMenuItem = <T extends ValidComponent = "div">(
|
||||
props: PolymorphicProps<T, DropdownMenuItemProps<T>>
|
||||
) => {
|
||||
const [, rest] = splitProps(props as DropdownMenuItemProps, ["class"])
|
||||
return (
|
||||
<DropdownMenuPrimitive.Item
|
||||
class={cn(
|
||||
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
props.class
|
||||
)}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const DropdownMenuShortcut: Component<ComponentProps<"span">> = (props) => {
|
||||
const [, rest] = splitProps(props, ["class"])
|
||||
return <span class={cn("ml-auto text-xs tracking-widest opacity-60", props.class)} {...rest} />
|
||||
}
|
||||
|
||||
const DropdownMenuLabel: Component<ComponentProps<"div"> & { inset?: boolean }> = (props) => {
|
||||
const [, rest] = splitProps(props, ["class", "inset"])
|
||||
return (
|
||||
<div
|
||||
class={cn("px-2 py-1.5 text-sm font-semibold", props.inset && "pl-8", props.class)}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
type DropdownMenuSeparatorProps<T extends ValidComponent = "hr"> =
|
||||
DropdownMenuPrimitive.DropdownMenuSeparatorProps<T> & {
|
||||
class?: string | undefined
|
||||
}
|
||||
|
||||
const DropdownMenuSeparator = <T extends ValidComponent = "hr">(
|
||||
props: PolymorphicProps<T, DropdownMenuSeparatorProps<T>>
|
||||
) => {
|
||||
const [, rest] = splitProps(props as DropdownMenuSeparatorProps, ["class"])
|
||||
return (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
class={cn("-mx-1 my-1 h-px bg-muted", props.class)}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
type DropdownMenuSubTriggerProps<T extends ValidComponent = "div"> =
|
||||
DropdownMenuPrimitive.DropdownMenuSubTriggerProps<T> & {
|
||||
class?: string | undefined
|
||||
children?: JSX.Element
|
||||
}
|
||||
|
||||
const DropdownMenuSubTrigger = <T extends ValidComponent = "div">(
|
||||
props: PolymorphicProps<T, DropdownMenuSubTriggerProps<T>>
|
||||
) => {
|
||||
const [, rest] = splitProps(props as DropdownMenuSubTriggerProps, ["class", "children"])
|
||||
return (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
class={cn(
|
||||
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
|
||||
props.class
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
{props.children}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="ml-auto size-4"
|
||||
>
|
||||
<path d="M9 6l6 6l-6 6" />
|
||||
</svg>
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
)
|
||||
}
|
||||
|
||||
type DropdownMenuSubContentProps<T extends ValidComponent = "div"> =
|
||||
DropdownMenuPrimitive.DropdownMenuSubContentProps<T> & {
|
||||
class?: string | undefined
|
||||
}
|
||||
|
||||
const DropdownMenuSubContent = <T extends ValidComponent = "div">(
|
||||
props: PolymorphicProps<T, DropdownMenuSubContentProps<T>>
|
||||
) => {
|
||||
const [, rest] = splitProps(props as DropdownMenuSubContentProps, ["class"])
|
||||
return (
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
class={cn(
|
||||
"z-50 min-w-32 origin-[var(--kb-menu-content-transform-origin)] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in",
|
||||
props.class
|
||||
)}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
type DropdownMenuCheckboxItemProps<T extends ValidComponent = "div"> =
|
||||
DropdownMenuPrimitive.DropdownMenuCheckboxItemProps<T> & {
|
||||
class?: string | undefined
|
||||
children?: JSX.Element
|
||||
}
|
||||
|
||||
const DropdownMenuCheckboxItem = <T extends ValidComponent = "div">(
|
||||
props: PolymorphicProps<T, DropdownMenuCheckboxItemProps<T>>
|
||||
) => {
|
||||
const [, rest] = splitProps(props as DropdownMenuCheckboxItemProps, ["class", "children"])
|
||||
return (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
class={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
props.class
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
<span class="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="size-4"
|
||||
>
|
||||
<path d="M5 12l5 5l10 -10" />
|
||||
</svg>
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{props.children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
)
|
||||
}
|
||||
|
||||
type DropdownMenuGroupLabelProps<T extends ValidComponent = "span"> =
|
||||
DropdownMenuPrimitive.DropdownMenuGroupLabelProps<T> & {
|
||||
class?: string | undefined
|
||||
}
|
||||
|
||||
const DropdownMenuGroupLabel = <T extends ValidComponent = "span">(
|
||||
props: PolymorphicProps<T, DropdownMenuGroupLabelProps<T>>
|
||||
) => {
|
||||
const [, rest] = splitProps(props as DropdownMenuGroupLabelProps, ["class"])
|
||||
return (
|
||||
<DropdownMenuPrimitive.GroupLabel
|
||||
class={cn("px-2 py-1.5 text-sm font-semibold", props.class)}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
type DropdownMenuRadioItemProps<T extends ValidComponent = "div"> =
|
||||
DropdownMenuPrimitive.DropdownMenuRadioItemProps<T> & {
|
||||
class?: string | undefined
|
||||
children?: JSX.Element
|
||||
}
|
||||
|
||||
const DropdownMenuRadioItem = <T extends ValidComponent = "div">(
|
||||
props: PolymorphicProps<T, DropdownMenuRadioItemProps<T>>
|
||||
) => {
|
||||
const [, rest] = splitProps(props as DropdownMenuRadioItemProps, ["class", "children"])
|
||||
return (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
class={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
props.class
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
<span class="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="size-2 fill-current"
|
||||
>
|
||||
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
|
||||
</svg>
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{props.children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuGroupLabel,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem
|
||||
}
|
33
src/components/ui/mode-toggle.tsx
Normal file
33
src/components/ui/mode-toggle.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { useColorMode } from "@kobalte/core";
|
||||
|
||||
import { Button } from "./button";
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "./dropdown-menu";
|
||||
import { Sun, Moon, Laptop } from "lucide-solid";
|
||||
|
||||
export function ModeToggle() {
|
||||
const { setColorMode } = useColorMode();
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as={Button<"button">} variant="ghost" size="sm" class="w-9 px-0">
|
||||
<Sun class="dark:-rotate-90 size-6 rotate-0 scale-100 transition-all dark:scale-0" />
|
||||
<Moon class="absolute size-6 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||
<span class="sr-only">Toggle theme</span>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem onSelect={() => setColorMode("light")}>
|
||||
<Sun class="mr-2 size-4" />
|
||||
<span>Light</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => setColorMode("dark")}>
|
||||
<Moon class="mr-2 size-4" />
|
||||
<span>Dark</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => setColorMode("system")}>
|
||||
<Laptop class="mr-2 size-4" />
|
||||
<span>System</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
|
@ -1,14 +1,5 @@
|
|||
/* @refresh reload */
|
||||
import { MetaProvider } from "@solidjs/meta";
|
||||
import { Router, useNavigate } from "@solidjs/router";
|
||||
import { Portal, render } from "solid-js/web";
|
||||
import { render } from "solid-js/web";
|
||||
import App from "./App";
|
||||
import { hasCashedData } from "./lib/db-cache";
|
||||
import { createEffect, Show } from "solid-js";
|
||||
import { dbLoaded } from "./db";
|
||||
import { Callout, CalloutContent, CalloutTitle } from "./components/ui/callout";
|
||||
import { A } from "./components/ui/A";
|
||||
import { isWasmSupported } from "./lib/utils";
|
||||
|
||||
const root = document.getElementById("root");
|
||||
|
||||
|
@ -26,82 +17,6 @@ if (import.meta.env.DEV && !("umami" in window)) {
|
|||
};
|
||||
}
|
||||
|
||||
const NO_DATA_NEEDED_PAGES = ["/", "/privacy"];
|
||||
|
||||
if (root) {
|
||||
render(
|
||||
() => (
|
||||
<div class="mx-auto max-w-(--breakpoint-2xl)">
|
||||
<MetaProvider>
|
||||
<Router
|
||||
root={(props) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
createEffect(() => {
|
||||
if (!dbLoaded() && !hasCashedData() && !NO_DATA_NEEDED_PAGES.includes(props.location.pathname)) {
|
||||
navigate("/");
|
||||
}
|
||||
});
|
||||
|
||||
const wasmSupport = isWasmSupported();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Show when={!wasmSupport}>
|
||||
<Portal>
|
||||
<div class="fixed inset-0 mx-4 flex flex-col items-center justify-center backdrop-blur-lg">
|
||||
<Callout variant="error">
|
||||
Your browser does not support WebAssembly, which is required for this site to work with the
|
||||
big amount of data a signal backup contains.
|
||||
<br />
|
||||
Please try a different browser.
|
||||
</Callout>
|
||||
</div>
|
||||
</Portal>
|
||||
</Show>
|
||||
<Show
|
||||
when={props.location.pathname !== "/" && !dbLoaded() && hasCashedData()}
|
||||
fallback={
|
||||
<Show when={!dbLoaded() && hasCashedData()}>
|
||||
<Callout variant="default" class="m-4">
|
||||
There is currently no backup database loaded, but you can watch statistics that have been
|
||||
cached, meaning only chats you already opened or chats that were preloaded.
|
||||
<br />
|
||||
<A
|
||||
href="/overview"
|
||||
onClick={() => {
|
||||
umami.track("Watch cached statistics");
|
||||
}}
|
||||
>
|
||||
Watch cached statistics
|
||||
</A>
|
||||
</Callout>
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<Callout variant="warning" class="m-4">
|
||||
<CalloutTitle>You are watching cached statistics</CalloutTitle>
|
||||
<CalloutContent>
|
||||
Currently there is no backup database loaded. You can only watch statistics that have been
|
||||
cached, meaning only chats you already opened or chats that were preloaded.
|
||||
<br />
|
||||
<A href="/">Load a backup</A>
|
||||
</CalloutContent>
|
||||
</Callout>
|
||||
</Show>
|
||||
<main>{props.children}</main>
|
||||
<footer class="mt-4 flex flex-row justify-end bg-muted p-8">
|
||||
<A href="/privacy">Privacy policy</A>
|
||||
</footer>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
>
|
||||
<App />
|
||||
</Router>
|
||||
</MetaProvider>
|
||||
</div>
|
||||
),
|
||||
root,
|
||||
);
|
||||
render(() => <App />, root);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ export const DmMessagesPerDate: Component<{
|
|||
label: "Total",
|
||||
data: currentDmMessagesValues.map((row) => row.totalMessages),
|
||||
borderWidth: 2,
|
||||
pointRadius: 0,
|
||||
pointHitRadius: 6,
|
||||
},
|
||||
...currentRecipients.map((recipient) => {
|
||||
return {
|
||||
|
@ -28,6 +30,8 @@ export const DmMessagesPerDate: Component<{
|
|||
label: recipient.name.toString(),
|
||||
data: currentDmMessagesValues.map((date) => date[recipient.recipientId]),
|
||||
borderWidth: 2,
|
||||
pointRadius: 0,
|
||||
pointHitRadius: 6,
|
||||
};
|
||||
}),
|
||||
],
|
||||
|
@ -41,7 +45,7 @@ export const DmMessagesPerDate: Component<{
|
|||
<LineChart
|
||||
options={{
|
||||
normalized: true,
|
||||
aspectRatio: 3,
|
||||
responsive: true,
|
||||
plugins: {
|
||||
zoom: {
|
||||
pan: {
|
||||
|
@ -61,6 +65,7 @@ export const DmMessagesPerDate: Component<{
|
|||
},
|
||||
}}
|
||||
data={currentDateChartData()}
|
||||
class="max-h-96"
|
||||
/>
|
||||
)}
|
||||
</Show>
|
||||
|
|
|
@ -23,7 +23,7 @@ export const DmOverview: Component<{
|
|||
};
|
||||
|
||||
return (
|
||||
<Grid cols={1} colsMd={2} class="my-12 min-w-[35rem] gap-y-8 text-sm">
|
||||
<Grid cols={1} colsMd={2} class="my-12 w-full gap-y-8 text-sm md:min-w-[35rem]">
|
||||
<Flex flexDirection="row" justifyContent="evenly" class="bg-amber-200 p-2 text-amber-900">
|
||||
<Flex alignItems="center" justifyContent="center" class="min-w-16">
|
||||
<CalendarArrowDown class="h-8 w-8" />
|
||||
|
|
|
@ -122,16 +122,16 @@ export const Home: Component<RouteSectionProps> = () => {
|
|||
</Flex>
|
||||
</Portal>
|
||||
<Title>Signal stats</Title>
|
||||
<form class="flex flex-col gap-y-8 p-8" onSubmit={onSubmit}>
|
||||
<form class="mx-auto flex w-full flex-col gap-y-8 p-8 md:w-fit" onSubmit={onSubmit}>
|
||||
<TextField onChange={(value) => setPassphrase(value)}>
|
||||
<TextFieldLabel>Passphrase</TextFieldLabel>
|
||||
<TextFieldInput type="password" class="max-w-md" />
|
||||
<TextFieldInput type="password" class="w-full md:w-sm" />
|
||||
</TextField>
|
||||
<Flex
|
||||
ref={dropzone.setRef}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
class="relative min-h-32 min-w-96 max-w-xl rounded-lg border-4 border-border border-dashed"
|
||||
class="relative min-h-40 w-full rounded-lg border-4 border-border border-dashed md:w-xl"
|
||||
classList={{
|
||||
"border-ring": dropzone.isDragging(),
|
||||
}}
|
||||
|
@ -154,7 +154,7 @@ export const Home: Component<RouteSectionProps> = () => {
|
|||
{backupFile() ? backupFile()?.name : "or drop the file here"}
|
||||
</span>
|
||||
</Flex>
|
||||
<Button type="submit" class="max-w-72">
|
||||
<Button type="submit" class="max-w-72 self-end md:w-sm">
|
||||
Decrypt and load backup
|
||||
</Button>
|
||||
</form>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue