diff --git a/package.json b/package.json index 8cc76cd3e..e5d044891 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@noble/secp256k1": "^1.7.0", "@webscopeio/react-textarea-autocomplete": "^4.9.2", "bech32": "^2.0.0", + "blurhash": "^2.0.5", "cheerio": "^1.0.0-rc.12", "chroma-js": "^2.4.2", "dayjs": "^1.11.9", diff --git a/src/app.tsx b/src/app.tsx index bc32a8c8b..a886c0342 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -90,6 +90,7 @@ const StreamView = lazy(() => import("./views/streams/stream")); const SearchView = lazy(() => import("./views/search")); const MapView = lazy(() => import("./views/map")); +const FilesView = lazy(() => import("./views/files")); const ChannelsHomeView = lazy(() => import("./views/channels")); const ChannelView = lazy(() => import("./views/channels/channel")); @@ -224,6 +225,7 @@ const router = createHashRouter([ element: , }, { path: "settings", element: }, + { path: "files", element: }, { path: "relays", children: [ diff --git a/src/components/blured-image.tsx b/src/components/blured-image.tsx new file mode 100644 index 000000000..e76677c0c --- /dev/null +++ b/src/components/blured-image.tsx @@ -0,0 +1,23 @@ +import { Box, Image, ImageProps, useDisclosure } from "@chakra-ui/react"; + +export default function BlurredImage(props: ImageProps) { + const { isOpen, onOpen } = useDisclosure(); + return ( + + { + e.stopPropagation(); + e.preventDefault(); + onOpen(); + } + : undefined + } + cursor="pointer" + filter={isOpen ? "" : "blur(1.5rem)"} + {...props} + /> + + ); +} diff --git a/src/components/blurhash-image.tsx b/src/components/blurhash-image.tsx new file mode 100644 index 000000000..2ca970c2e --- /dev/null +++ b/src/components/blurhash-image.tsx @@ -0,0 +1,29 @@ +import { Box, BoxProps } from "@chakra-ui/react"; +import { decode } from "blurhash"; +import { useEffect, useRef } from "react"; + +export type BlurhashImageProps = { + blurhash: string; + width: number; + height: number; +} & Omit; + +export default function BlurhashImage({ blurhash, width, height, ...props }: BlurhashImageProps) { + const canvasRef = useRef(null); + + useEffect(() => { + if (!canvasRef.current) return; + const ctx = canvasRef.current.getContext("2d"); + if (!ctx) return; + + ctx.canvas.width = width; + ctx.canvas.height = height; + + const imageData = ctx.createImageData(width, height); + const pixels = decode(blurhash, width, height); + imageData.data.set(pixels); + ctx.putImageData(imageData, 0, 0); + }, [blurhash, width, height]); + + return ; +} diff --git a/src/components/icons.tsx b/src/components/icons.tsx index 2dc14f50e..eef970b90 100644 --- a/src/components/icons.tsx +++ b/src/components/icons.tsx @@ -63,6 +63,7 @@ import ReverseLeft from "./icons/reverse-left"; import Pin01 from "./icons/pin-01"; import Translate01 from "./icons/translate-01"; import MessageChatSquare from "./icons/message-chat-square"; +import File01 from "./icons/file-01"; const defaultProps: IconProps = { boxSize: 4 }; @@ -232,7 +233,6 @@ export const WalletIcon = Wallet02; export const DownloadIcon = Download01; export const TranslateIcon = Translate01; - export const ChannelsIcon = MessageChatSquare; - export const ThreadIcon = MessageChatSquare; +export const FileIcon = File01; \ No newline at end of file diff --git a/src/components/layout/nav-items.tsx b/src/components/layout/nav-items.tsx index 5d57cd77b..c4f2e2dcf 100644 --- a/src/components/layout/nav-items.tsx +++ b/src/components/layout/nav-items.tsx @@ -20,6 +20,7 @@ import { NotesIcon, LightningIcon, ChannelsIcon, + FileIcon, } from "../icons"; import useCurrentAccount from "../../hooks/use-current-account"; import accountService from "../../services/account"; @@ -53,6 +54,7 @@ export default function NavItems() { else if (location.pathname.startsWith("/c/")) active = "communities"; else if (location.pathname.startsWith("/goals")) active = "goals"; else if (location.pathname.startsWith("/badges")) active = "badges"; + else if (location.pathname.startsWith("/files")) active = "files"; else if (location.pathname.startsWith("/emojis")) active = "emojis"; else if (location.pathname.startsWith("/settings")) active = "settings"; else if (location.pathname.startsWith("/tools")) active = "tools"; @@ -179,6 +181,15 @@ export default function NavItems() { > Lists + + + + + + + + + 0 && selectedImageTypes.length !== IMAGE_TYPES.length} + isChecked={selectedImageTypes.length === IMAGE_TYPES.length} + onChange={() => toggleCategory(IMAGE_TYPES)} + > + Images + + + {IMAGE_TYPES.map((type) => ( + toggleType(type)}> + {type} + + ))} + + + 0 && selectedVideoTypes.length !== VIDEO_TYPES.length} + isChecked={selectedVideoTypes.length === VIDEO_TYPES.length} + onChange={() => toggleCategory(VIDEO_TYPES)} + > + Videos + + + {VIDEO_TYPES.map((type) => ( + toggleType(type)}> + {type} + + ))} + + + 0 && selectedAudioTypes.length !== AUDIO_TYPES.length} + isChecked={selectedAudioTypes.length === AUDIO_TYPES.length} + onChange={() => toggleCategory(AUDIO_TYPES)} + > + Audio + + + {AUDIO_TYPES.map((type) => ( + toggleType(type)}> + {type} + + ))} + + + 0 && selectedTextTypes.length !== TEXT_TYPES.length} + isChecked={selectedTextTypes.length === TEXT_TYPES.length} + onChange={() => toggleCategory(TEXT_TYPES)} + > + Text + + + {TEXT_TYPES.map((type) => ( + toggleType(type)}> + {type} + + ))} + + + + + + ); +} diff --git a/yarn.lock b/yarn.lock index cc7bd00d0..9f107a6dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3056,6 +3056,11 @@ bn.js@^4.11.8: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== +blurhash@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-2.0.5.tgz#efde729fc14a2f03571a6aa91b49cba80d1abe4b" + integrity sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w== + boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"