aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSebastien Castiel <sebastien@castiel.me>2024-01-24 11:12:55 -0500
committerSebastien Castiel <sebastien@castiel.me>2024-01-24 11:12:55 -0500
commit545cf75e9993f4dbc0653512d01b198f111998bf (patch)
tree352c01040e4941a76b9ade71c5e65a80671ebcf1 /src
parent7956156d705297ba7c372577bb19c09517bbd444 (diff)
Join group by URL (Closes #55)
Diffstat (limited to 'src')
-rw-r--r--src/app/groups/add-group-by-url-button-actions.ts8
-rw-r--r--src/app/groups/add-group-by-url-button.tsx91
-rw-r--r--src/app/groups/page.tsx21
-rw-r--r--src/app/groups/recent-group-list.tsx68
-rw-r--r--src/app/groups/recent-groups-page.tsx29
5 files changed, 180 insertions, 37 deletions
diff --git a/src/app/groups/add-group-by-url-button-actions.ts b/src/app/groups/add-group-by-url-button-actions.ts
new file mode 100644
index 0000000..b6c5147
--- /dev/null
+++ b/src/app/groups/add-group-by-url-button-actions.ts
@@ -0,0 +1,8 @@
+'use server'
+
+import { getGroup } from '@/lib/api'
+
+export async function getGroupInfoAction(groupId: string) {
+ 'use server'
+ return getGroup(groupId)
+}
diff --git a/src/app/groups/add-group-by-url-button.tsx b/src/app/groups/add-group-by-url-button.tsx
new file mode 100644
index 0000000..a33f5eb
--- /dev/null
+++ b/src/app/groups/add-group-by-url-button.tsx
@@ -0,0 +1,91 @@
+import { getGroupInfoAction } from '@/app/groups/add-group-by-url-button-actions'
+import { saveRecentGroup } from '@/app/groups/recent-groups-helpers'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from '@/components/ui/popover'
+import { useMediaQuery } from '@/lib/hooks'
+import { Loader2, Plus } from 'lucide-react'
+import { useState } from 'react'
+
+type Props = {
+ reload: () => void
+}
+
+export function AddGroupByUrlButton({ reload }: Props) {
+ const isDesktop = useMediaQuery('(min-width: 640px)')
+ const [url, setUrl] = useState('')
+ const [error, setError] = useState(false)
+ const [open, setOpen] = useState(false)
+ const [pending, setPending] = useState(false)
+
+ return (
+ <Popover open={open} onOpenChange={setOpen}>
+ <PopoverTrigger asChild>
+ <Button variant="secondary">
+ {/* <Plus className="w-4 h-4 mr-2" /> */}
+ <>Add by URL</>
+ </Button>
+ </PopoverTrigger>
+ <PopoverContent
+ align={isDesktop ? 'end' : 'start'}
+ className="[&_p]:text-sm flex flex-col gap-3"
+ >
+ <h3 className="font-bold">Add a group by URL</h3>
+ <p>
+ If a group was shared with you, you can paste its URL here to add it
+ to your list.
+ </p>
+ <form
+ className="flex gap-2"
+ onSubmit={async (event) => {
+ event.preventDefault()
+ const [, groupId] =
+ url.match(
+ new RegExp(`${window.location.origin}/groups/([^/]+)`),
+ ) ?? []
+ setPending(true)
+ const group = groupId ? await getGroupInfoAction(groupId) : null
+ setPending(false)
+ if (!group) {
+ setError(true)
+ } else {
+ saveRecentGroup({ id: group.id, name: group.name })
+ reload()
+ setUrl('')
+ setOpen(false)
+ }
+ }}
+ >
+ <Input
+ type="url"
+ required
+ placeholder="https://spliit.app/..."
+ className="flex-1 text-base"
+ value={url}
+ disabled={pending}
+ onChange={(event) => {
+ setUrl(event.target.value)
+ setError(false)
+ }}
+ />
+ <Button size="icon" type="submit" disabled={pending}>
+ {pending ? (
+ <Loader2 className="w-4 h-4 animate-spin" />
+ ) : (
+ <Plus className="w-4 h-4" />
+ )}
+ </Button>
+ </form>
+ {error && (
+ <p className="text-destructive">
+ Oops, we are not able to find the group from the URL you provided…
+ </p>
+ )}
+ </PopoverContent>
+ </Popover>
+ )
+}
diff --git a/src/app/groups/page.tsx b/src/app/groups/page.tsx
index 967c6b7..4366ec5 100644
--- a/src/app/groups/page.tsx
+++ b/src/app/groups/page.tsx
@@ -1,29 +1,10 @@
import { RecentGroupList } from '@/app/groups/recent-group-list'
-import { Button } from '@/components/ui/button'
-import { Plus } from 'lucide-react'
import { Metadata } from 'next'
-import Link from 'next/link'
export const metadata: Metadata = {
title: 'Recently visited groups',
}
export default async function GroupsPage() {
- return (
- <>
- <div className="flex justify-between items-center gap-4">
- <h1 className="font-bold text-2xl">
- <Link href="/groups">My groups</Link>
- </h1>
- <Button asChild size="icon">
- <Link href="/groups/create">
- <Plus className="w-4 h-4" />
- </Link>
- </Button>
- </div>
- <div>
- <RecentGroupList />
- </div>
- </>
- )
+ return <RecentGroupList />
}
diff --git a/src/app/groups/recent-group-list.tsx b/src/app/groups/recent-group-list.tsx
index 5f41fdb..9ad6f56 100644
--- a/src/app/groups/recent-group-list.tsx
+++ b/src/app/groups/recent-group-list.tsx
@@ -1,5 +1,6 @@
'use client'
import { getGroupsAction } from '@/app/groups/actions'
+import { AddGroupByUrlButton } from '@/app/groups/add-group-by-url-button'
import {
RecentGroups,
getArchivedGroups,
@@ -10,7 +11,7 @@ import { Button } from '@/components/ui/button'
import { getGroups } from '@/lib/api'
import { Loader2 } from 'lucide-react'
import Link from 'next/link'
-import { SetStateAction, useEffect, useState } from 'react'
+import { PropsWithChildren, SetStateAction, useEffect, useState } from 'react'
import { RecentGroupListCard } from './recent-group-list-card'
export type RecentGroupsState =
@@ -54,7 +55,7 @@ function sortGroups(
export function RecentGroupList() {
const [state, setState] = useState<RecentGroupsState>({ status: 'pending' })
- useEffect(() => {
+ function loadGroups() {
const groupsInStorage = getRecentGroups()
const starredGroups = getStarredGroups()
const archivedGroups = getArchivedGroups()
@@ -73,35 +74,43 @@ export function RecentGroupList() {
archivedGroups,
})
})
+ }
+
+ useEffect(() => {
+ loadGroups()
}, [])
if (state.status === 'pending') {
return (
- <p>
- <Loader2 className="w-4 m-4 mr-2 inline animate-spin" /> Loading recent
- groups…
- </p>
+ <GroupsPage reload={loadGroups}>
+ <p>
+ <Loader2 className="w-4 m-4 mr-2 inline animate-spin" /> Loading
+ recent groups…
+ </p>
+ </GroupsPage>
)
}
if (state.groups.length === 0) {
return (
- <div className="text-sm space-y-2">
- <p>You have not visited any group recently.</p>
- <p>
- <Button variant="link" asChild className="-m-4">
- <Link href={`/groups/create`}>Create one</Link>
- </Button>{' '}
- or ask a friend to send you the link to an existing one.
- </p>
- </div>
+ <GroupsPage reload={loadGroups}>
+ <div className="text-sm space-y-2">
+ <p>You have not visited any group recently.</p>
+ <p>
+ <Button variant="link" asChild className="-m-4">
+ <Link href={`/groups/create`}>Create one</Link>
+ </Button>{' '}
+ or ask a friend to send you the link to an existing one.
+ </p>
+ </div>
+ </GroupsPage>
)
}
const { starredGroupInfo, groupInfo, archivedGroupInfo } = sortGroups(state)
return (
- <>
+ <GroupsPage reload={loadGroups}>
{starredGroupInfo.length > 0 && (
<>
<h2 className="mb-2">Starred groups</h2>
@@ -132,7 +141,7 @@ export function RecentGroupList() {
</div>
</>
)}
- </>
+ </GroupsPage>
)
}
@@ -158,3 +167,28 @@ function GroupList({
</ul>
)
}
+
+function GroupsPage({
+ children,
+ reload,
+}: PropsWithChildren<{ reload: () => void }>) {
+ return (
+ <>
+ <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2">
+ <h1 className="font-bold text-2xl flex-1">
+ <Link href="/groups">My groups</Link>
+ </h1>
+ <div className="flex gap-2">
+ <AddGroupByUrlButton reload={reload} />
+ <Button asChild>
+ <Link href="/groups/create">
+ {/* <Plus className="w-4 h-4 mr-2" /> */}
+ <>Create</>
+ </Link>
+ </Button>
+ </div>
+ </div>
+ <div>{children}</div>
+ </>
+ )
+}
diff --git a/src/app/groups/recent-groups-page.tsx b/src/app/groups/recent-groups-page.tsx
new file mode 100644
index 0000000..36874b0
--- /dev/null
+++ b/src/app/groups/recent-groups-page.tsx
@@ -0,0 +1,29 @@
+'use client'
+import { AddGroupByUrlButton } from '@/app/groups/add-group-by-url-button'
+import { RecentGroupList } from '@/app/groups/recent-group-list'
+import { Button } from '@/components/ui/button'
+import Link from 'next/link'
+
+export function RecentGroupsPage() {
+ return (
+ <>
+ <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2">
+ <h1 className="font-bold text-2xl flex-1">
+ <Link href="/groups">My groups</Link>
+ </h1>
+ <div className="flex gap-2">
+ <AddGroupByUrlButton reload={() => {}} />
+ <Button asChild>
+ <Link href="/groups/create">
+ {/* <Plus className="w-4 h-4 mr-2" /> */}
+ <>Create</>
+ </Link>
+ </Button>
+ </div>
+ </div>
+ <div>
+ <RecentGroupList />
+ </div>
+ </>
+ )
+}