diff --git a/src/components/permission-button.tsx b/src/components/permission-button.tsx index 1bd281c..24db7f7 100644 --- a/src/components/permission-button.tsx +++ b/src/components/permission-button.tsx @@ -5,7 +5,7 @@ import Tooltip from "./tooltip"; export interface PermissionButtonProps { scope: "create" | "update" | "delete" | "blob"; - tooltip: string; + tooltip?: string; class?: string; disabledClass?: string; onClick: () => void; @@ -28,11 +28,16 @@ export const PermissionButton = (props: PermissionButtonProps) => { "flex items-center rounded-sm p-1.5 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600"; const disabledClass = props.disabledClass || "flex items-center rounded-sm p-1.5 opacity-40"; - return ( - - - + const tooltip = () => + hasPermission() ? props.tooltip : `${props.tooltip ?? ""} (permission required)`.trimStart(); + + const button = ( + ); + + return props.tooltip ? + {button} + : button; }; diff --git a/src/views/blob.tsx b/src/views/blob.tsx index a47efa9..f461222 100644 --- a/src/views/blob.tsx +++ b/src/views/blob.tsx @@ -45,8 +45,8 @@ export const BlobView = (props: { pds: string; repo: string }) => { -
-
+
+

{blobs()?.length} blob{(blobs()?.length ?? 0 > 1) ? "s" : ""}

diff --git a/src/views/collection.tsx b/src/views/collection.tsx index d247da0..3fedba4 100644 --- a/src/views/collection.tsx +++ b/src/views/collection.tsx @@ -4,7 +4,7 @@ import { $type, ActorIdentifier, InferXRPCBodyOutput } from "@atcute/lexicons"; import * as TID from "@atcute/tid"; import { Title } from "@solidjs/meta"; import { A, useBeforeLeave, useParams, useSearchParams } from "@solidjs/router"; -import { createMemo, createResource, createSignal, For, onMount, Show } from "solid-js"; +import { createMemo, createResource, createSignal, For, onCleanup, onMount, Show } from "solid-js"; import { createStore } from "solid-js/store"; import { agent } from "../auth/state"; import { Button } from "../components/button.jsx"; @@ -13,9 +13,8 @@ import { JSONType, JSONValue } from "../components/json.jsx"; import { Modal } from "../components/modal.jsx"; import { addNotification, removeNotification } from "../components/notification.jsx"; import { PermissionButton } from "../components/permission-button.jsx"; -import { StickyOverlay } from "../components/sticky.jsx"; -import { TextInput } from "../components/text-input.jsx"; import Tooltip from "../components/tooltip.jsx"; +import { canHover } from "../layout.jsx"; import { resolvePDS } from "../utils/api.js"; import { localDateFromTimestamp } from "../utils/date.js"; import { @@ -85,6 +84,7 @@ const CollectionView = () => { const did = params.repo; let pds: string; let rpc: Client; + let filterInputRef: HTMLInputElement | undefined; const cacheKey = () => `${params.pds}/${params.repo}/${params.collection}`; @@ -103,6 +103,19 @@ const CollectionView = () => { window.scrollTo(0, cached.scrollY); }); } + + const handleKeyDown = (e: KeyboardEvent) => { + if ( + e.key === "/" && + !["INPUT", "TEXTAREA"].includes((e.target as HTMLElement)?.tagName) && + !document.querySelector("[data-modal]") + ) { + e.preventDefault(); + filterInputRef?.focus(); + } + }; + document.addEventListener("keydown", handleKeyDown); + onCleanup(() => document.removeEventListener("keydown", handleKeyDown)); }); useBeforeLeave((e) => { @@ -241,190 +254,231 @@ const CollectionView = () => { <> {params.collection} - PDSls -
- -
-
- -
- + {/* Tab bar */} +
+
+ Records + + Jetstream + +
+ +
+ + + - } - /> - { - setRecreate(true); - setOpenDelete(true); - }} - > - - - { - setRecreate(false); - setOpenDelete(true); - }} - class="flex items-center rounded-md p-1.5 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" - > - - - } - /> - -
- setOpenDelete(false)} - contentClass="dark:bg-dark-300 dark:shadow-dark-700 pointer-events-auto rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md dark:border-neutral-700" - > -

- {recreate() ? "Recreate" : "Delete"}{" "} - {records.filter((r) => r.toDelete).length} records? -

-
- - -
-
-
- setFilter(e.currentTarget.value)} - class="grow text-sm" - /> - - + + + - - - -
- 1}> -
- -
- - {records.filter((rec) => rec.toDelete).length} - / - - {filter() ? filteredRecords().length : records.length} records -
-
- - - + + + + + + + { + setRecords({ from: 0, to: records.length - 1 }, "toDelete", false); + setLastSelected(undefined); + setBatchDelete(!batchDelete()); + }} + > + + {batchDelete() ? "Cancel" : "Manage"} + +
+ +
+ + {/* Record list */} +
+ 0} + fallback={ + + {filter() ? "No records match filter" : "No records"} + + } + > + + {(record, index) => { + const rounding = () => { + const recs = filteredRecords(); + const prevSelected = recs[index() - 1]?.toDelete; + const nextSelected = recs[index() + 1]?.toDelete; + return `${!prevSelected ? "rounded-t" : ""} ${!nextSelected ? "rounded-b" : ""}`; + }; + return ( + <> + +
{ + handleSelectionClick(e, index()); + setRecords(index(), "toDelete", !record.toDelete); + }} + > + +
- -
+ + + + - -
-
+ + ); + }} + +
+
+
+ + {/* Confirm delete/recreate modal */} + setOpenDelete(false)} + contentClass="dark:bg-dark-300 dark:shadow-dark-700 pointer-events-auto rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md dark:border-neutral-700" + > +

+ {recreate() ? "Recreate" : "Delete"} {records.filter((r) => r.toDelete).length} records? +

+
+ + +
+
+ + {/* Fixed bottom panel */} + 1}> +
+ {/* Filter */} +
{ + const input = e.currentTarget.querySelector("input"); + if (e.target !== input) input?.focus(); + }} + > + + setFilter(e.currentTarget.value)} + /> + + + / +
- -
- - {(record, index) => { - const rounding = () => { - const recs = filteredRecords(); - const prevSelected = recs[index() - 1]?.toDelete; - const nextSelected = recs[index() + 1]?.toDelete; - return `${!prevSelected ? "rounded-t" : ""} ${!nextSelected ? "rounded-b" : ""}`; - }; - return ( - <> - -
{ - handleSelectionClick(e, index()); - setRecords(index(), "toDelete", !record.toDelete); - }} - > - -
-
- - - - + + {/* Pagination */} +
+ + + {/* Record count */} +
+ + {records.filter((rec) => rec.toDelete).length} + / + + {filter() ? filteredRecords().length : records.length} records +
+ + {/* Load more */} +
+ + + +
+
-
+
); diff --git a/src/views/pds.tsx b/src/views/pds.tsx index 07fe526..2a141da 100644 --- a/src/views/pds.tsx +++ b/src/views/pds.tsx @@ -147,7 +147,7 @@ export const PdsView = () => {
-
+
{(repo) => }
@@ -243,8 +243,8 @@ export const PdsView = () => {
-
-
+
+

{repos()?.length} loaded !r.active)}> diff --git a/src/views/repo.tsx b/src/views/repo.tsx index e705e4b..5445924 100644 --- a/src/views/repo.tsx +++ b/src/views/repo.tsx @@ -463,7 +463,7 @@ export const RepoView = () => { return (