Skip to content
This repository was archived by the owner on Mar 18, 2024. It is now read-only.

Commit c22e3fb

Browse files
committed
show photos on list page
1 parent f839f09 commit c22e3fb

11 files changed

+129
-48
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ Check out the [Nuxt documentation](https://nuxt.com/docs/getting-started/introdu
3232
- [x] Handle line breaks in comments and post descriptions
3333
- [x] Handle NSFW modesty panel on post page
3434
- [x] Handle videos on post page
35-
- [ ] Show Photos on list page
36-
- [ ] Handle NSFW modesty panel on list page
35+
- [x] Show Photos on list page
36+
- [x] Handle NSFW modesty panel on list page
3737
- [ ] Add NSFW option
3838
- [ ] Display 9 files on list pages (follow pagination logic)
3939
- [ ] Add grid layout option

components/CommentDetail.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
<li>Posted at: {{ comment.posted_at }}</li>
77
<li>
88
Posted by:
9-
<nuxt-link :to="`/user/${comment.user.name}`">{{
9+
<NuxtLink :to="`/user/${comment.user.name}`">{{
1010
comment.user.name
11-
}}</nuxt-link>
11+
}}</NuxtLink>
1212
</li>
1313
<li>
1414
<img

components/PostActions.vue

+18-11
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,23 @@
1111
:saved="post.saved"
1212
:shakes="user.shakes"
1313
/>
14-
<button v-if="isOwnPost && !isEditing" type="button" @click="toggleEditing">
15-
Edit
16-
</button>
17-
<PostEditForm
18-
v-if="isOwnPost && isEditing"
19-
:title="post.title"
20-
:description="post.description"
21-
:sharekey="post.sharekey"
22-
@save="handleEdit"
23-
@cancel="toggleEditing"
24-
/>
14+
<template v-if="isEditable">
15+
<button
16+
v-if="isOwnPost && !isEditing"
17+
type="button"
18+
@click="toggleEditing"
19+
>
20+
Edit
21+
</button>
22+
<PostEditForm
23+
v-if="isOwnPost && isEditing"
24+
:title="post.title"
25+
:description="post.description"
26+
:sharekey="post.sharekey"
27+
@save="handleEdit"
28+
@cancel="toggleEditing"
29+
/>
30+
</template>
2531
</div>
2632
</template>
2733

@@ -35,6 +41,7 @@ defineProps<{
3541
post: MltshpFile;
3642
isOwnPost: boolean;
3743
user: AuthUser | undefined;
44+
isEditable: boolean;
3845
}>();
3946
4047
const isEditing = ref(false);

components/PostCard.vue

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<template>
2+
<li>
3+
<PostDetails :post="post" :is-detail="false" />
4+
<PostActions
5+
:post="post"
6+
:is-own-post="isOwnPost(post, user)"
7+
:is-editable="false"
8+
:user="user"
9+
/>
10+
</li>
11+
</template>
12+
13+
<script setup lang="ts">
14+
import { AuthUser } from '~/types/AuthUser';
15+
import { MltshpFile } from '~/types/MltshpFile';
16+
17+
defineProps<{
18+
post: MltshpFile;
19+
}>();
20+
21+
// Get the authenticated user
22+
const { data: authData } = useAuth();
23+
const user = authData.value?.user as AuthUser | undefined;
24+
25+
// Is this a post by the user? If so, we'll hide the like button, etc.
26+
const { isOwnPost } = usePost();
27+
</script>

components/PostDetails.vue

+25-11
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,36 @@
11
<template>
22
<div>
3-
<h1>
4-
{{ post.title || post.name }}
5-
</h1>
3+
<component :is="isDetail ? 'h1' : 'h2'">
4+
<NuxtLink v-if="!isDetail" :to="postLink">{{ displayTitle }}</NuxtLink>
5+
<template v-else>{{ displayTitle }}</template>
6+
</component>
67
<NSFWShield v-if="post.nsfw" :width="post.width" :height="post.height">
7-
<PostMedia :post="post" />
8+
<PostMedia :post="post" :post-link="postLink" />
89
</NSFWShield>
9-
<PostMedia v-else :post="post" />
10+
<PostMedia v-else :post="post" :post-link="postLink" />
1011
<!-- eslint-disable-next-line vue/no-v-html -->
1112
<div v-html="formattedDescription" />
1213
<ul>
1314
<li>Comment Count: {{ post.comments }}</li>
1415
<li>Views: {{ post.views }}</li>
1516
<li>Likes: {{ post.likes }}</li>
1617
<li>Saves: {{ post.saves }}</li>
17-
<li>NSFW: {{ post.nsfw }}</li>
1818
<li>
1919
Posted at:
20-
<NuxtLink :to="`/post/${post.sharekey}`">
20+
<NuxtLink :to="`postLink`">
2121
{{ post.posted_at }}
2222
</NuxtLink>
2323
</li>
2424
<li v-if="post.user">
2525
Posted by:
26-
<nuxt-link :to="`/user/${post.user.name}`">
26+
<NuxtLink :to="`/user/${post.user.name}`">
2727
{{ post.user.name }}
28-
</nuxt-link>
28+
</NuxtLink>
2929
<img
3030
v-if="post.user.profile_image_url"
3131
:src="post.user.profile_image_url"
3232
alt=""
33-
width="50"
33+
width="20"
3434
/>
3535
</li>
3636
</ul>
@@ -42,11 +42,25 @@ import { MltshpFile } from '~/types/MltshpFile';
4242
4343
const props = defineProps<{
4444
post: MltshpFile;
45-
isOwnPost: boolean;
45+
isDetail: boolean;
4646
}>();
4747
4848
// Apply formatting to the post description
4949
const formattedDescription = computed(() =>
5050
simpleFormatter(props.post.description ?? '')
5151
);
52+
53+
// Link to the original URL on detail, the detail page otherwise
54+
const postLink = computed(() =>
55+
props.isDetail
56+
? props.post.original_image_url
57+
: `/post/${props.post.sharekey}`
58+
);
59+
60+
// If no title, display filename. If no filename, display sharekey.
61+
const displayTitle = computed(() => {
62+
if (props.post.title) return props.post.title;
63+
if (props.post.name) return props.post.name;
64+
return props.post.sharekey;
65+
});
5266
</script>

components/PostMedia.vue

+28-6
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,49 @@
11
<template>
22
<div>
3-
<a v-if="post.original_image_url" :href="post.original_image_url">
3+
<NuxtLink v-if="post.original_image_url" :to="postLink">
44
<img
5-
:src="post.original_image_url"
5+
:srcset="`
6+
${post.original_image_url}?width=${imgWidth}&dpr=1 1x,
7+
${post.original_image_url}?width=${imgWidth}&dpr=2 2x,
8+
${post.original_image_url}?width=${imgWidth}&dpr=2 2.5x
9+
`"
10+
:src="`${post.original_image_url}?width=${imgWidth}`"
611
:alt="post.title"
7-
:width="post.width"
8-
:height="post.height"
12+
:width="imgWidth"
13+
:height="imgHeight"
14+
loading="lazy"
15+
decoding="async"
916
/>
10-
</a>
17+
</NuxtLink>
1118
<VideoEmbed v-if="post.url" :url="post.url" />
1219
</div>
1320
</template>
1421

1522
<script setup lang="ts">
1623
import { MltshpFile } from '~/types/MltshpFile';
1724
18-
defineProps<{
25+
const props = defineProps<{
1926
post: MltshpFile;
27+
postLink: string | undefined;
2028
}>();
29+
30+
const constrainedWidth = 550;
31+
32+
const imgWidth = computed(() =>
33+
props.post.width >= constrainedWidth ? constrainedWidth : props.post.width
34+
);
35+
36+
const aspectRatio = computed(() => props.post.height / props.post.width);
37+
38+
const imgHeight = computed(() => imgWidth.value * aspectRatio.value);
2139
</script>
2240

2341
<style scoped>
2442
img {
2543
display: block;
2644
}
45+
46+
a {
47+
display: inline-block; /* shrink-wrap the link */
48+
}
2749
</style>

components/PostPage.vue

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<template>
22
<div>
3-
<PostDetails :post="post" :is-own-post="isOwnPost" />
3+
<PostDetails :post="post" :is-detail="true" />
44
<PostActions
55
:post="post"
6-
:is-own-post="isOwnPost"
6+
:is-own-post="isOwnPost(post, user)"
7+
:is-editable="true"
78
:user="user"
89
@edited="refreshPost"
910
/>
@@ -17,14 +18,16 @@ import { MltshpFile } from '~/types/MltshpFile';
1718
1819
const emit = defineEmits(['refresh']);
1920
20-
const props = defineProps<{
21+
defineProps<{
2122
post: MltshpFile;
2223
}>();
2324
24-
// Is this a post by the user? If so, we'll hide the like button, etc.
25+
// Get the authenticated user
2526
const { data: authData } = useAuth();
2627
const user = authData.value?.user as AuthUser | undefined;
27-
const isOwnPost = computed(() => props.post.user.name === user?.name);
28+
29+
// Is this a post by the user? If so, we'll hide the like button, etc.
30+
const { isOwnPost } = usePost();
2831
2932
// Reload the post after editing
3033
const refreshPost = () => {

components/ShakeDetail.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<h1>{{ shake.name }}</h1>
44
<!-- eslint-disable-next-line vue/no-v-html -->
55
<div v-html="formattedDescription" />
6-
<img v-if="largeThumbnailUrl" :src="largeThumbnailUrl" alt="" />
6+
<img v-if="largeThumbnailUrl" :src="largeThumbnailUrl" alt="" width="142" />
77
<ul>
88
<li v-if="shake.created_at">
99
{{ shake.type === 'user' ? 'Member since' : 'Created' }}:
@@ -12,9 +12,9 @@
1212
<li v-if="shake.updated_at">Updated: {{ shake.updated_at }}</li>
1313
<li v-if="shake.owner">
1414
Owner:
15-
<nuxt-link :to="`/user/${shake.owner.name}`">
15+
<NuxtLink :to="`/user/${shake.owner.name}`">
1616
{{ shake.owner.name }}
17-
</nuxt-link>
17+
</NuxtLink>
1818
<img
1919
v-if="shake.owner.profile_image_url"
2020
:src="shake.owner.profile_image_url"

components/ShakeList.vue

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
<template>
22
<div>
33
<ol>
4-
<li v-for="file in files" :key="file.sharekey">
5-
<NuxtLink :to="`/post/${file.sharekey}`">
6-
{{ file.pivot_id }} - {{ file.title || file.name }}
7-
</NuxtLink>
8-
</li>
4+
<PostCard v-for="file in files" :key="file.sharekey" :post="file" />
95
</ol>
106
</div>
117
</template>

composables/usePost.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { AuthUser } from '~/types/AuthUser';
2+
import { MltshpFile } from '~/types/MltshpFile';
3+
4+
const usePost = () => {
5+
// Is this a post by the user? If so, we'll hide the like button, etc.
6+
const isOwnPost = (post: MltshpFile, user: AuthUser | undefined) =>
7+
post.user.name === user?.name;
8+
9+
return { isOwnPost };
10+
};
11+
12+
export default usePost;

server/api/oembed.get.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ export default defineEventHandler(async (event) => {
1313
let oembedUrl = null;
1414

1515
if (youtubeRegex.test(url)) {
16-
oembedUrl = `https://www.youtube.com/oembed?url=${encodedUrl}&format=json`;
16+
oembedUrl = `https://www.youtube.com/oembed?url=${encodedUrl}&maxwidth=550&format=json`;
1717
}
1818

1919
if (vimeoRegex.test(url)) {
20-
oembedUrl = `https://vimeo.com/api/oembed.json?url=${encodedUrl}`;
20+
oembedUrl = `https://vimeo.com/api/oembed.json?url=${encodedUrl}&maxwidth=550`;
2121
}
2222

2323
if (flickrRegex.test(url)) {
24-
oembedUrl = `https://www.flickr.com/services/oembed?url=${encodedUrl}&format=json`;
24+
oembedUrl = `https://www.flickr.com/services/oembed?url=${encodedUrl}&maxwidth=550&format=json`;
2525
}
2626

2727
if (!oembedUrl)

0 commit comments

Comments
 (0)