import { useMemo, useState } from "react";
import { useUIState } from "src/useUIState";
import { useEntity, useReplicache } from "src/replicache";
import { BlockProps, BlockLayout } from "./Block";
import {
usePublicationData,
useNormalizedPublicationRecord,
} from "app/(app)/lish/[did]/[publication]/dashboard/PublicationSWRProvider";
import { PublicationPostsList } from "app/(app)/lish/[did]/[publication]/PublicationPostsList";
import { Popover } from "components/Popover";
import { Toggle } from "components/Toggle";
import { SettingsTriggerButton } from "./SettingsTriggerButton";
import { PlaceholderText } from "./PostSizeIcons";
import { CloseTiny } from "components/Icons/CloseTiny";
type PostsListView = "small" | "medium";
export const PostsListBlock = (props: BlockProps & { preview?: boolean }) => {
let isSelected = useUIState(
(s) => !!s.selectedBlocks.find((b) => b.value === props.value),
);
if (props.preview) {
return (
);
}
return (
}
>
);
};
function PostsListBlockContent({ entityID }: { entityID: string }) {
let { data } = usePublicationData();
let publicationRecord = useNormalizedPublicationRecord();
let viewFact = useEntity(entityID, "posts-list/view");
let view: PostsListView = viewFact?.data.value ?? "medium";
let highlightFirstFact = useEntity(
entityID,
"posts-list/highlight-first-post",
);
let highlightFirst = highlightFirstFact?.data.value ?? false;
let filterTagFacts = useEntity(entityID, "posts-list/filter-tag");
let filteredPosts = useMemo(() => {
if (!data?.documents) return data?.documents;
let filterTags = filterTagFacts.map((f) => f.data.value);
if (filterTags.length === 0) return data.documents;
return data.documents.filter((d) =>
d.record.tags?.some((t) => filterTags.includes(t)),
);
}, [data?.documents, filterTagFacts]);
if (data === undefined) return ;
if (!data?.publication) return ;
return (
);
}
function PostsListPlaceholder() {
return (
{[0, 1, 2].map((i) => (
))}
);
}
function PostsListSettingsButton(props: { entityID: string }) {
let { rep } = useReplicache();
let { data } = usePublicationData();
let viewFact = useEntity(props.entityID, "posts-list/view");
let view: PostsListView = viewFact?.data.value ?? "medium";
let highlightFirstFact = useEntity(
props.entityID,
"posts-list/highlight-first-post",
);
let highlightFirst = highlightFirstFact?.data.value ?? false;
let filterTagFacts = useEntity(props.entityID, "posts-list/filter-tag");
let selectedTags = useMemo(
() => filterTagFacts.map((f) => f.data.value),
[filterTagFacts],
);
let [filterByTagEnabled, setFilterByTagEnabled] = useState(
() => selectedTags.length > 0,
);
let allTags = useMemo(() => {
let tagSet = new Set();
for (let doc of data?.documents ?? []) {
for (let tag of doc.record.tags ?? []) tagSet.add(tag);
}
return Array.from(tagSet).sort((a, b) =>
a.localeCompare(b, undefined, { sensitivity: "base" }),
);
}, [data?.documents]);
return (
}
>
List Layout
{(
[
{ value: "small", Icon: SmallIcon },
{ value: "medium", Icon: MedIcon },
] as {
value: PostsListView;
Icon: (props: { selected: boolean }) => React.ReactNode;
}[]
).map((option) => {
let selected =
view === option.value ||
(option.value === "medium" && view !== "small");
return (
e.preventDefault()}
onClick={() => {
if (!rep) return;
rep.mutate.assertFact({
entity: props.entityID,
attribute: "posts-list/view",
data: {
type: "posts-list-view-union",
value: option.value,
},
});
}}
>
{option.value}
);
})}
{
if (!rep) return;
rep.mutate.assertFact({
entity: props.entityID,
attribute: "posts-list/highlight-first-post",
data: { type: "boolean", value: !highlightFirst },
});
}}
>
Highlight First Post
Settings
{
if (filterByTagEnabled) {
// turning off the filter clears any selected tags
if (rep)
for (let fact of filterTagFacts)
rep.mutate.retractFact({ factID: fact.id });
setFilterByTagEnabled(false);
} else {
setFilterByTagEnabled(true);
}
}}
>
Filter by Tag
{filterByTagEnabled && selectedTags.length > 0 && (
e.preventDefault()}
onClick={() => {
if (!rep) return;
for (let fact of filterTagFacts)
rep.mutate.retractFact({ factID: fact.id });
}}
>
clear
)}
{filterByTagEnabled ? (
{allTags.length === 0 ? (
no tags yet
) : (
{allTags.map((tag) => {
let selectedFact = filterTagFacts.find(
(f) => f.data.value === tag,
);
let isSelected = !!selectedFact;
return (
e.preventDefault()}
onClick={() => {
if (!rep) return;
if (selectedFact) {
rep.mutate.retractFact({
factID: selectedFact.id,
});
} else {
rep.mutate.assertFact({
entity: props.entityID,
attribute: "posts-list/filter-tag",
data: { type: "string", value: tag },
});
}
}}
className={`tag flex items-center text-xs rounded-md border px-1 py-0.5 ${
isSelected
? "bg-accent-1 border-accent-1 font-bold text-accent-2"
: "bg-bg-page border-border text-tertiary"
}`}
>
{tag}
{isSelected ? (
) : null}
);
})}
)}
) : null}
);
}
const SmallIcon = ({ selected }: { selected: boolean }) => {
return (
{[0, 1, 2].map((i) => (
{PlaceholderText("lg")}
{PlaceholderText("sm", "60%")}
))}
);
};
const MedIcon = ({ selected }: { selected: boolean }) => {
return (
{[0, 1].map((i) => (
{PlaceholderText("lg")}
{PlaceholderText("md")}
{PlaceholderText("md", "80%")}
{PlaceholderText("sm", "60%")}
))}
);
};