mirror of
https://github.com/linsa-io/linsa.git
synced 2026-04-24 09:18:37 +02:00
feat: guest auth (#141)
* feat: Start using guest auth * feat: Implement more functionality to work as guest * chore: update package and tweak public route * chore: update root package json * chore: update web package json --------- Co-authored-by: Aslam H <iupin5212@gmail.com>
This commit is contained in:
@@ -13,7 +13,7 @@ interface GraphNode {
|
||||
|
||||
interface AutocompleteProps {
|
||||
topics: GraphNode[]
|
||||
onSelect: (topic: GraphNode) => void
|
||||
onSelect: (topic: string) => void
|
||||
onInputChange: (value: string) => void
|
||||
}
|
||||
|
||||
@@ -46,18 +46,16 @@ export function Autocomplete({ topics = [], onSelect, onInputChange }: Autocompl
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(topic: GraphNode) => {
|
||||
setInputValue(topic.prettyName)
|
||||
// setInputValue(topicPrettyName)
|
||||
setOpen(false)
|
||||
onSelect(topic)
|
||||
onSelect(topic.name)
|
||||
},
|
||||
[onSelect]
|
||||
)
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (e.key === "Enter" && filteredTopics.length > 0) {
|
||||
handleSelect(filteredTopics[0])
|
||||
} else if ((e.key === "Backspace" || e.key === "Delete") && inputRef.current?.value === "") {
|
||||
if ((e.key === "Backspace" || e.key === "Delete") && inputRef.current?.value === "") {
|
||||
setOpen(true)
|
||||
setIsInitialOpen(true)
|
||||
}
|
||||
@@ -65,7 +63,7 @@ export function Autocomplete({ topics = [], onSelect, onInputChange }: Autocompl
|
||||
setHasInteracted(true)
|
||||
}
|
||||
},
|
||||
[filteredTopics, handleSelect, hasInteracted]
|
||||
[hasInteracted]
|
||||
)
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
@@ -143,6 +141,7 @@ export function Autocomplete({ topics = [], onSelect, onInputChange }: Autocompl
|
||||
{filteredTopics.map((topic, index) => (
|
||||
<CommandItem
|
||||
key={index}
|
||||
value={topic.name}
|
||||
onSelect={() => handleSelect(topic)}
|
||||
className="min-h-10 rounded-none px-3 py-1.5"
|
||||
>
|
||||
|
||||
@@ -23,8 +23,8 @@ export function PublicHomeRoute() {
|
||||
const raw_graph_data = React.use(graph_data_promise) as GraphNode[]
|
||||
const [filterQuery, setFilterQuery] = React.useState<string>("")
|
||||
|
||||
const handleTopicSelect = (topicName: string) => {
|
||||
router.push(`/${topicName}`)
|
||||
const handleTopicSelect = (topic: string) => {
|
||||
router.replace(`/${topic}`)
|
||||
}
|
||||
|
||||
const handleInputChange = (value: string) => {
|
||||
@@ -34,11 +34,7 @@ export function PublicHomeRoute() {
|
||||
return (
|
||||
<>
|
||||
<div className="relative h-full w-screen">
|
||||
<ForceGraphClient
|
||||
raw_nodes={raw_graph_data}
|
||||
onNodeClick={val => handleTopicSelect(val)}
|
||||
filter_query={filterQuery}
|
||||
/>
|
||||
<ForceGraphClient raw_nodes={raw_graph_data} onNodeClick={handleTopicSelect} filter_query={filterQuery} />
|
||||
|
||||
<div className="absolute left-1/2 top-1/2 w-full max-w-lg -translate-x-1/2 -translate-y-1/2 transform max-sm:px-5">
|
||||
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }}>
|
||||
@@ -53,11 +49,7 @@ export function PublicHomeRoute() {
|
||||
>
|
||||
I want to learn
|
||||
</motion.h1>
|
||||
<Autocomplete
|
||||
topics={raw_graph_data}
|
||||
onSelect={topic => handleTopicSelect(topic.name)}
|
||||
onInputChange={handleInputChange}
|
||||
/>
|
||||
<Autocomplete topics={raw_graph_data} onSelect={handleTopicSelect} onInputChange={handleInputChange} />
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,15 +4,16 @@ import * as React from "react"
|
||||
import { ContentHeader, SidebarToggleButton } from "@/components/custom/content-header"
|
||||
import { ListOfTopics, Topic } from "@/lib/schema"
|
||||
import { LearningStateSelector } from "@/components/custom/learning-state-selector"
|
||||
import { useAccount } from "@/lib/providers/jazz-provider"
|
||||
import { useAccountOrGuest } from "@/lib/providers/jazz-provider"
|
||||
import { LearningStateValue } from "@/lib/constants"
|
||||
import { toast } from "sonner"
|
||||
|
||||
interface TopicDetailHeaderProps {
|
||||
topic: Topic
|
||||
}
|
||||
|
||||
export const TopicDetailHeader = React.memo(function TopicDetailHeader({ topic }: TopicDetailHeaderProps) {
|
||||
const { me } = useAccount({
|
||||
const { me } = useAccountOrGuest({
|
||||
root: {
|
||||
topicsWantToLearn: [],
|
||||
topicsLearning: [],
|
||||
@@ -26,34 +27,44 @@ export const TopicDetailHeader = React.memo(function TopicDetailHeader({ topic }
|
||||
learningState: LearningStateValue
|
||||
} | null = null
|
||||
|
||||
const wantToLearnIndex = me?.root.topicsWantToLearn.findIndex(t => t?.id === topic.id) ?? -1
|
||||
const wantToLearnIndex =
|
||||
me?._type === "Anonymous" ? -1 : (me?.root.topicsWantToLearn.findIndex(t => t?.id === topic.id) ?? -1)
|
||||
if (wantToLearnIndex !== -1) {
|
||||
p = {
|
||||
index: wantToLearnIndex,
|
||||
topic: me?.root.topicsWantToLearn[wantToLearnIndex],
|
||||
// TODO: fix this type error by doing better conditionals on both index and p
|
||||
topic: me && me._type !== "Anonymous" ? me.root.topicsWantToLearn[wantToLearnIndex] : undefined,
|
||||
learningState: "wantToLearn"
|
||||
}
|
||||
}
|
||||
|
||||
const learningIndex = me?.root.topicsLearning.findIndex(t => t?.id === topic.id) ?? -1
|
||||
const learningIndex =
|
||||
me?._type === "Anonymous" ? -1 : (me?.root.topicsLearning.findIndex(t => t?.id === topic.id) ?? -1)
|
||||
if (learningIndex !== -1) {
|
||||
p = {
|
||||
index: learningIndex,
|
||||
topic: me?.root.topicsLearning[learningIndex],
|
||||
topic: me && me._type !== "Anonymous" ? me?.root.topicsLearning[learningIndex] : undefined,
|
||||
learningState: "learning"
|
||||
}
|
||||
}
|
||||
|
||||
const learnedIndex = me?.root.topicsLearned.findIndex(t => t?.id === topic.id) ?? -1
|
||||
const learnedIndex =
|
||||
me?._type === "Anonymous" ? -1 : (me?.root.topicsLearned.findIndex(t => t?.id === topic.id) ?? -1)
|
||||
if (learnedIndex !== -1) {
|
||||
p = {
|
||||
index: learnedIndex,
|
||||
topic: me?.root.topicsLearned[learnedIndex],
|
||||
topic: me && me._type !== "Anonymous" ? me?.root.topicsLearned[learnedIndex] : undefined,
|
||||
learningState: "learned"
|
||||
}
|
||||
}
|
||||
|
||||
const handleAddToProfile = (learningState: LearningStateValue) => {
|
||||
if (me?._type === "Anonymous") {
|
||||
// TODO: handle better
|
||||
toast.error("You need to sign in to add links to your personal list.")
|
||||
return
|
||||
}
|
||||
|
||||
const topicLists: Record<LearningStateValue, (ListOfTopics | null) | undefined> = {
|
||||
wantToLearn: me?.root.topicsWantToLearn,
|
||||
learning: me?.root.topicsLearning,
|
||||
|
||||
@@ -4,9 +4,8 @@ import React, { useMemo, useRef } from "react"
|
||||
import { TopicDetailHeader } from "./Header"
|
||||
import { TopicSections } from "./partials/topic-sections"
|
||||
import { atom } from "jotai"
|
||||
import { useAccount, useCoState } from "@/lib/providers/jazz-provider"
|
||||
import { Topic } from "@/lib/schema"
|
||||
import { JAZZ_GLOBAL_GROUP_ID } from "@/lib/constants"
|
||||
import { useAccount, useAccountOrGuest } from "@/lib/providers/jazz-provider"
|
||||
import { useTopicData } from "@/hooks/use-topic-data"
|
||||
|
||||
interface TopicDetailRouteProps {
|
||||
topicName: string
|
||||
@@ -15,10 +14,8 @@ interface TopicDetailRouteProps {
|
||||
export const openPopoverForIdAtom = atom<string | null>(null)
|
||||
|
||||
export function TopicDetailRoute({ topicName }: TopicDetailRouteProps) {
|
||||
const { me } = useAccount({ root: { personalLinks: [] } })
|
||||
|
||||
const topicID = useMemo(() => me && Topic.findUnique({ topicName }, JAZZ_GLOBAL_GROUP_ID, me), [topicName, me])
|
||||
const topic = useCoState(Topic, topicID, { latestGlobalGuide: { sections: [{ links: [] }] } })
|
||||
const { me } = useAccountOrGuest({ root: { personalLinks: [] } })
|
||||
const { topic } = useTopicData(topicName, me)
|
||||
// const { activeIndex, setActiveIndex, containerRef, linkRefs } = useLinkNavigation(allLinks)
|
||||
const linksRefDummy = useRef<(HTMLLIElement | null)[]>([])
|
||||
const containerRefDummy = useRef<HTMLDivElement>(null)
|
||||
@@ -37,8 +34,6 @@ export function TopicDetailRoute({ topicName }: TopicDetailRouteProps) {
|
||||
setActiveIndex={() => {}}
|
||||
linkRefs={linksRefDummy}
|
||||
containerRef={containerRefDummy}
|
||||
me={me}
|
||||
personalLinks={me.root.personalLinks}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -13,6 +13,7 @@ import { cn, ensureUrlProtocol, generateUniqueSlug } from "@/lib/utils"
|
||||
import { LaAccount, Link as LinkSchema, PersonalLink, PersonalLinkLists, Topic, UserRoot } from "@/lib/schema"
|
||||
import { openPopoverForIdAtom } from "../TopicDetailRoute"
|
||||
import { LEARNING_STATES, LearningStateValue } from "@/lib/constants"
|
||||
import { useAccountOrGuest } from "@/lib/providers/jazz-provider"
|
||||
|
||||
interface LinkItemProps {
|
||||
topic: Topic
|
||||
@@ -20,23 +21,24 @@ interface LinkItemProps {
|
||||
isActive: boolean
|
||||
index: number
|
||||
setActiveIndex: (index: number) => void
|
||||
me: {
|
||||
root: {
|
||||
personalLinks: PersonalLinkLists
|
||||
} & UserRoot
|
||||
} & LaAccount
|
||||
personalLinks: PersonalLinkLists
|
||||
}
|
||||
|
||||
export const LinkItem = React.memo(
|
||||
React.forwardRef<HTMLLIElement, LinkItemProps>(
|
||||
({ topic, link, isActive, index, setActiveIndex, me, personalLinks }, ref) => {
|
||||
({ topic, link, isActive, index, setActiveIndex }, ref) => {
|
||||
const router = useRouter()
|
||||
const [, setOpenPopoverForId] = useAtom(openPopoverForIdAtom)
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
|
||||
|
||||
const { me } = useAccountOrGuest({ root: { personalLinks: [] } });
|
||||
|
||||
const personalLinks = useMemo(() => {
|
||||
if (!me || me._type === "Anonymous") return undefined;
|
||||
return me?.root?.personalLinks || []
|
||||
}, [me])
|
||||
|
||||
const personalLink = useMemo(() => {
|
||||
return personalLinks.find(pl => pl?.link?.id === link.id)
|
||||
return personalLinks?.find(pl => pl?.link?.id === link.id)
|
||||
}, [personalLinks, link.id])
|
||||
|
||||
const selectedLearningState = useMemo(() => {
|
||||
@@ -53,6 +55,14 @@ export const LinkItem = React.memo(
|
||||
|
||||
const handleSelectLearningState = useCallback(
|
||||
(learningState: LearningStateValue) => {
|
||||
if (!personalLinks || !me || me?._type === "Anonymous") {
|
||||
if (me?._type === "Anonymous") {
|
||||
// TODO: handle better
|
||||
toast.error("You need to sign in to add links to your personal list.")
|
||||
}
|
||||
return
|
||||
};
|
||||
|
||||
const defaultToast = {
|
||||
duration: 5000,
|
||||
position: "bottom-right" as const,
|
||||
|
||||
@@ -11,24 +11,9 @@ interface SectionProps {
|
||||
startIndex: number
|
||||
linkRefs: React.MutableRefObject<(HTMLLIElement | null)[]>
|
||||
setActiveIndex: (index: number) => void
|
||||
me: {
|
||||
root: {
|
||||
personalLinks: PersonalLinkLists
|
||||
} & UserRoot
|
||||
} & LaAccount
|
||||
personalLinks: PersonalLinkLists
|
||||
}
|
||||
|
||||
export function Section({
|
||||
topic,
|
||||
section,
|
||||
activeIndex,
|
||||
setActiveIndex,
|
||||
startIndex,
|
||||
linkRefs,
|
||||
me,
|
||||
personalLinks
|
||||
}: SectionProps) {
|
||||
export function Section({ topic, section, activeIndex, setActiveIndex, startIndex, linkRefs }: SectionProps) {
|
||||
const [nLinksToLoad, setNLinksToLoad] = useState(10)
|
||||
|
||||
const linksToLoad = useMemo(() => {
|
||||
@@ -55,8 +40,6 @@ export function Section({
|
||||
ref={el => {
|
||||
linkRefs.current[startIndex + index] = el
|
||||
}}
|
||||
me={me}
|
||||
personalLinks={personalLinks}
|
||||
/>
|
||||
) : (
|
||||
<Skeleton key={index} className="h-14 w-full xl:h-11" />
|
||||
|
||||
@@ -9,12 +9,6 @@ interface TopicSectionsProps {
|
||||
setActiveIndex: (index: number) => void
|
||||
linkRefs: React.MutableRefObject<(HTMLLIElement | null)[]>
|
||||
containerRef: React.RefObject<HTMLDivElement>
|
||||
me: {
|
||||
root: {
|
||||
personalLinks: PersonalLinkLists
|
||||
} & UserRoot
|
||||
} & LaAccount
|
||||
personalLinks: PersonalLinkLists
|
||||
}
|
||||
|
||||
export function TopicSections({
|
||||
@@ -24,8 +18,6 @@ export function TopicSections({
|
||||
setActiveIndex,
|
||||
linkRefs,
|
||||
containerRef,
|
||||
me,
|
||||
personalLinks
|
||||
}: TopicSectionsProps) {
|
||||
return (
|
||||
<div ref={containerRef} className="flex w-full flex-1 flex-col overflow-y-auto [scrollbar-gutter:stable]">
|
||||
@@ -42,8 +34,6 @@ export function TopicSections({
|
||||
setActiveIndex={setActiveIndex}
|
||||
startIndex={sections.slice(0, sectionIndex).reduce((acc, s) => acc + (s?.links?.length || 0), 0)}
|
||||
linkRefs={linkRefs}
|
||||
me={me}
|
||||
personalLinks={personalLinks}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user