add search input to appheader

This commit is contained in:
Ren Amamiya
2023-05-06 09:41:35 +07:00
parent b55b595b20
commit 306b09ce64
9 changed files with 39 additions and 250 deletions

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <></>;
}

View File

@@ -1 +0,0 @@
export { LayoutNewsfeed as Layout } from './layout';

View File

@@ -1,74 +0,0 @@
import { ImagePicker } from '@lume/shared/form/imagePicker';
import { RelayContext } from '@lume/shared/relayProvider';
import { WRITEONLY_RELAYS } from '@lume/stores/constants';
import { noteContentAtom } from '@lume/stores/note';
import { dateToUnix } from '@lume/utils/getDate';
import { useActiveAccount } from '@lume/utils/hooks/useActiveAccount';
import { useAtom } from 'jotai';
import { useResetAtom } from 'jotai/utils';
import { getEventHash, signEvent } from 'nostr-tools';
import { useContext } from 'react';
export default function NoteForm() {
const pool: any = useContext(RelayContext);
const { account, isLoading, isError } = useActiveAccount();
const [value, setValue] = useAtom(noteContentAtom);
const resetValue = useResetAtom(noteContentAtom);
const submitEvent = () => {
if (!isLoading && !isError && account) {
const event: any = {
content: value,
created_at: dateToUnix(),
kind: 1,
pubkey: account.pubkey,
tags: [],
};
event.id = getEventHash(event);
event.sig = signEvent(event, account.privkey);
// publish note
pool.publish(event, WRITEONLY_RELAYS);
// reset form
resetValue();
// send notification
// sendNotification('Note has been published successfully');
} else {
console.log('Cannot publish note');
}
};
return (
<div className="p-3">
<div className="relative h-32 w-full shrink-0 overflow-hidden before:pointer-events-none before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-fuchsia-500 before:opacity-0 before:ring-2 before:ring-fuchsia-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight after:shadow-white/5 after:transition focus-within:before:opacity-100 focus-within:after:shadow-fuchsia-500/100 dark:focus-within:after:shadow-fuchsia-500/20">
<div>
<textarea
value={value}
onChange={(e) => setValue(e.target.value)}
spellCheck={false}
placeholder="What's up?"
className="relative h-32 w-full resize-none rounded-lg border border-black/5 px-3.5 py-3 text-sm shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-500"
/>
</div>
<div className="absolute bottom-2 w-full px-2">
<div className="flex w-full items-center justify-between bg-zinc-800">
<div className="flex items-center gap-2 divide-x divide-zinc-700">
<ImagePicker type="note" />
<div className="flex items-center gap-2 pl-2"></div>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => submitEvent()}
disabled={value.length === 0 ? true : false}
className="inline-flex h-8 w-16 items-center justify-center rounded-md bg-fuchsia-500 px-4 text-sm font-medium shadow-button hover:bg-fuchsia-600 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50"
>
Send
</button>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,29 +0,0 @@
import AppHeader from '@lume/shared/appHeader';
import MultiAccounts from '@lume/shared/multiAccounts';
import Navigation from '@lume/shared/navigation';
export function LayoutNewsfeed({ children }: { children: React.ReactNode }) {
return (
<div className="h-screen w-screen bg-zinc-50 text-zinc-900 dark:bg-zinc-950 dark:text-white">
<div className="flex h-screen w-full flex-col">
<div
data-tauri-drag-region
className="relative h-9 shrink-0 border-b border-zinc-100 bg-white dark:border-zinc-900 dark:bg-black"
>
<AppHeader />
</div>
<div className="relative flex min-h-0 w-full flex-1">
<div className="relative w-[68px] shrink-0 border-r border-zinc-900">
<MultiAccounts />
</div>
<div className="grid w-full grid-cols-4 xl:grid-cols-5">
<div className="scrollbar-hide col-span-1 overflow-y-auto overflow-x-hidden border-r border-zinc-900">
<Navigation />
</div>
<div className="col-span-3 m-3 overflow-hidden xl:col-span-4">{children}</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,9 +0,0 @@
export function Page() {
return (
<div className="relative h-full w-full rounded-lg border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20">
<div className="flex h-full w-full items-center justify-center">
<p className="text-sm text-zinc-400">Sorry, this feature under development, it will come in the next version</p>
</div>
</div>
);
}

View File

@@ -1,105 +0,0 @@
import NoteForm from '@lume/app/newsfeed/components/form';
import NoteBase from '@lume/app/note/components/base';
import { Placeholder } from '@lume/app/note/components/placeholder';
import { NoteQuoteRepost } from '@lume/app/note/components/quoteRepost';
import { hasNewerNoteAtom } from '@lume/stores/note';
import { getNotes } from '@lume/utils/storage';
import { useInfiniteQuery } from '@tanstack/react-query';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useAtom } from 'jotai';
import { useEffect, useRef } from 'react';
const ITEM_PER_PAGE = 10;
const TIME = Math.floor(Date.now() / 1000);
export function Page() {
const [hasNewerNote] = useAtom(hasNewerNoteAtom);
const { status, error, data, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage }: any = useInfiniteQuery({
queryKey: ['following'],
queryFn: async ({ pageParam = 0 }) => {
return await getNotes(TIME, ITEM_PER_PAGE, pageParam);
},
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
const allRows = data ? data.pages.flatMap((d: { data: any }) => d.data) : [];
const parentRef = useRef();
const rowVirtualizer = useVirtualizer({
count: hasNextPage ? allRows.length + 1 : allRows.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 400,
overscan: 5,
});
const itemsVirtualizer = rowVirtualizer.getVirtualItems();
useEffect(() => {
const [lastItem] = [...rowVirtualizer.getVirtualItems()].reverse();
if (!lastItem) {
return;
}
if (lastItem.index >= allRows.length - 1 && hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fetchNextPage, allRows.length, rowVirtualizer.getVirtualItems()]);
return (
<div className="relative h-full w-full rounded-lg border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20">
{hasNewerNote && (
<div className="absolute left-1/2 top-2 z-50 -translate-x-1/2 transform">
<button className="inline-flex h-8 transform items-center justify-center gap-1 rounded-full bg-fuchsia-500 pl-3 pr-3.5 text-sm shadow-md shadow-fuchsia-800/20 active:translate-y-1">
Load latest
</button>
</div>
)}
{status === 'loading' ? (
<Placeholder />
) : status === 'error' ? (
<div>{error.message}</div>
) : (
<div ref={parentRef} className="scrollbar-hide h-full w-full overflow-y-auto" style={{ contain: 'strict' }}>
<NoteForm />
<div
className="relative w-full"
style={{
height: `${rowVirtualizer.getTotalSize()}px`,
}}
>
<div
className="absolute left-0 top-0 w-full"
style={{
transform: `translateY(${itemsVirtualizer[0].start - rowVirtualizer.options.scrollMargin}px)`,
}}
>
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
const note = allRows[virtualRow.index];
if (note) {
if (note.kind === 1) {
return (
<div key={virtualRow.index} data-index={virtualRow.index} ref={rowVirtualizer.measureElement}>
<NoteBase key={note.event_id} event={note} />
</div>
);
} else {
return (
<div key={virtualRow.index} data-index={virtualRow.index} ref={rowVirtualizer.measureElement}>
<NoteQuoteRepost key={note.event_id} event={note} />
</div>
);
}
}
})}
</div>
</div>
</div>
)}
<div>{isFetching && !isFetchingNextPage ? 'Background Updating...' : null}</div>
</div>
);
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <></>;
}

View File

@@ -2,16 +2,7 @@ import ArrowLeftIcon from '@lume/shared/icons/arrowLeft';
import ArrowRightIcon from '@lume/shared/icons/arrowRight'; import ArrowRightIcon from '@lume/shared/icons/arrowRight';
import RefreshIcon from '@lume/shared/icons/refresh'; import RefreshIcon from '@lume/shared/icons/refresh';
import useSWR from 'swr';
const fetcher = async () => {
const { platform } = await import('@tauri-apps/api/os');
return await platform();
};
export default function AppHeader() { export default function AppHeader() {
const { data: platform } = useSWR('platform', fetcher);
const goBack = () => { const goBack = () => {
window.history.back(); window.history.back();
}; };
@@ -26,28 +17,38 @@ export default function AppHeader() {
return ( return (
<div data-tauri-drag-region className="flex h-full w-full flex-1 items-center px-2"> <div data-tauri-drag-region className="flex h-full w-full flex-1 items-center px-2">
<div className={`flex h-full items-center gap-2 ${platform === 'darwin' ? 'pl-[68px]' : ''}`}> <div className="flex w-full items-center justify-center gap-2">
<button <div className="flex h-full items-center gap-2">
onClick={() => goBack()} <button
className="group inline-flex h-5 w-5 items-center justify-center rounded-md hover:bg-zinc-900" onClick={() => goBack()}
> className="group inline-flex h-5 w-5 items-center justify-center rounded hover:bg-zinc-900"
<ArrowLeftIcon width={14} height={14} className="text-zinc-500 group-hover:text-zinc-300" /> >
</button> <ArrowLeftIcon width={14} height={14} className="text-zinc-500 group-hover:text-zinc-300" />
<button </button>
onClick={() => goForward()} <button
className="group inline-flex h-5 w-5 items-center justify-center rounded-md hover:bg-zinc-900" onClick={() => goForward()}
> className="group inline-flex h-5 w-5 items-center justify-center rounded hover:bg-zinc-900"
<ArrowRightIcon width={14} height={14} className="text-zinc-500 group-hover:text-zinc-300" /> >
</button> <ArrowRightIcon width={14} height={14} className="text-zinc-500 group-hover:text-zinc-300" />
<button </button>
onClick={() => reload()} </div>
className="group inline-flex h-5 w-5 items-center justify-center rounded-md hover:bg-zinc-900" <div>
> <input
<RefreshIcon width={14} height={14} className="text-zinc-500 group-hover:text-zinc-300" /> autoCapitalize="none"
</button> autoCorrect="off"
</div> autoFocus={false}
<div data-tauri-drag-region className="flex h-full w-full items-center justify-between"> placeholder="Search..."
<div className="flex h-full items-center divide-x divide-zinc-900 px-4 pt-px"></div> className="h-6 w-[453px] rounded border border-zinc-800 bg-zinc-900 px-2.5 text-center text-[11px] text-sm leading-5 text-zinc-500 placeholder:leading-5 placeholder:text-zinc-600 focus:outline-none"
/>
</div>
<div className="flex h-full items-center gap-2">
<button
onClick={() => reload()}
className="group inline-flex h-5 w-5 items-center justify-center rounded hover:bg-zinc-900"
>
<RefreshIcon width={14} height={14} className="text-zinc-500 group-hover:text-zinc-300" />
</button>
</div>
</div> </div>
</div> </div>
); );

View File

@@ -18,7 +18,7 @@ export default function Navigation() {
</div> </div>
<div className="flex flex-col text-zinc-400"> <div className="flex flex-col text-zinc-400">
<ActiveLink <ActiveLink
href="/app/newsfeed/following" href="/app/daily"
className="flex h-8 items-center gap-2.5 rounded-md px-2.5 text-[13px] font-semibold hover:text-zinc-200" className="flex h-8 items-center gap-2.5 rounded-md px-2.5 text-[13px] font-semibold hover:text-zinc-200"
activeClassName="" activeClassName=""
> >