feat: update default column list

This commit is contained in:
reya 2024-01-15 08:36:23 +07:00
parent dae4b1d52b
commit e93aedb703
14 changed files with 122 additions and 77 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

View File

@ -1,7 +1,4 @@
/* Vidstack */ /* Vidstack */
@import '@vidstack/react/player/styles/default/theme.css';
@import '@vidstack/react/player/styles/default/layouts/video.css';
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;

View File

@ -82,6 +82,7 @@ export function HomeScreen() {
}} }}
> >
{columns.map((column) => renderItem(column))} {columns.map((column) => renderItem(column))}
<div className="w-[420px]" />
</VList> </VList>
<div className="absolute bottom-3 right-3"> <div className="absolute bottom-3 right-3">
<div className="flex items-center gap-1 p-1 bg-black/50 dark:bg-white/30 backdrop-blur-xl rounded-xl"> <div className="flex items-center gap-1 p-1 bg-black/50 dark:bg-white/30 backdrop-blur-xl rounded-xl">

View File

@ -42,7 +42,7 @@ export function ColumnProvider({ children }: { children: ReactNode }) {
column.title, column.title,
column.content, column.content,
); );
if (result) setColumns((prev) => [...prev, column]); if (result) setColumns((prev) => [...prev, result]);
}, []); }, []);
const removeColumn = useCallback(async (id: number) => { const removeColumn = useCallback(async (id: number) => {

View File

@ -11,7 +11,7 @@ export function LinkPreview({ url }: { url: string }) {
if (status === "pending") { if (status === "pending") {
return ( return (
<div className="flex flex-col w-full my-1 rounded-lg bg-neutral-100 dark:bg-neutral-900"> <div className="flex flex-col w-full my-1 rounded-lg overflow-hidden bg-neutral-100 dark:bg-neutral-900">
<div className="w-full h-48 animate-pulse bg-neutral-300 dark:bg-neutral-700" /> <div className="w-full h-48 animate-pulse bg-neutral-300 dark:bg-neutral-700" />
<div className="flex flex-col gap-2 px-3 py-3"> <div className="flex flex-col gap-2 px-3 py-3">
<div className="w-2/3 h-3 rounded animate-pulse bg-neutral-300 dark:bg-neutral-700" /> <div className="w-2/3 h-3 rounded animate-pulse bg-neutral-300 dark:bg-neutral-700" />

View File

@ -46,21 +46,17 @@ export function AntenasForm({ id }: { id: number }) {
</div> </div>
<div className="flex flex-col h-full gap-5 px-3 pt-2 overflow-y-auto"> <div className="flex flex-col h-full gap-5 px-3 pt-2 overflow-y-auto">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<label <label htmlFor="name" className="font-medium">
htmlFor="name"
className="select-none text-neutral-950 data-[disabled]:opacity-50 font-medium mb-1 dark:text-white"
>
Name Name
</label> </label>
<span className="relative block w-full before:absolute before:inset-px before:rounded-[calc(theme(borderRadius.lg)-1px)] before:bg-white before:shadow dark:before:hidden after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-inset after:ring-transparent sm:after:focus-within:ring-2 sm:after:focus-within:ring-blue-500 has-[[data-disabled]]:opacity-50 before:has-[[data-disabled]]:bg-neutral-950/5 before:has-[[data-disabled]]:shadow-none before:has-[[data-invalid]]:shadow-red-500/10"> <input
<input type="text"
name="name" name="name"
value={title} value={title}
onChange={(e) => setTitle(e.target.value)} onChange={(e) => setTitle(e.target.value)}
placeholder="Nostrichs..." placeholder="Nostrichs..."
className="relative block w-full appearance-none rounded-lg px-[calc(theme(spacing[3.5])-1px)] py-[calc(theme(spacing[2.5])-1px)] sm:px-[calc(theme(spacing[3])-1px)] sm:py-[calc(theme(spacing[1.5])-1px)] focus:ring-0 text-base/6 text-neutral-950 placeholder:text-neutral-500 sm:text-sm/6 dark:text-white border border-neutral-950/10 data-[hover]:border-neutral-950/20 dark:border-white/10 dark:data-[hover]:border-white/20 bg-transparent dark:bg-white/5 focus:outline-none data-[invalid]:border-red-500 data-[invalid]:data-[hover]:border-red-500 data-[invalid]:dark:border-red-500 data-[invalid]:data-[hover]:dark:border-red-500 data-[disabled]:border-neutral-950/20 dark:data-[hover]:data-[disabled]:border-white/15 data-[disabled]:dark:border-white/15 data-[disabled]:dark:bg-white/[2.5%]" className="px-2 border border-neutral-100 dark:border-neutral-900 bg-neutral-50 rounded-lg h-10 dark:bg-neutral-950 placeholder:text-neutral-600 focus:border-blue-500 focus:shadow-none focus:ring-0"
/> />
</span>
</div> </div>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<label <label
@ -69,17 +65,15 @@ export function AntenasForm({ id }: { id: number }) {
> >
Source Source
</label> </label>
<span className="relative block w-full before:absolute before:inset-px before:rounded-[calc(theme(borderRadius.lg)-1px)] before:bg-white before:shadow dark:before:hidden after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-inset after:ring-transparent sm:after:focus-within:ring-2 sm:after:focus-within:ring-blue-500 has-[[data-disabled]]:opacity-50 before:has-[[data-disabled]]:bg-neutral-950/5 before:has-[[data-disabled]]:shadow-none before:has-[[data-invalid]]:shadow-red-500/10"> <select
<select name="source"
name="source" value={source}
value={source} onChange={(e) => setSource(e.target.value)}
onChange={(e) => setSource(e.target.value)} className="px-2 w-full border border-neutral-100 dark:border-neutral-900 bg-neutral-50 rounded-lg dark:bg-neutral-950 placeholder:text-neutral-600 focus:border-blue-500 focus:shadow-none focus:ring-0"
className="relative block w-full appearance-none rounded-lg px-[calc(theme(spacing[3.5])-1px)] py-[calc(theme(spacing[2.5])-1px)] sm:px-[calc(theme(spacing[3])-1px)] sm:py-[calc(theme(spacing[1.5])-1px)] focus:ring-0 text-base/6 text-neutral-950 placeholder:text-neutral-500 sm:text-sm/6 dark:text-white border border-neutral-950/10 data-[hover]:border-neutral-950/20 dark:border-white/10 dark:data-[hover]:border-white/20 bg-transparent dark:bg-white/5 focus:outline-none data-[invalid]:border-red-500 data-[invalid]:data-[hover]:border-red-500 data-[invalid]:dark:border-red-500 data-[invalid]:data-[hover]:dark:border-red-500 data-[disabled]:border-neutral-950/20 dark:data-[hover]:data-[disabled]:border-white/15 data-[disabled]:dark:border-white/15 data-[disabled]:dark:bg-white/[2.5%]" >
> <option value="contacts">Contacts</option>
<option value="contacts">Contacts</option> <option value="global">Global</option>
<option value="global">Global</option> </select>
</select>
</span>
</div> </div>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<label <label
@ -89,18 +83,16 @@ export function AntenasForm({ id }: { id: number }) {
Hashtags to listen to Hashtags to listen to
</label> </label>
<div className="flex items-center justify-between gap-2 mb-1"> <div className="flex items-center justify-between gap-2 mb-1">
<span className="relative block flex-1 before:absolute before:inset-px before:rounded-[calc(theme(borderRadius.lg)-1px)] before:bg-white before:shadow dark:before:hidden after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-inset after:ring-transparent sm:after:focus-within:ring-2 sm:after:focus-within:ring-blue-500 has-[[data-disabled]]:opacity-50 before:has-[[data-disabled]]:bg-neutral-950/5 before:has-[[data-disabled]]:shadow-none before:has-[[data-invalid]]:shadow-red-500/10"> <input
<input name="name"
name="name" value={hashtag}
value={hashtag} onChange={(e) => setHashtag(e.target.value)}
onChange={(e) => setHashtag(e.target.value)} onKeyPress={(event) => {
onKeyPress={(event) => { if (event.key === "Enter") addHashtag();
if (event.key === "Enter") addHashtag(); }}
}} placeholder="#nostr..."
placeholder="#nostr..." className="px-2 w-full border border-neutral-100 dark:border-neutral-900 bg-neutral-50 rounded-lg h-10 dark:bg-neutral-950 placeholder:text-neutral-600 focus:border-blue-500 focus:shadow-none focus:ring-0"
className="relative block w-full appearance-none rounded-lg px-[calc(theme(spacing[3.5])-1px)] py-[calc(theme(spacing[2.5])-1px)] sm:px-[calc(theme(spacing[3])-1px)] sm:py-[calc(theme(spacing[1.5])-1px)] focus:ring-0 text-base/6 text-neutral-950 placeholder:text-neutral-500 sm:text-sm/6 dark:text-white border border-neutral-950/10 data-[hover]:border-neutral-950/20 dark:border-white/10 dark:data-[hover]:border-white/20 bg-transparent dark:bg-white/5 focus:outline-none data-[invalid]:border-red-500 data-[invalid]:data-[hover]:border-red-500 data-[invalid]:dark:border-red-500 data-[invalid]:data-[hover]:dark:border-red-500 data-[disabled]:border-neutral-950/20 dark:data-[hover]:data-[disabled]:border-white/15 data-[disabled]:dark:border-white/15 data-[disabled]:dark:bg-white/[2.5%]" />
/>
</span>
<button <button
type="button" type="button"
onClick={() => addHashtag()} onClick={() => addHashtag()}
@ -127,7 +119,7 @@ export function AntenasForm({ id }: { id: number }) {
type="button" type="button"
onClick={submit} onClick={submit}
disabled={hashtags.length < 1} disabled={hashtags.length < 1}
className="inline-flex items-center justify-center h-10 px-4 font-semibold text-white transform bg-blue-500 rounded-lg active:translate-y-1 hover:bg-blue-600 focus:outline-none disabled:opacity-50" className="w-full inline-flex items-center justify-center h-10 px-4 font-semibold text-white transform bg-blue-500 rounded-lg active:translate-y-1 hover:bg-blue-600 focus:outline-none disabled:opacity-50"
> >
Create Create
</button> </button>

View File

@ -1,9 +1,11 @@
import { Column } from "@lume/ark"; import { Column, useColumnContext } from "@lume/ark";
import { ColumnIcon } from "@lume/icons"; import { ColumnIcon } from "@lume/icons";
import { IColumn } from "@lume/types"; import { IColumn } from "@lume/types";
import { TOPICS } from "@lume/utils"; import { COL_TYPES } from "@lume/utils";
export function Default({ column }: { column: IColumn }) { export function Default({ column }: { column: IColumn }) {
const { addColumn } = useColumnContext();
return ( return (
<Column.Root> <Column.Root>
<Column.Header <Column.Header
@ -11,32 +13,86 @@ export function Default({ column }: { column: IColumn }) {
title="Add columns" title="Add columns"
icon={<ColumnIcon className="size-4" />} icon={<ColumnIcon className="size-4" />}
/> />
<div className="h-full px-3 mt-3 overflow-y-auto scrollbar-none"> <div className="h-full px-3 mt-3 flex flex-col gap-3 overflow-y-auto scrollbar-none">
<div className="flex flex-col gap-5"> <div className="flex flex-col rounded-xl overflow-hidden">
<div> <div className="h-[100px] w-full">
<h1 className="text-lg font-semibold leading-tight">Topics</h1> <img
<p className="text-neutral-600 dark:text-neutral-400"> src="/columns/topic.jpg"
Discover content based on your interests. srcSet="/columns/topic@2x.jpg 2x"
</p> alt="topic"
className="w-full h-auto object-cover"
/>
</div> </div>
<div className="grid grid-cols-2 gap-3"> <div className="h-16 shrink-0 px-3 flex items-center justify-between bg-neutral-50 dark:bg-neutral-950">
{TOPICS.sort((a, b) => a.title.localeCompare(b.title)).map( <div>
(topic, index) => ( <h1 className="font-semibold">Topic</h1>
<div <p className="max-w-[18rem] truncate text-sm text-neutral-500 dark:text-neutral-600">
key={topic + index.toString()} Explore all content based on your interest.
className="flex flex-col w-full px-3 rounded-lg bg-neutral-100" </p>
> </div>
<div className="rounded-md h-9 w-9 shrink-0"> <button
<img type="button"
src={`/${topic.title.toLowerCase()}.jpg`} onClick={() => {
alt={topic.title} addColumn({ kind: COL_TYPES.topic, title: "", content: "" });
className="rounded-md h-9 w-9" }}
/> className="shrink-0 w-16 h-8 rounded-lg text-sm font-semibold bg-neutral-100 dark:bg-neutral-900 text-blue-500 hover:bg-neutral-200 dark:hover:bg-neutral-800 inline-flex items-center justify-center"
</div> >
<p className="font-medium">{topic.title}</p> Add
</div> </button>
), </div>
)} </div>
<div className="flex flex-col rounded-xl overflow-hidden">
<div className="h-[100px] w-full">
<img
src="/columns/group.jpg"
srcSet="/columns/group@2x.jpg 2x"
alt="group"
className="w-full h-auto object-cover"
/>
</div>
<div className="h-16 shrink-0 px-3 flex items-center justify-between bg-neutral-50 dark:bg-neutral-950">
<div>
<h1 className="font-semibold">Group Feeds</h1>
<p className="max-w-[18rem] truncate text-sm text-neutral-500 dark:text-neutral-600">
Collective of people you're interested in.
</p>
</div>
<button
type="button"
onClick={() => {
addColumn({ kind: COL_TYPES.group, title: "", content: "" });
}}
className="shrink-0 w-16 h-8 rounded-lg text-sm font-semibold bg-neutral-100 dark:bg-neutral-900 text-blue-500 hover:bg-neutral-200 dark:hover:bg-neutral-800 inline-flex items-center justify-center"
>
Add
</button>
</div>
</div>
<div className="flex flex-col rounded-xl overflow-hidden">
<div className="h-[100px] w-full">
<img
src="/columns/antenas.jpg"
srcSet="/columns/antenas@2x.jpg 2x"
alt="antenas"
className="w-full h-auto object-cover"
/>
</div>
<div className="h-16 shrink-0 px-3 flex items-center justify-between bg-neutral-50 dark:bg-neutral-950">
<div>
<h1 className="font-semibold">Antenas</h1>
<p className="max-w-[18rem] truncate text-sm text-neutral-500 dark:text-neutral-600">
Keep track to specific content.
</p>
</div>
<button
type="button"
onClick={() => {
addColumn({ kind: COL_TYPES.antenas, title: "", content: "" });
}}
className="shrink-0 w-16 h-8 rounded-lg text-sm font-semibold bg-neutral-100 dark:bg-neutral-900 text-blue-500 hover:bg-neutral-200 dark:hover:bg-neutral-800 inline-flex items-center justify-center"
>
Add
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -7,7 +7,7 @@ export function GroupForm({ id }: { id: number }) {
const ark = useArk(); const ark = useArk();
const { updateColumn, removeColumn } = useColumnContext(); const { updateColumn, removeColumn } = useColumnContext();
const [title, setTitle] = useState<string>(`Group-${id}`); const [title, setTitle] = useState<string>("Just a new group");
const [users, setUsers] = useState<Array<string>>([]); const [users, setUsers] = useState<Array<string>>([]);
// toggle follow state // toggle follow state
@ -36,7 +36,7 @@ export function GroupForm({ id }: { id: number }) {
</button> </button>
</div> </div>
<div className="flex flex-col gap-5 px-3 pt-2 overflow-y-auto"> <div className="flex flex-col gap-5 px-3 pt-2 overflow-y-auto">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1.5">
<label <label
htmlFor="name" htmlFor="name"
className="font-medium text-neutral-700 dark:text-neutral-300" className="font-medium text-neutral-700 dark:text-neutral-300"
@ -48,7 +48,7 @@ export function GroupForm({ id }: { id: number }) {
value={title} value={title}
onChange={(e) => setTitle(e.target.value)} onChange={(e) => setTitle(e.target.value)}
placeholder="Nostrichs..." placeholder="Nostrichs..."
className="px-3 rounded-lg border-neutral-200 dark:border-neutral-900 h-11 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800" className="px-2 border border-neutral-100 dark:border-neutral-900 bg-neutral-50 rounded-lg h-10 dark:bg-neutral-950 placeholder:text-neutral-600 focus:border-blue-500 focus:shadow-none focus:ring-0"
/> />
</div> </div>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
@ -64,7 +64,7 @@ export function GroupForm({ id }: { id: number }) {
key={item} key={item}
type="button" type="button"
onClick={() => toggleUser(item)} onClick={() => toggleUser(item)}
className="inline-flex items-center justify-between px-3 py-2 rounded-lg bg-neutral-50 dark:bg-neutral-950 hover:bg-neutral-100 dark:hover:bg-neutral-900" className="inline-flex items-center justify-between px-3 py-2 rounded-xl bg-neutral-50 dark:bg-neutral-950 hover:bg-neutral-100 dark:hover:bg-neutral-900"
> >
<User pubkey={item} variant="simple" /> <User pubkey={item} variant="simple" />
{users.includes(item) ? ( {users.includes(item) ? (

View File

@ -341,13 +341,12 @@ export class LumeStorage {
if (insert) { if (insert) {
const columns: Array<IColumn> = await this.#db.select( const columns: Array<IColumn> = await this.#db.select(
"SELECT * FROM columns ORDER BY id DESC LIMIT 1;", "SELECT * FROM columns WHERE id = $1 ORDER BY id DESC LIMIT 1;",
[insert.lastInsertId],
); );
if (columns.length < 1) console.error("get created widget failed"); if (!columns.length) console.error("get created widget failed");
return columns[0]; return columns[0];
} }
console.error("create widget failed");
} }
public async updateColumn(id: number, title: string, content: string) { public async updateColumn(id: number, title: string, content: string) {