update preview components
This commit is contained in:
92
src/components/navigation.tsx
Normal file
92
src/components/navigation.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import { ActiveLink } from '@components/activeLink';
|
||||
import ChannelList from '@components/channels/channelList';
|
||||
import ChatList from '@components/chats/chatList';
|
||||
|
||||
import { Disclosure } from '@headlessui/react';
|
||||
import { Bonfire, NavArrowUp, PeopleTag } from 'iconoir-react';
|
||||
import { Suspense } from 'react';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
|
||||
export default function Navigation() {
|
||||
return (
|
||||
<div className="relative flex h-full flex-col gap-1 overflow-hidden pt-3">
|
||||
{/* Newsfeed */}
|
||||
<Disclosure defaultOpen={true}>
|
||||
{({ open }) => (
|
||||
<div className="flex flex-col px-2">
|
||||
<Disclosure.Button className="flex cursor-pointer items-center gap-1 px-1 py-1">
|
||||
<div
|
||||
className={`inline-flex h-5 w-5 transform items-center justify-center transition-transform duration-150 ease-in-out ${
|
||||
open ? 'rotate-180' : ''
|
||||
}`}
|
||||
>
|
||||
<NavArrowUp width={16} height={16} className="text-zinc-700" />
|
||||
</div>
|
||||
<h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">Newsfeed</h3>
|
||||
</Disclosure.Button>
|
||||
<Disclosure.Panel className="flex flex-col text-zinc-400">
|
||||
<ActiveLink
|
||||
href="/newsfeed/following"
|
||||
className="flex h-8 items-center gap-2.5 rounded-md px-2.5 text-sm font-medium hover:text-zinc-200"
|
||||
activeClassName="dark:bg-zinc-900 dark:text-zinc-100 hover:dark:bg-zinc-800"
|
||||
>
|
||||
<PeopleTag width={16} height={16} className="text-zinc-500" />
|
||||
<span>Following</span>
|
||||
</ActiveLink>
|
||||
<ActiveLink
|
||||
href="/newsfeed/circle"
|
||||
className="flex h-8 items-center gap-2.5 rounded-md px-2.5 text-sm font-medium hover:text-zinc-200"
|
||||
activeClassName="dark:bg-zinc-900 dark:text-zinc-100 hover:dark:bg-zinc-800"
|
||||
>
|
||||
<Bonfire width={16} height={16} className="text-zinc-500" />
|
||||
<span>Circle</span>
|
||||
</ActiveLink>
|
||||
</Disclosure.Panel>
|
||||
</div>
|
||||
)}
|
||||
</Disclosure>
|
||||
{/* Channels */}
|
||||
<Disclosure defaultOpen={true}>
|
||||
{({ open }) => (
|
||||
<div className="flex flex-col px-2">
|
||||
<Disclosure.Button className="flex cursor-pointer items-center gap-1 px-1 py-1">
|
||||
<div
|
||||
className={`inline-flex h-5 w-5 transform items-center justify-center transition-transform duration-150 ease-in-out ${
|
||||
open ? 'rotate-180' : ''
|
||||
}`}
|
||||
>
|
||||
<NavArrowUp width={16} height={16} className="text-zinc-700" />
|
||||
</div>
|
||||
<h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">Channels</h3>
|
||||
</Disclosure.Button>
|
||||
<Disclosure.Panel>
|
||||
<Suspense fallback={<Skeleton count={2} />}>
|
||||
<ChannelList />
|
||||
</Suspense>
|
||||
</Disclosure.Panel>
|
||||
</div>
|
||||
)}
|
||||
</Disclosure>
|
||||
{/* Chats */}
|
||||
<Disclosure defaultOpen={true}>
|
||||
{({ open }) => (
|
||||
<div className="flex flex-col px-2">
|
||||
<Disclosure.Button className="flex cursor-pointer items-center gap-1 px-1 py-1">
|
||||
<div
|
||||
className={`inline-flex h-5 w-5 transform items-center justify-center transition-transform duration-150 ease-in-out ${
|
||||
open ? 'rotate-180' : ''
|
||||
}`}
|
||||
>
|
||||
<NavArrowUp width={16} height={16} className="text-zinc-700" />
|
||||
</div>
|
||||
<h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">Chats</h3>
|
||||
</Disclosure.Button>
|
||||
<Disclosure.Panel>
|
||||
<ChatList />
|
||||
</Disclosure.Panel>
|
||||
</div>
|
||||
)}
|
||||
</Disclosure>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import ChannelList from '@components/channels/channelList';
|
||||
|
||||
import { Disclosure } from '@headlessui/react';
|
||||
import { NavArrowUp } from 'iconoir-react';
|
||||
import { Suspense } from 'react';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
|
||||
export default function Channels() {
|
||||
return (
|
||||
<Disclosure>
|
||||
{({ open }) => (
|
||||
<div className="flex flex-col px-2">
|
||||
<Disclosure.Button className="flex cursor-pointer items-center gap-1 px-1 py-1">
|
||||
<div
|
||||
className={`inline-flex h-5 w-5 transform items-center justify-center transition-transform duration-150 ease-in-out ${
|
||||
open ? 'rotate-180' : ''
|
||||
}`}
|
||||
>
|
||||
<NavArrowUp width={16} height={16} className="text-zinc-700" />
|
||||
</div>
|
||||
<h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">Channels</h3>
|
||||
</Disclosure.Button>
|
||||
<Disclosure.Panel>
|
||||
<Suspense fallback={<Skeleton count={2} />}>
|
||||
<ChannelList />
|
||||
</Suspense>
|
||||
</Disclosure.Panel>
|
||||
</div>
|
||||
)}
|
||||
</Disclosure>
|
||||
);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import ChatList from '@components/chats/chatList';
|
||||
|
||||
import { Disclosure } from '@headlessui/react';
|
||||
import { NavArrowUp } from 'iconoir-react';
|
||||
|
||||
export default function Chats() {
|
||||
return (
|
||||
<Disclosure>
|
||||
{({ open }) => (
|
||||
<div className="flex flex-col px-2">
|
||||
<Disclosure.Button className="flex cursor-pointer items-center gap-1 px-1 py-1">
|
||||
<div
|
||||
className={`inline-flex h-5 w-5 transform items-center justify-center transition-transform duration-150 ease-in-out ${
|
||||
open ? 'rotate-180' : ''
|
||||
}`}
|
||||
>
|
||||
<NavArrowUp width={16} height={16} className="text-zinc-700" />
|
||||
</div>
|
||||
<h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">Chats</h3>
|
||||
</Disclosure.Button>
|
||||
<Disclosure.Panel>
|
||||
<ChatList />
|
||||
</Disclosure.Panel>
|
||||
</div>
|
||||
)}
|
||||
</Disclosure>
|
||||
);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import Channels from '@components/navigation/channels';
|
||||
import Chats from '@components/navigation/chats';
|
||||
import Newsfeed from '@components/navigation/newsfeed';
|
||||
|
||||
export default function Navigation() {
|
||||
return (
|
||||
<div className="relative flex h-full flex-col gap-1 overflow-hidden pt-3">
|
||||
{/* Newsfeed */}
|
||||
<Newsfeed />
|
||||
{/* Channels */}
|
||||
<Channels />
|
||||
{/* Chats */}
|
||||
<Chats />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import { ActiveLink } from '@components/activeLink';
|
||||
|
||||
import { Disclosure } from '@headlessui/react';
|
||||
import { Bonfire, NavArrowUp, PeopleTag } from 'iconoir-react';
|
||||
|
||||
export default function Newsfeed() {
|
||||
return (
|
||||
<Disclosure>
|
||||
{({ open }) => (
|
||||
<div className="flex flex-col px-2">
|
||||
<Disclosure.Button className="flex cursor-pointer items-center gap-1 px-1 py-1">
|
||||
<div
|
||||
className={`inline-flex h-5 w-5 transform items-center justify-center transition-transform duration-150 ease-in-out ${
|
||||
open ? 'rotate-180' : ''
|
||||
}`}
|
||||
>
|
||||
<NavArrowUp width={16} height={16} className="text-zinc-700" />
|
||||
</div>
|
||||
<h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">Newsfeed</h3>
|
||||
</Disclosure.Button>
|
||||
<Disclosure.Panel className="flex flex-col text-zinc-400">
|
||||
<ActiveLink
|
||||
href="/newsfeed/following"
|
||||
className="flex h-8 items-center gap-2.5 rounded-md px-2.5 text-sm font-medium hover:text-zinc-200"
|
||||
activeClassName="dark:bg-zinc-900 dark:text-zinc-100 hover:dark:bg-zinc-800"
|
||||
>
|
||||
<PeopleTag width={16} height={16} className="text-zinc-500" />
|
||||
<span>Following</span>
|
||||
</ActiveLink>
|
||||
<ActiveLink
|
||||
href="/newsfeed/circle"
|
||||
className="flex h-8 items-center gap-2.5 rounded-md px-2.5 text-sm font-medium hover:text-zinc-200"
|
||||
activeClassName="dark:bg-zinc-900 dark:text-zinc-100 hover:dark:bg-zinc-800"
|
||||
>
|
||||
<Bonfire width={16} height={16} className="text-zinc-500" />
|
||||
<span>Circle</span>
|
||||
</ActiveLink>
|
||||
</Disclosure.Panel>
|
||||
</div>
|
||||
)}
|
||||
</Disclosure>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
export const ImagePreview = memo(function ImagePreview({ url, size }: { url: string; size: string }) {
|
||||
export const ImagePreview = ({ url, size }: { url: string; size: string }) => {
|
||||
return (
|
||||
<div className={`relative h-full ${size === 'large' ? 'w-4/5' : 'w-1/2'} mt-2 rounded-lg border border-zinc-800`}>
|
||||
<img src={url} alt={url} className="h-auto w-full rounded-lg object-cover" loading="lazy" fetchpriority="high" />
|
||||
<img src={url} alt={url} className="h-auto w-full rounded-lg object-cover" loading="lazy" />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import { memo } from 'react';
|
||||
import ReactPlayer from 'react-player';
|
||||
import { MediaOutlet, MediaPlayer } from '@vidstack/react';
|
||||
|
||||
export const VideoPreview = memo(function VideoPreview({ url }: { url: string }) {
|
||||
export const VideoPreview = ({ url }: { url: string }) => {
|
||||
return (
|
||||
<div onClick={(e) => e.stopPropagation()} className="relative mt-2 flex flex-col overflow-hidden rounded-lg">
|
||||
<ReactPlayer
|
||||
url={url}
|
||||
controls={true}
|
||||
volume={0}
|
||||
className="aspect-video w-full xl:w-2/3"
|
||||
width="100%"
|
||||
height="100%"
|
||||
/>
|
||||
<MediaPlayer src={url} poster="" controls>
|
||||
<MediaOutlet />
|
||||
</MediaPlayer>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
16
src/components/note/preview/youtube.tsx
Normal file
16
src/components/note/preview/youtube.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import YouTube from 'react-youtube';
|
||||
|
||||
function getVideoId(url: string) {
|
||||
const regex = /(youtu.*be.*)\/(watch\?v=|embed\/|v|shorts|)(.*?((?=[&#?])|$))/gm;
|
||||
return regex.exec(url)[3];
|
||||
}
|
||||
|
||||
export const YoutubePreview = ({ url }: { url: string }) => {
|
||||
const id = getVideoId(url);
|
||||
|
||||
return (
|
||||
<div onClick={(e) => e.stopPropagation()} className="relative mt-2 flex flex-col overflow-hidden rounded-lg">
|
||||
<YouTube videoId={id} className="aspect-video xl:w-2/3" opts={{ width: '100%', height: '100%' }} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -3,6 +3,7 @@ import { Shell } from '@renderer/shell';
|
||||
import { PageContextClient } from '@renderer/types';
|
||||
|
||||
import { Root, createRoot, hydrateRoot } from 'react-dom/client';
|
||||
import 'vidstack/styles/defaults.css';
|
||||
|
||||
export const clientRouting = true;
|
||||
let root: Root;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { ImagePreview } from '@components/note/preview/image';
|
||||
import { VideoPreview } from '@components/note/preview/video';
|
||||
import { YoutubePreview } from '@components/note/preview/youtube';
|
||||
import { NoteQuote } from '@components/note/quote';
|
||||
import { UserMention } from '@components/user/mention';
|
||||
|
||||
import destr from 'destr';
|
||||
import reactStringReplace from 'react-string-replace';
|
||||
|
||||
export const contentParser = (noteContent, noteTags) => {
|
||||
export const contentParser = (noteContent: any, noteTags: any) => {
|
||||
let parsedContent = noteContent.trim();
|
||||
|
||||
// get data tags
|
||||
@@ -16,9 +17,9 @@ export const contentParser = (noteContent, noteTags) => {
|
||||
if (match.match(/\.(jpg|jpeg|gif|png|webp)$/i)) {
|
||||
// image url
|
||||
return <ImagePreview key={match + i} url={match} size="large" />;
|
||||
} else if (match.match(/(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/i)) {
|
||||
} else if (match.match(/(http:|https:)?(\/\/)?(www\.)?(youtube.com|youtu.be)\/(watch|embed)?(\?v=|\/)?(\S+)?/)) {
|
||||
// youtube
|
||||
return <VideoPreview key={match + i} url={match} />;
|
||||
return <YoutubePreview key={match + i} url={match} />;
|
||||
} else if (match.match(/\.(mp4|webm)$/i)) {
|
||||
// video
|
||||
return <VideoPreview key={match + i} url={match} />;
|
||||
@@ -54,7 +55,7 @@ export const contentParser = (noteContent, noteTags) => {
|
||||
return parsedContent;
|
||||
};
|
||||
|
||||
export const messageParser = (noteContent) => {
|
||||
export const messageParser = (noteContent: any) => {
|
||||
let parsedContent = noteContent.trim();
|
||||
|
||||
// handle urls
|
||||
@@ -62,9 +63,9 @@ export const messageParser = (noteContent) => {
|
||||
if (match.match(/\.(jpg|jpeg|gif|png|webp)$/i)) {
|
||||
// image url
|
||||
return <ImagePreview key={match + i} url={match} size="small" />;
|
||||
} else if (match.match(/(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/i)) {
|
||||
} else if (match.match(/(http:|https:)?(\/\/)?(www\.)?(youtube.com|youtu.be)\/(watch|embed)?(\?v=|\/)?(\S+)?/)) {
|
||||
// youtube
|
||||
return <VideoPreview key={match + i} url={match} />;
|
||||
return <YoutubePreview key={match + i} url={match} />;
|
||||
} else if (match.match(/\.(mp4|webm)$/i)) {
|
||||
// video
|
||||
return <VideoPreview key={match + i} url={match} />;
|
||||
|
||||
Reference in New Issue
Block a user