From f9bced1e1197d334e90efcba913c722d36d6450b Mon Sep 17 00:00:00 2001 From: celine Date: Wed, 18 Feb 2026 23:03:48 -0500 Subject: [PATCH] added combobox and populated analytics page --- .../p/[didOrHandle]/ProfileHeader.tsx | 1 - .../publish/BskyPostEditorProsemirror.tsx | 2 - .../[did]/[publication]/[rkey]/ThreadPage.tsx | 1 - .../dashboard/PublicationAnalytics.tsx | 177 +++++++++++++- .../dashboard/PublicationSWRProvider.tsx | 6 +- .../dashboard/PublicationSubscribers.tsx | 1 - appview/index.ts | 1 - components/Blocks/BlockCommandBar.tsx | 218 +++++------------- components/Tags.tsx | 1 - 9 files changed, 222 insertions(+), 186 deletions(-) diff --git a/app/(home-pages)/p/[didOrHandle]/ProfileHeader.tsx b/app/(home-pages)/p/[didOrHandle]/ProfileHeader.tsx index 3fb2b0fd..2cf55f2e 100644 --- a/app/(home-pages)/p/[didOrHandle]/ProfileHeader.tsx +++ b/app/(home-pages)/p/[didOrHandle]/ProfileHeader.tsx @@ -42,7 +42,6 @@ export const ProfileHeader = (props: { @{props.profile.handle} ); - console.log(props.profile); return (
{ - console.log("view", view); if (!view) return; const { from, to } = range; const tr = view.state.tr; @@ -437,7 +436,6 @@ export const addMentionToEditor = ( }); tr.insert(from, atMentionNode); } - console.log("yo", mention); // Add a space after the mention tr.insertText(" ", from + 1); diff --git a/app/lish/[did]/[publication]/[rkey]/ThreadPage.tsx b/app/lish/[did]/[publication]/[rkey]/ThreadPage.tsx index e8951d4f..180987bb 100644 --- a/app/lish/[did]/[publication]/[rkey]/ThreadPage.tsx +++ b/app/lish/[did]/[publication]/[rkey]/ThreadPage.tsx @@ -298,7 +298,6 @@ const ReplyPost = (props: { e.stopPropagation(); props.toggleCollapsed(parentPostUri); - console.log("reply clicked"); }} /> diff --git a/app/lish/[did]/[publication]/dashboard/PublicationAnalytics.tsx b/app/lish/[did]/[publication]/dashboard/PublicationAnalytics.tsx index d76e7940..fb2b6cb7 100644 --- a/app/lish/[did]/[publication]/dashboard/PublicationAnalytics.tsx +++ b/app/lish/[did]/[publication]/dashboard/PublicationAnalytics.tsx @@ -2,16 +2,34 @@ import { ArrowRightTiny } from "components/Icons/ArrowRightTiny"; import { UpgradeContent } from "../UpgradeModal"; import { Popover } from "components/Popover"; import { DatePicker } from "components/DatePicker"; -import { useState } from "react"; +import { useMemo, useState } from "react"; import { useLocalizedDate } from "src/hooks/useLocalizedDate"; import type { DateRange } from "react-day-picker"; import { usePublicationData } from "./PublicationSWRProvider"; +import { + Combobox, + ComboboxResult, + useComboboxState, +} from "components/Combobox"; +import { Input } from "components/Input"; + +type referrorType = { iconSrc: string; name: string; viewCount: string }; +let refferors = [ + { iconSrc: "", name: "Bluesky", viewCount: "12k" }, + { iconSrc: "", name: "Reddit", viewCount: "1.2k" }, + { iconSrc: "", name: "X", viewCount: "583" }, + { iconSrc: "", name: "Google", viewCount: "12" }, +]; export const PublicationAnalytics = () => { let isPro = true; let { data: publication } = usePublicationData(); let [dateRange, setDateRange] = useState({ from: undefined }); + let [selectedPost, setSelectedPost] = useState(undefined); + let [selectedReferror, setSelectedReferror] = useState< + referrorType | undefined + >(undefined); if (!isPro) return ( @@ -21,12 +39,33 @@ export const PublicationAnalytics = () => { ); return ( -
+
+
+
+

Subscribers

+ +
+
+

Traffic

- + + + {selectedReferror && ( + <> + + {selectedReferror.name} + + )}
{ pubStartDate={publication?.publication?.indexed_at} />
-
+
+
+ {" "} +
- - {/*
subscriber count over time
-
Top Referrers
*/}
); }; -const PostSelector = () => { - return
Total
; +const PostSelector = (props: { + selectedPost: string | undefined; + setSelectedPost: (s: string | undefined) => void; +}) => { + let { data } = usePublicationData(); + let { documents } = data || {}; + + let [highlighted, setHighlighted] = useState(undefined); + let [searchValue, setSearchValue] = useState(""); + + let open = useComboboxState((s) => s.open); + let posts = documents?.map((doc) => doc.record.title); + let filteredPosts = useMemo( + () => + posts && + posts.filter((post) => + post.toLowerCase().includes(searchValue.toLowerCase()), + ), + [searchValue], + ); + + let filteredPostsWithClear = ["All Posts", ...(filteredPosts || [])]; + + return ( + setSearchValue(e.target.value)} + onClick={(e) => e.stopPropagation()} + onPointerDown={(e) => e.stopPropagation()} + /> + ) : ( + + ) + } + results={filteredPostsWithClear || []} + highlighted={highlighted} + setHighlighted={setHighlighted} + onSelect={() => { + props.setSelectedPost(highlighted); + }} + sideOffset={2} + > + {filteredPostsWithClear.map((post) => { + if (post === "All Posts") + return ( + <> + { + props.setSelectedPost(undefined); + }} + highlighted={highlighted} + setHighlighted={setHighlighted} + > + All Posts + + {filteredPosts && filteredPosts.length !== 0 && ( +
+ )} + + ); + return ( + { + props.setSelectedPost(post); + }} + highlighted={highlighted} + setHighlighted={setHighlighted} + > + {post} + + ); + })} +
+ ); }; const DateRangeSelector = (props: { @@ -57,9 +182,6 @@ const DateRangeSelector = (props: { let currentDate = new Date(); - console.log("dateRange" + props.dateRange.from?.toISOString()); - console.log("pubstart" + props.pubStartDate); - let startDate = useLocalizedDate( props.dateRange.from?.toISOString() || props.pubStartDate || @@ -133,3 +255,34 @@ const DateRangeSelector = (props: { ); }; + +const TopReferrors = (props: { + refferors: referrorType[]; + setSelectedReferror: (ref: referrorType) => void; + selectedReferror: referrorType | undefined; +}) => { + return ( +
+ {props.refferors.map((ref) => { + let selected = ref === props.selectedReferror; + return ( + <> + +
+ + ); + })} +
+ ); +}; diff --git a/app/lish/[did]/[publication]/dashboard/PublicationSWRProvider.tsx b/app/lish/[did]/[publication]/dashboard/PublicationSWRProvider.tsx index 4ba0fd87..65217a34 100644 --- a/app/lish/[did]/[publication]/dashboard/PublicationSWRProvider.tsx +++ b/app/lish/[did]/[publication]/dashboard/PublicationSWRProvider.tsx @@ -12,7 +12,8 @@ import { // Derive all types from the RPC return type export type PublicationData = GetPublicationDataReturnType["result"]; -export type PublishedDocument = NonNullable["documents"][number]; +export type PublishedDocument = + NonNullable["documents"][number]; export type PublicationDraft = NonNullable["drafts"][number]; const PublicationContext = createContext({ name: "", did: "" }); @@ -24,7 +25,6 @@ export function PublicationSWRDataProvider(props: { }) { let key = `publication-data-${props.publication_did}-${props.publication_rkey}`; useEffect(() => { - console.log("UPDATING"); mutate(key, props.publication_data); }, [props.publication_data]); return ( @@ -64,7 +64,7 @@ export function useNormalizedPublicationRecord(): NormalizedPublication | null { const { data } = usePublicationData(); return useMemo( () => normalizePublicationRecord(data?.publication?.record), - [data?.publication?.record] + [data?.publication?.record], ); } diff --git a/app/lish/[did]/[publication]/dashboard/PublicationSubscribers.tsx b/app/lish/[did]/[publication]/dashboard/PublicationSubscribers.tsx index ac1b27b2..aede5c80 100644 --- a/app/lish/[did]/[publication]/dashboard/PublicationSubscribers.tsx +++ b/app/lish/[did]/[publication]/dashboard/PublicationSubscribers.tsx @@ -175,7 +175,6 @@ const SubscriberListItem = (props: { // ); // props.setCheckedSubscribers(newCheckedSubscribers); // } - // console.log(props.checkedSubscribers); // }} // > <> diff --git a/appview/index.ts b/appview/index.ts index 3588b659..8c1b4ef0 100644 --- a/appview/index.ts +++ b/appview/index.ts @@ -374,7 +374,6 @@ async function handleEvent(evt: Event) { embedRecord.embed?.media?.external?.uri?.includes(QUOTE_PARAM); if (!hasQuoteParam) return; - console.log("FOUND EMBED!!!"); // Now validate the record since we know it contains our quote param let record = AppBskyFeedPost.validateRecord(evt.record); diff --git a/components/Blocks/BlockCommandBar.tsx b/components/Blocks/BlockCommandBar.tsx index 3381de97..b9c4b0b6 100644 --- a/components/Blocks/BlockCommandBar.tsx +++ b/components/Blocks/BlockCommandBar.tsx @@ -1,12 +1,10 @@ -import { useEffect, useRef, useState } from "react"; -import * as Popover from "@radix-ui/react-popover"; +import { useState } from "react"; import { blockCommands } from "./BlockCommands"; import { useReplicache } from "src/replicache"; import { useEntitySetContext } from "components/EntitySetProvider"; -import { NestedCardThemeProvider } from "components/ThemeManager/ThemeProvider"; -import { UndoManager } from "src/undoManager"; import { useLeafletPublicationData } from "components/PageSWRDataProvider"; import { setEditorState, useEditorStates } from "src/state/useEditorState"; +import { Combobox, ComboboxResult } from "components/Combobox"; type Props = { parent: string; @@ -25,8 +23,6 @@ export const BlockCommandBar = ({ props: Props; searchValue: string; }) => { - let ref = useRef(null); - let [highlighted, setHighlighted] = useState(undefined); let { rep, undoManager } = useReplicache(); @@ -51,173 +47,67 @@ export const BlockCommandBar = ({ const matchesName = command.name .toLocaleLowerCase() .includes(lowerSearchValue); - const matchesAlternate = command.alternateNames?.some((altName) => - altName.toLocaleLowerCase().includes(lowerSearchValue) - ) ?? false; + const matchesAlternate = + command.alternateNames?.some((altName) => + altName.toLocaleLowerCase().includes(lowerSearchValue), + ) ?? false; const matchesSearch = matchesName || matchesAlternate; const isVisible = !pub || !command.hiddenInPublication; return matchesSearch && isVisible; }); - useEffect(() => { - if ( - !highlighted || - !commandResults.find((result) => result.name === highlighted) - ) - setHighlighted(commandResults[0]?.name); - if (commandResults.length === 1) { - setHighlighted(commandResults[0].name); - } - }, [commandResults, setHighlighted, highlighted]); - - useEffect(() => { - let listener = async (e: KeyboardEvent) => { - let reverseDir = ref.current?.dataset.side === "top"; - let currentHighlightIndex = commandResults.findIndex( - (command: { name: string }) => - highlighted && command.name === highlighted, - ); - - if (reverseDir ? e.key === "ArrowUp" : e.key === "ArrowDown") { - setHighlighted( - commandResults[ - currentHighlightIndex === commandResults.length - 1 || - currentHighlightIndex === undefined - ? 0 - : currentHighlightIndex + 1 - ].name, - ); - return; - } - if (reverseDir ? e.key === "ArrowDown" : e.key === "ArrowUp") { - setHighlighted( - commandResults[ - currentHighlightIndex === 0 || - currentHighlightIndex === undefined || - currentHighlightIndex === -1 - ? commandResults.length - 1 - : currentHighlightIndex - 1 - ].name, - ); - return; - } - - // on enter, select the highlighted item - if (e.key === "Enter") { + return ( + r.name)} + highlighted={highlighted} + setHighlighted={setHighlighted} + onSelect={async () => { + let command = commandResults.find((c) => c.name === highlighted); + if (!command || !rep) return; undoManager.startGroup(); - e.preventDefault(); - rep && - (await commandResults[currentHighlightIndex]?.onSelect( - rep, - { - ...props, - entity_set: entity_set.set, - }, - undoManager, - )); + await command.onSelect( + rep, + { ...props, entity_set: entity_set.set }, + undoManager, + ); undoManager.endGroup(); - return; - } - }; - - window.addEventListener("keydown", listener); - - return () => window.removeEventListener("keydown", listener); - }, [highlighted, setHighlighted, commandResults, rep, entity_set.set, props]); - - return ( - { - if (!open) { - clearCommandSearchText(); - } }} + onOpenChange={() => clearCommandSearchText()} > - - - e.preventDefault()} - className={` - commandMenuContent group/cmd-menu - z-20 w-[264px] - flex data-[side=top]:items-end items-start - `} - > - -
- {commandResults.length === 0 ? ( -
- No blocks found -
- ) : ( - commandResults.map((result, index) => ( -
- { - rep && - result.onSelect( - rep, - { - ...props, - entity_set: entity_set.set, - }, - undoManager, - ); - }} - highlighted={highlighted} - setHighlighted={(highlighted) => - setHighlighted(highlighted) - } - /> - {commandResults[index + 1] && - result.type !== commandResults[index + 1].type && ( -
- )} -
- )) + {commandResults.length === 0 ? ( +
+ No blocks found +
+ ) : ( + commandResults.map((result, index) => ( +
+ { + rep && + result.onSelect( + rep, + { ...props, entity_set: entity_set.set }, + undoManager, + ); + }} + highlighted={highlighted} + setHighlighted={setHighlighted} + > +
+ {result.icon} +
+ {result.name} +
+ {commandResults[index + 1] && + result.type !== commandResults[index + 1].type && ( +
)} -
- - - - - ); -}; - -const CommandResult = (props: { - name: string; - icon: React.ReactNode; - onSelect: () => void; - highlighted: string | undefined; - setHighlighted: (state: string | undefined) => void; -}) => { - let isHighlighted = props.highlighted === props.name; - - return ( - +
+ )) + )} +
); }; -function usePublicationContext() { - throw new Error("Function not implemented."); -} diff --git a/components/Tags.tsx b/components/Tags.tsx index d2266e9a..a5af994b 100644 --- a/components/Tags.tsx +++ b/components/Tags.tsx @@ -109,7 +109,6 @@ export const TagSearchInput = (props: { } function selectTag(tag: string) { - console.log("selected " + tag); props.setSelectedTags([...props.selectedTags, tag]); clearTagInput(); } -- 2.51.2