Merge pull request #152 from cloudflare/images-grid

Images grid
pull/161/head
Dario Piotrowicz 2023-01-30 15:46:09 +00:00 zatwierdzone przez GitHub
commit 7776a5af23
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
11 zmienionych plików z 359 dodań i 54 usunięć

Wyświetl plik

@ -25,7 +25,7 @@ export async function createPublicNote(
db: D1Database,
content: string,
actor: Actor,
attachment: Array<objects.APObject> = [],
attachments: Array<objects.APObject> = [],
extraProperties: any = {}
): Promise<Note> {
const actorId = new URL(actor.id)
@ -41,8 +41,8 @@ export async function createPublicNote(
sensitive: false,
summary: null,
tag: [],
attachment,
attachment: attachments,
inReplyTo: null,
...extraProperties,
}

Wyświetl plik

@ -6,6 +6,8 @@ import type { APObject } from 'wildebeest/backend/src/activitypub/objects'
export function fromObject(obj: APObject): MediaAttachment {
if (obj.type === IMAGE) {
return fromObjectImage(obj)
} else if (obj.type === 'Video') {
return fromObjectVideo(obj)
} else if (obj.type === 'Document') {
return fromObjectDocument(obj as Document)
} else {
@ -17,39 +19,7 @@ export function fromObjectDocument(obj: Document): MediaAttachment {
if (obj.mediaType === 'image/jpeg' || obj.mediaType === 'image/png') {
return fromObjectImage(obj)
} else if (obj.mediaType === 'video/mp4') {
return {
url: new URL(obj.url),
preview_url: new URL(obj.url),
id: obj.url.toString(),
type: 'video',
meta: {
length: '0:01:28.65',
duration: 88.65,
fps: 24,
size: '1280x720',
width: 1280,
height: 720,
aspect: 1.7777777777777777,
audio_encode: 'aac (LC) (mp4a / 0x6134706D)',
audio_bitrate: '44100 Hz',
audio_channels: 'stereo',
original: {
width: 1280,
height: 720,
frame_rate: '6159375/249269',
duration: 88.654,
bitrate: 862056,
},
small: {
width: 400,
height: 225,
size: '400x225',
aspect: 1.7777777777777777,
},
},
description: 'test media description',
blurhash: 'UFBWY:8_0Jxv4mx]t8t64.%M-:IUWGWAt6M}',
}
return fromObjectVideo(obj)
} else {
throw new Error(`unsupported media Document type: ${JSON.stringify(obj)}`)
}
@ -83,3 +53,39 @@ function fromObjectImage(obj: APObject): MediaAttachment {
blurhash: 'UFBWY:8_0Jxv4mx]t8t64.%M-:IUWGWAt6M}',
}
}
function fromObjectVideo(obj: APObject): MediaAttachment {
return {
url: new URL(obj.url),
preview_url: new URL(obj.url),
id: obj.url.toString(),
type: 'video',
meta: {
length: '0:01:28.65',
duration: 88.65,
fps: 24,
size: '1280x720',
width: 1280,
height: 720,
aspect: 1.7777777777777777,
audio_encode: 'aac (LC) (mp4a / 0x6134706D)',
audio_bitrate: '44100 Hz',
audio_channels: 'stereo',
original: {
width: 1280,
height: 720,
frame_rate: '6159375/249269',
duration: 88.654,
bitrate: 862056,
},
small: {
width: 400,
height: 225,
size: '400x225',
aspect: 1.7777777777777777,
},
},
description: 'test media description',
blurhash: 'UFBWY:8_0Jxv4mx]t8t64.%M-:IUWGWAt6M}',
}
}

Wyświetl plik

@ -5,6 +5,8 @@ import { Note } from 'wildebeest/backend/src/activitypub/objects/note'
import { createReblog } from 'wildebeest/backend/src/mastodon/reblog'
import { createReply as createReplyInBackend } from 'wildebeest/backend/test/shared.utils'
import { createStatus } from 'wildebeest/backend/src/mastodon/status'
import type { APObject } from 'wildebeest/backend/src/activitypub/objects'
/**
* Run helper commands to initialize the database with actors, statuses, etc.
*/
@ -12,7 +14,13 @@ export async function init(domain: string, db: D1Database) {
const loadedStatuses: { status: MastodonStatus; note: Note }[] = []
for (const status of statuses) {
const actor = await getOrCreatePerson(domain, db, status.account)
const note = await createStatus(domain, db, actor, status.content)
const note = await createStatus(
domain,
db,
actor,
status.content,
status.media_attachments as unknown as APObject[]
)
loadedStatuses.push({ status, note })
}

Wyświetl plik

@ -37,7 +37,7 @@ export default component$<Props>(({ mediaAttachment }) => {
return (
<>
<div class="h-60">
<div class={`${store.isModalOpen ? '' : 'cursor-zoom-in'} w-full h-full`}>
<img
class="object-cover w-full h-full rounded"
style={{

Wyświetl plik

@ -1,5 +1,5 @@
import { component$ } from '@builder.io/qwik'
import Image from './ImageGallery'
import Image from './Image'
import Video from './Video'
import { MediaAttachment } from '~/types'

Wyświetl plik

@ -7,8 +7,8 @@ type Props = {
export default component$<Props>(({ mediaAttachment }) => {
return (
<div class="h-60">
<video class="object-cover w-full h-full rounded">
<div class="h-full">
<video controls class="object-cover w-full h-full rounded">
<source src={mediaAttachment.preview_url || mediaAttachment.url} type="video/mp4" />
</video>
</div>

Wyświetl plik

@ -0,0 +1,34 @@
.media-gallery:has(:nth-child(1)) {
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.media-gallery:has(:nth-child(2)) {
grid-template-columns: 1fr 1fr;
}
.media-gallery:has(:nth-child(3)) {
grid-template-rows: 1fr 1fr;
&:not(:has(:nth-child(4))) {
:nth-child(1) {
grid-column: 1;
grid-row: 1 / -1;
}
:nth-child(2) {
grid-column: 2;
grid-row: 1;
}
:nth-child(3) {
grid-column: 2;
grid-row: 2;
}
}
}
.media-gallery:has(:nth-child(4)) {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}

Wyświetl plik

@ -0,0 +1,26 @@
import { component$, useStylesScoped$ } from '@builder.io/qwik'
import { MediaAttachment } from '~/types'
import Media from './Media'
import styles from './index.scss?inline'
type Props = {
medias: MediaAttachment[]
}
export const MediaGallery = component$<Props>(({ medias }) => {
useStylesScoped$(styles)
return (
<>
{!!medias.length && (
<div class={`media-gallery overflow-hidden grid gap-1 h-52 md:h-60 lg:h-72 xl:h-80`}>
{medias.map((media) => (
<div class="w-full flex items-center justify-center overflow-hidden bg-black">
<Media mediaAttachment={media} />
</div>
))}
</div>
)}
</>
)
})

Wyświetl plik

@ -2,9 +2,9 @@ import { component$, $, useStyles$ } from '@builder.io/qwik'
import { Link, useNavigate } from '@builder.io/qwik-city'
import { formatTimeAgo } from '~/utils/dateTime'
import { Avatar } from '../avatar'
import Media from './Media'
import type { Account, MastodonStatus } from '~/types'
import styles from './index.scss?inline'
import { MediaGallery } from '../MediaGallery.tsx'
type Props = {
status: MastodonStatus
@ -48,11 +48,9 @@ export default component$((props: Props) => {
<div class="leading-relaxed status-content" dangerouslySetInnerHTML={status.content} />
</div>
{status.media_attachments.map((attachment) => (
<Media mediaAttachment={attachment} />
))}
<MediaGallery medias={status.media_attachments} />
{status.card && status.media_attachments.length == 0 && (
{status.card && status.media_attachments.length === 0 && (
<a class="no-underline" href={status.card.url}>
<div class="rounded flex border border-wildebeest-600">
<img class="w-16 h-16" src={status.card.image} />

Wyświetl plik

@ -1,6 +1,7 @@
import { MastodonLink, MastodonStatus, TagDetails } from './types'
export const statuses: MastodonStatus[] = [
// Raw statuses taken directly from mastodon
const mastodonRawStatuses: MastodonStatus[] = [
{
id: '109632280734155646',
created_at: '2023-01-04T17:53:39.000Z',
@ -1469,7 +1470,7 @@ export const statuses: MastodonStatus[] = [
url: 'https://techcommunity.microsoft.com/t5/discussions/how-to-disable-writing-assistance-via-group-policy/td-p/3648422',
title: 'How to disable writing assistance via Group Policy',
description:
'Hello,   new Edge version 106 has got settings "Use writing assistance". I look for matching settings in group policy for Edge but with not succes. I use the latest adml and admx files.  Group setting Disable spellcheck will help but only for Edge 104 (and lower). Edge 106 has replaced settings Spel...',
'Hello, new Edge version 106 has got settings "Use writing assistance". I look for matching settings in group policy for Edge but with not succes. I use the latest adml and admx files. Group setting Disable spellcheck will help but only for Edge 104 (and lower). Edge 106 has replaced settings Spel...',
type: 'link',
author_name: '',
author_url: '',
@ -1555,8 +1556,245 @@ export const statuses: MastodonStatus[] = [
card: null,
poll: null,
},
{
id: '109734788115874319',
created_at: '2023-01-22T20:39:44.678Z',
in_reply_to_id: null,
in_reply_to_account_id: null,
sensitive: false,
spoiler_text: '',
visibility: 'public',
language: 'en',
uri: 'https://mastodon.design/users/rafa/statuses/109734788115874319',
url: 'https://mastodon.design/@rafa/109734788115874319',
replies_count: 43,
reblogs_count: 256,
favourites_count: 537,
edited_at: null,
content:
'\u003cp\u003eHi, meet HiDock!\u003c/p\u003e\u003cp\u003eIt\u0026#39;s a free Mac app that lets you set different Dock settings for different display configurations\u003c/p\u003e\u003cp\u003e\u003ca href="https://hidock.app" target="_blank" rel="nofollow noopener noreferrer"\u003e\u003cspan class="invisible"\u003ehttps://\u003c/span\u003e\u003cspan class=""\u003ehidock.app\u003c/span\u003e\u003cspan class="invisible"\u003e\u003c/span\u003e\u003c/a\u003e →\u003c/p\u003e',
reblog: null,
application: {
name: 'Web',
website: null,
},
account: {
id: '11932',
username: 'rafa',
acct: 'rafa',
display_name: 'Rafa',
locked: false,
bot: false,
discoverable: true,
group: false,
created_at: '2018-08-21T00:00:00.000Z',
note: '\u003cp\u003eIm a designer and app developer, currently working on Sketch, and Hand Mirror for Mac\u003c/p\u003e',
url: 'https://mastodon.design/@rafa',
avatar: 'https://cdn.masto.host/mastodondesign/accounts/avatars/000/011/932/original/8f601be03c98b2e8.png',
avatar_static: 'https://cdn.masto.host/mastodondesign/accounts/avatars/000/011/932/original/8f601be03c98b2e8.png',
header: 'https://cdn.masto.host/mastodondesign/accounts/headers/000/011/932/original/668f6d32abb54252.jpeg',
header_static:
'https://cdn.masto.host/mastodondesign/accounts/headers/000/011/932/original/668f6d32abb54252.jpeg',
followers_count: 2511,
following_count: 437,
statuses_count: 1115,
last_status_at: '2023-01-23',
noindex: false,
emojis: [],
fields: [
{
name: 'Website',
value:
'\u003ca href="https://rafa.design" target="_blank" rel="nofollow noopener noreferrer me"\u003e\u003cspan class="invisible"\u003ehttps://\u003c/span\u003e\u003cspan class=""\u003erafa.design\u003c/span\u003e\u003cspan class="invisible"\u003e\u003c/span\u003e\u003c/a\u003e',
verified_at: '2022-11-06T16:49:24.339+00:00',
},
{
name: 'App',
value:
'\u003ca href="http://handmirror.app" target="_blank" rel="nofollow noopener noreferrer me"\u003e\u003cspan class="invisible"\u003ehttp://\u003c/span\u003e\u003cspan class=""\u003ehandmirror.app\u003c/span\u003e\u003cspan class="invisible"\u003e\u003c/span\u003e\u003c/a\u003e',
verified_at: null,
},
{
name: 'Podcast',
value:
'\u003ca href="http://layout.fm" target="_blank" rel="nofollow noopener noreferrer me"\u003e\u003cspan class="invisible"\u003ehttp://\u003c/span\u003e\u003cspan class=""\u003elayout.fm\u003c/span\u003e\u003cspan class="invisible"\u003e\u003c/span\u003e\u003c/a\u003e',
verified_at: null,
},
{
name: 'Pronouns',
value: 'He/Them',
verified_at: null,
},
],
},
media_attachments: [
{
id: '109734774350200717',
type: 'image',
url: 'https://cdn.masto.host/mastodondesign/media_attachments/files/109/734/774/350/200/717/original/51054ec255c7a366.png',
preview_url:
'https://cdn.masto.host/mastodondesign/media_attachments/files/109/734/774/350/200/717/small/51054ec255c7a366.png',
remote_url: null,
preview_remote_url: null,
text_url: null,
meta: {
original: {
width: 1821,
height: 1138,
size: '1821x1138',
aspect: 1.6001757469244289,
},
small: {
width: 606,
height: 379,
size: '606x379',
aspect: 1.5989445910290236,
},
focus: {
x: 0.0,
y: 0.0,
},
},
description:
'App icon for the app HiDock, against the default macOS Ventura background. The icon resembles an old school Apple display, with a aluminum frame, glossy black bezels, and what looks like the Snow Leopard wallpaper with a dock highlighted',
blurhash: 'UIQsS4?R}zRh%_$wKP-4-xt11mwwu0xUMmr?',
},
{
id: '109734775049506533',
type: 'image',
url: 'https://cdn.masto.host/mastodondesign/media_attachments/files/109/734/775/049/506/533/original/b6c22d380e50cd11.png',
preview_url:
'https://cdn.masto.host/mastodondesign/media_attachments/files/109/734/775/049/506/533/small/b6c22d380e50cd11.png',
remote_url: null,
preview_remote_url: null,
text_url: null,
meta: {
original: {
width: 1821,
height: 1138,
size: '1821x1138',
aspect: 1.6001757469244289,
},
small: {
width: 606,
height: 379,
size: '606x379',
aspect: 1.5989445910290236,
},
focus: {
x: 0.0,
y: 0.0,
},
},
description:
'Screenshot of HiDock, and with a subtitle that reads "Set different Docks settings for different displays"',
blurhash: 'UaQ@*F%HqsadyWxVNGw[QkobPCo0%1n#Rls:',
},
{
id: '109734775691562296',
type: 'image',
url: 'https://cdn.masto.host/mastodondesign/media_attachments/files/109/734/775/691/562/296/original/266c1740f6e4cdc1.png',
preview_url:
'https://cdn.masto.host/mastodondesign/media_attachments/files/109/734/775/691/562/296/small/266c1740f6e4cdc1.png',
remote_url: null,
preview_remote_url: null,
text_url: null,
meta: {
original: {
width: 1821,
height: 1138,
size: '1821x1138',
aspect: 1.6001757469244289,
},
small: {
width: 606,
height: 379,
size: '606x379',
aspect: 1.5989445910290236,
},
focus: {
x: 0.0,
y: 0.0,
},
},
description:
'Screenshot of HiDock, now in dark mode, with a caption that reads "Customize Size, Position, and Hidden Preference"',
blurhash: 'UgIB[UxW}Aox=Y$#xYs,sos-FdbFwJsmS4n+',
},
{
id: '109734776307839727',
type: 'image',
url: 'https://cdn.masto.host/mastodondesign/media_attachments/files/109/734/776/307/839/727/original/6085716d5f865ce1.png',
preview_url:
'https://cdn.masto.host/mastodondesign/media_attachments/files/109/734/776/307/839/727/small/6085716d5f865ce1.png',
remote_url: null,
preview_remote_url: null,
text_url: null,
meta: {
original: {
width: 1821,
height: 1138,
size: '1821x1138',
aspect: 1.6001757469244289,
},
small: {
width: 606,
height: 379,
size: '606x379',
aspect: 1.5989445910290236,
},
focus: {
x: 0.0,
y: 0.0,
},
},
description:
'Another screenshot of HiDock, this time you can see a "Meet the Developer section", with a caption that reads "Runs in the background, no clutter or extra icons"',
blurhash: 'UiH+IDxW}Toc-6xWxWs-xGs-F0bGwJsnS5n%',
},
],
mentions: [],
tags: [],
emojis: [],
card: {
url: 'https://hidock.app/',
title: 'HiDock, for Mac',
description: 'Custom Dock settings for different displays',
type: 'link',
author_name: '',
author_url: '',
provider_name: 'HiDock, for Mac',
provider_url: '',
html: '',
width: 400,
height: 200,
image:
'https://cdn.masto.host/mastodondesign/cache/preview_cards/images/001/081/966/original/f9d41b35290d4ddf.jpg',
embed_url: '',
blurhash: 'UHIV*-iwxuIU0}mlBp9|V@gNiws:FxpIVsw^',
},
poll: null,
},
]
export const statuses: MastodonStatus[] = mastodonRawStatuses.map((rawStatus) => ({
...rawStatus,
media_attachments: rawStatus.media_attachments.map((mediaAttachment) => ({
...mediaAttachment,
type: getStandardMediaType(mediaAttachment.type),
})),
}))
function getStandardMediaType(mediaAttachmentMastodonType: string): string {
switch (mediaAttachmentMastodonType) {
case 'image':
return 'Image'
case 'video':
return 'Video'
}
return mediaAttachmentMastodonType
}
export const replies: MastodonStatus[] = [
{
id: '209630407170172986',

Wyświetl plik

@ -8,6 +8,7 @@ import * as contextAPI from 'wildebeest/functions/api/v1/statuses/[id]/context'
import { Link, loader$ } from '@builder.io/qwik-city'
import StickyHeader from '~/components/StickyHeader/StickyHeader'
import { Avatar } from '~/components/avatar'
import { MediaGallery } from '~/components/MediaGallery.tsx'
export const statusLoader = loader$<
{ DATABASE: D1Database; domain: string },
@ -30,7 +31,6 @@ export const statusLoader = loader$<
export default component$(() => {
const { status, context } = statusLoader.use().value
const mediaAttachment = (status.media_attachments && status.media_attachments[0]) || null
return (
<>
@ -44,14 +44,9 @@ export default component$(() => {
</StickyHeader>
<div class="bg-wildebeest-700 p-4">
<AccountCard status={status} />
<div class="leading-normal status-content text-lg" dangerouslySetInnerHTML={status.content} />
{mediaAttachment && (
<div class="flex justify-center" style={{ height: `${mediaAttachment.meta.small.height}px` }}>
{mediaAttachment.preview_url && <img class="rounded" src={mediaAttachment.preview_url} />}
</div>
)}
<MediaGallery medias={status.media_attachments} />
<InfoTray status={status} />
</div>