add search input to appheader
This commit is contained in:
3
src/app/myspace/pages/index.page.tsx
Normal file
3
src/app/myspace/pages/index.page.tsx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Page() {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { LayoutNewsfeed as Layout } from './layout';
|
|
||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
3
src/app/threads/pages/index.page.tsx
Normal file
3
src/app/threads/pages/index.page.tsx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Page() {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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=""
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user