This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'development'

+237 -145
+41
package-lock.json
··· 17594 17594 "integrity": "sha512-khBdc+w/Gv+cS8e/Pbnaw/FXcBUeKrRVik9IxfXtgREOWyJhR4tj43n3amkVogJ/yeQUqzkrZcFhtIxIdqmmcQ==", 17595 17595 "dev": true 17596 17596 }, 17597 + "node_modules/embla-carousel": { 17598 + "version": "8.6.0", 17599 + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", 17600 + "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==" 17601 + }, 17602 + "node_modules/embla-carousel-react": { 17603 + "version": "8.6.0", 17604 + "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.6.0.tgz", 17605 + "integrity": "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==", 17606 + "dependencies": { 17607 + "embla-carousel": "8.6.0", 17608 + "embla-carousel-reactive-utils": "8.6.0" 17609 + }, 17610 + "peerDependencies": { 17611 + "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" 17612 + } 17613 + }, 17614 + "node_modules/embla-carousel-reactive-utils": { 17615 + "version": "8.6.0", 17616 + "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.6.0.tgz", 17617 + "integrity": "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==", 17618 + "peerDependencies": { 17619 + "embla-carousel": "8.6.0" 17620 + } 17621 + }, 17597 17622 "node_modules/emittery": { 17598 17623 "version": "0.13.1", 17599 17624 "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", ··· 32264 32289 "name": "semble-webapp", 32265 32290 "version": "0.1.0", 32266 32291 "dependencies": { 32292 + "@mantine-bites/lightbox": "^2.0.0", 32267 32293 "@mantine/code-highlight": "9.0.0", 32268 32294 "@mantine/core": "9.0.0", 32269 32295 "@mantine/dates": "9.0.0", ··· 32285 32311 "date-fns": "^4.1.0", 32286 32312 "dayjs": "^1.11.13", 32287 32313 "dompurify": "^3.4.2", 32314 + "embla-carousel": "^8.6.0", 32315 + "embla-carousel-react": "^8.6.0", 32288 32316 "next": "16.2.3", 32289 32317 "react": "19.2.5", 32290 32318 "react-dom": "19.2.5", ··· 32329 32357 "typescript": "^5.3.3", 32330 32358 "vite": "^8.0.10", 32331 32359 "vitest": "^4.1.5" 32360 + } 32361 + }, 32362 + "src/webapp/node_modules/@mantine-bites/lightbox": { 32363 + "version": "2.0.0", 32364 + "resolved": "https://registry.npmjs.org/@mantine-bites/lightbox/-/lightbox-2.0.0.tgz", 32365 + "integrity": "sha512-r2b/L/VnvhiowVFHWfX/J1WLYrnEPOxAP7NQavjt/GwE9S6pf6flt2CFjaMhcIOfcHdq7D27l8VGd4iKpk78AQ==", 32366 + "peerDependencies": { 32367 + "@mantine/core": ">=9.0.0", 32368 + "@mantine/hooks": ">=9.0.0", 32369 + "embla-carousel": ">=8.0.0", 32370 + "embla-carousel-react": ">=8.0.0", 32371 + "react": "^19.2.x", 32372 + "react-dom": "^19.2.x" 32332 32373 } 32333 32374 }, 32334 32375 "src/webapp/node_modules/@mantine/code-highlight": {
+1
src/webapp/.storybook/preview.tsx
··· 1 1 import type { Preview } from '@storybook/nextjs-vite'; 2 2 import '@mantine/core/styles.css'; 3 + import '@mantine-bites/lightbox/styles.css'; 3 4 import { MantineProvider, v8CssVariablesResolver } from '@mantine/core'; 4 5 import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; 5 6 import { theme } from '../styles/theme';
+189 -143
src/webapp/features/platforms/bluesky/components/imageEmbed/ImageEmbed.tsx
··· 1 1 'use client'; 2 2 3 3 import { AppBskyEmbedImages } from '@atproto/api'; 4 - import { 5 - AspectRatio, 6 - SimpleGrid, 7 - Image, 8 - Anchor, 9 - Spoiler, 10 - Button, 11 - } from '@mantine/core'; 4 + import { Lightbox } from '@mantine-bites/lightbox'; 5 + import { AspectRatio, SimpleGrid, Image, Spoiler, Button } from '@mantine/core'; 6 + import { useState } from 'react'; 12 7 import styles from './ImageEmbed.module.css'; 13 8 import { useUserSettings } from '@/features/settings/lib/queries/useUserSettings'; 14 9 ··· 18 13 19 14 export default function ImageEmbed(props: Props) { 20 15 const { settings } = useUserSettings(); 16 + const [lightboxOpened, setLightboxOpened] = useState(false); 17 + const [initialSlide, setInitialSlide] = useState(0); 21 18 22 - if (settings.cardView === 'grid') { 23 - if (props.images.length === 3) { 24 - return ( 25 - <SimpleGrid cols={2} spacing="xs"> 26 - <AspectRatio 27 - ratio={ 28 - props.images[0]?.aspectRatio 29 - ? props.images[0].aspectRatio.width / 30 - props.images[0].aspectRatio.height 31 - : 1 / 1 32 - } 33 - > 34 - <Image 35 - src={props.images[0].thumb} 36 - alt={props.images[0].alt} 37 - radius="sm" 38 - h={'100%'} 39 - w={'100%'} 40 - mah={160} 41 - /> 42 - </AspectRatio> 43 - <SimpleGrid cols={1} spacing="xs"> 44 - {props.images.slice(1).map((img, i) => { 45 - const ratio = img?.aspectRatio 46 - ? img.aspectRatio.width / img.aspectRatio.height 47 - : 1 / 1; 19 + const lightboxImages = props.images.map((img) => ({ 20 + src: img.fullsize, 21 + alt: img.alt, 22 + })); 48 23 49 - return ( 50 - <AspectRatio ratio={ratio} key={i + 1}> 51 - <Image 52 - src={img.thumb} 53 - alt={img.alt} 54 - radius="sm" 55 - h={'100%'} 56 - w={'100%'} 57 - mah={75} 58 - /> 59 - </AspectRatio> 60 - ); 61 - })} 62 - </SimpleGrid> 63 - </SimpleGrid> 64 - ); 65 - } 24 + const openLightbox = (e: React.MouseEvent, index: number) => { 25 + e.stopPropagation(); 26 + setInitialSlide(index); 27 + setLightboxOpened(true); 28 + }; 66 29 67 - return ( 68 - <SimpleGrid cols={props.images.length > 1 ? 2 : 1} spacing="xs"> 69 - {props.images.map((img, i) => { 70 - const ratio = 71 - props.images.length === 1 72 - ? img?.aspectRatio 73 - ? img.aspectRatio.width / img.aspectRatio.height 74 - : 16 / 9 75 - : img?.aspectRatio 76 - ? img.aspectRatio.width / img.aspectRatio.height 77 - : 1 / 1; 78 - 79 - return ( 80 - <AspectRatio ratio={ratio} key={i}> 81 - <Image 82 - src={img.thumb} 83 - alt={img.alt} 84 - radius="sm" 85 - h={'100%'} 86 - w={'100%'} 87 - mah={props.images.length === 1 ? 200 : 150} 88 - /> 89 - </AspectRatio> 90 - ); 91 - })} 92 - </SimpleGrid> 93 - ); 94 - } 30 + const lightbox = ( 31 + <div onClick={(e) => e.stopPropagation()}> 32 + <Lightbox.Root 33 + opened={lightboxOpened} 34 + onClose={() => setLightboxOpened(false)} 35 + initialSlide={initialSlide} 36 + > 37 + <Lightbox.Toolbar> 38 + <Lightbox.CloseButton /> 39 + </Lightbox.Toolbar> 40 + <Lightbox.Controls /> 41 + <Lightbox.Counter /> 42 + <Lightbox.Slides> 43 + {lightboxImages.map((img) => ( 44 + <Lightbox.Slide key={img.src}> 45 + <img src={img.src} alt={img.alt} /> 46 + </Lightbox.Slide> 47 + ))} 48 + </Lightbox.Slides> 49 + </Lightbox.Root> 50 + </div> 51 + ); 95 52 96 - return ( 97 - <Spoiler 98 - classNames={styles} 99 - showLabel={ 100 - <Button 101 - component="span" 102 - size="xs" 103 - variant="light" 104 - color="gray" 105 - radius={'sm'} 106 - fullWidth 107 - > 108 - Expand image 109 - </Button> 110 - } 111 - hideLabel={ 112 - <Button 113 - component="span" 114 - size="xs" 115 - variant="light" 116 - color="gray" 117 - radius={'sm'} 118 - fullWidth 119 - > 120 - Collape image 121 - </Button> 122 - } 123 - maxHeight={280} 124 - > 125 - {props.images.length === 3 ? ( 126 - <SimpleGrid cols={2} spacing="xs"> 127 - <AspectRatio 128 - ratio={ 129 - props.images[0]?.aspectRatio 130 - ? props.images[0].aspectRatio.width / 131 - props.images[0].aspectRatio.height 132 - : 1 / 1 133 - } 134 - > 135 - <Anchor href={props.images[0].fullsize} target="_blank"> 53 + if (settings.cardView === 'grid') { 54 + if (props.images.length === 3) { 55 + return ( 56 + <> 57 + <SimpleGrid cols={2} spacing="xs"> 58 + <AspectRatio 59 + ratio={ 60 + props.images[0]?.aspectRatio 61 + ? props.images[0].aspectRatio.width / 62 + props.images[0].aspectRatio.height 63 + : 1 / 1 64 + } 65 + > 136 66 <Image 137 67 src={props.images[0].thumb} 138 68 alt={props.images[0].alt} 139 69 radius="sm" 140 70 h={'100%'} 141 71 w={'100%'} 72 + mah={160} 73 + style={{ cursor: 'pointer' }} 74 + onClick={(e) => openLightbox(e, 0)} 142 75 /> 143 - </Anchor> 144 - </AspectRatio> 145 - <SimpleGrid cols={1} spacing="xs"> 146 - {props.images.slice(1).map((img, i) => { 147 - const ratio = img?.aspectRatio 148 - ? img.aspectRatio.width / img.aspectRatio.height 149 - : 1 / 1; 76 + </AspectRatio> 77 + <SimpleGrid cols={1} spacing="xs"> 78 + {props.images.slice(1).map((img, i) => { 79 + const ratio = img?.aspectRatio 80 + ? img.aspectRatio.width / img.aspectRatio.height 81 + : 1 / 1; 150 82 151 - return ( 152 - <AspectRatio ratio={ratio} key={i + 1}> 153 - <Anchor href={img.fullsize} target="_blank"> 83 + return ( 84 + <AspectRatio ratio={ratio} key={i + 1}> 154 85 <Image 155 86 src={img.thumb} 156 87 alt={img.alt} 157 88 radius="sm" 158 89 h={'100%'} 159 90 w={'100%'} 91 + mah={75} 92 + style={{ cursor: 'pointer' }} 93 + onClick={(e) => openLightbox(e, i + 1)} 160 94 /> 161 - </Anchor> 162 - </AspectRatio> 163 - ); 164 - })} 95 + </AspectRatio> 96 + ); 97 + })} 98 + </SimpleGrid> 165 99 </SimpleGrid> 166 - </SimpleGrid> 167 - ) : ( 100 + {lightbox} 101 + </> 102 + ); 103 + } 104 + 105 + return ( 106 + <> 168 107 <SimpleGrid cols={props.images.length > 1 ? 2 : 1} spacing="xs"> 169 108 {props.images.map((img, i) => { 170 109 const ratio = ··· 178 117 179 118 return ( 180 119 <AspectRatio ratio={ratio} key={i}> 181 - <Anchor href={img.fullsize} target="_blank"> 120 + <Image 121 + src={img.thumb} 122 + alt={img.alt} 123 + radius="sm" 124 + h={'100%'} 125 + w={'100%'} 126 + mah={props.images.length === 1 ? 200 : 150} 127 + style={{ cursor: 'pointer' }} 128 + onClick={(e) => openLightbox(e, i)} 129 + /> 130 + </AspectRatio> 131 + ); 132 + })} 133 + </SimpleGrid> 134 + {lightbox} 135 + </> 136 + ); 137 + } 138 + 139 + return ( 140 + <> 141 + <Spoiler 142 + classNames={styles} 143 + showLabel={ 144 + <Button 145 + component="span" 146 + size="xs" 147 + variant="light" 148 + color="gray" 149 + radius={'sm'} 150 + fullWidth 151 + > 152 + Expand image 153 + </Button> 154 + } 155 + hideLabel={ 156 + <Button 157 + component="span" 158 + size="xs" 159 + variant="light" 160 + color="gray" 161 + radius={'sm'} 162 + fullWidth 163 + > 164 + Collape image 165 + </Button> 166 + } 167 + maxHeight={280} 168 + > 169 + {props.images.length === 3 ? ( 170 + <SimpleGrid cols={2} spacing="xs"> 171 + <AspectRatio 172 + ratio={ 173 + props.images[0]?.aspectRatio 174 + ? props.images[0].aspectRatio.width / 175 + props.images[0].aspectRatio.height 176 + : 1 / 1 177 + } 178 + > 179 + <Image 180 + src={props.images[0].thumb} 181 + alt={props.images[0].alt} 182 + radius="sm" 183 + h={'100%'} 184 + w={'100%'} 185 + style={{ cursor: 'pointer' }} 186 + onClick={(e) => openLightbox(e, 0)} 187 + /> 188 + </AspectRatio> 189 + <SimpleGrid cols={1} spacing="xs"> 190 + {props.images.slice(1).map((img, i) => { 191 + const ratio = img?.aspectRatio 192 + ? img.aspectRatio.width / img.aspectRatio.height 193 + : 1 / 1; 194 + 195 + return ( 196 + <AspectRatio ratio={ratio} key={i + 1}> 197 + <Image 198 + src={img.thumb} 199 + alt={img.alt} 200 + radius="sm" 201 + h={'100%'} 202 + w={'100%'} 203 + style={{ cursor: 'pointer' }} 204 + onClick={(e) => openLightbox(e, i + 1)} 205 + /> 206 + </AspectRatio> 207 + ); 208 + })} 209 + </SimpleGrid> 210 + </SimpleGrid> 211 + ) : ( 212 + <SimpleGrid cols={props.images.length > 1 ? 2 : 1} spacing="xs"> 213 + {props.images.map((img, i) => { 214 + const ratio = 215 + props.images.length === 1 216 + ? img?.aspectRatio 217 + ? img.aspectRatio.width / img.aspectRatio.height 218 + : 16 / 9 219 + : img?.aspectRatio 220 + ? img.aspectRatio.width / img.aspectRatio.height 221 + : 1 / 1; 222 + 223 + return ( 224 + <AspectRatio ratio={ratio} key={i}> 182 225 <Image 183 226 src={img.thumb} 184 227 alt={img.alt} 185 228 radius="sm" 186 229 h={'100%'} 187 230 w={'100%'} 231 + style={{ cursor: 'pointer' }} 232 + onClick={(e) => openLightbox(e, i)} 188 233 /> 189 - </Anchor> 190 - </AspectRatio> 191 - ); 192 - })} 193 - </SimpleGrid> 194 - )} 195 - </Spoiler> 234 + </AspectRatio> 235 + ); 236 + })} 237 + </SimpleGrid> 238 + )} 239 + </Spoiler> 240 + {lightbox} 241 + </> 196 242 ); 197 243 }
+1 -1
src/webapp/features/platforms/bluesky/components/videoEmbed/VideoEmbed.tsx
··· 20 20 : 16 / 9; 21 21 22 22 return ( 23 - <AspectRatio ratio={ratio}> 23 + <AspectRatio ratio={ratio} style={{ position: 'relative', zIndex: 1 }}> 24 24 <MediaPlayer 25 25 crossOrigin 26 26 playsInline
+1 -1
src/webapp/next-env.d.ts
··· 1 1 /// <reference types="next" /> 2 2 /// <reference types="next/image-types/global" /> 3 - import "./.next/types/routes.d.ts"; 3 + import "./.next/dev/types/routes.d.ts"; 4 4 5 5 // NOTE: This file should not be edited 6 6 // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
+3
src/webapp/package.json
··· 28 28 "build-storybook": "storybook build" 29 29 }, 30 30 "dependencies": { 31 + "@mantine-bites/lightbox": "^2.0.0", 31 32 "@mantine/code-highlight": "9.0.0", 32 33 "@mantine/core": "9.0.0", 33 34 "@mantine/dates": "9.0.0", ··· 49 50 "date-fns": "^4.1.0", 50 51 "dayjs": "^1.11.13", 51 52 "dompurify": "^3.4.2", 53 + "embla-carousel": "^8.6.0", 54 + "embla-carousel-react": "^8.6.0", 52 55 "next": "16.2.3", 53 56 "react": "19.2.5", 54 57 "react-dom": "19.2.5",
+1
src/webapp/providers/mantine.tsx
··· 3 3 import '@mantine/core/styles.css'; 4 4 import '@mantine/notifications/styles.css'; 5 5 import '@mantine/code-highlight/styles.css'; 6 + import '@mantine-bites/lightbox/styles.css'; 6 7 import { theme } from '@/styles/theme'; 7 8 import { 8 9 MantineProvider as BaseProvider,