Frontend: Removed the gallery feature
This commit is contained in:
parent
d16f9d704d
commit
364074aecc
|
@ -14,7 +14,6 @@ import {
|
|||
FollowingFolloweePopulated,
|
||||
FollowingFollowerPopulated,
|
||||
FollowRequest,
|
||||
GalleryPost,
|
||||
Instance,
|
||||
LiteInstanceMetadata,
|
||||
MeDetailed,
|
||||
|
@ -563,25 +562,6 @@ export type Endpoints = {
|
|||
"following/requests/list": { req: NoParams; res: FollowRequest[] };
|
||||
"following/requests/reject": { req: { userId: User["id"] }; res: null };
|
||||
|
||||
// gallery
|
||||
"gallery/featured": { req: TODO; res: TODO };
|
||||
"gallery/popular": { req: TODO; res: TODO };
|
||||
"gallery/posts": { req: TODO; res: TODO };
|
||||
"gallery/posts/create": { req: TODO; res: TODO };
|
||||
"gallery/posts/delete": { req: { postId: GalleryPost["id"] }; res: null };
|
||||
"gallery/posts/like": { req: TODO; res: TODO };
|
||||
"gallery/posts/show": { req: TODO; res: TODO };
|
||||
"gallery/posts/unlike": { req: TODO; res: TODO };
|
||||
"gallery/posts/update": { req: TODO; res: TODO };
|
||||
|
||||
// games
|
||||
"games/reversi/games": { req: TODO; res: TODO };
|
||||
"games/reversi/games/show": { req: TODO; res: TODO };
|
||||
"games/reversi/games/surrender": { req: TODO; res: TODO };
|
||||
"games/reversi/invitations": { req: TODO; res: TODO };
|
||||
"games/reversi/match": { req: TODO; res: TODO };
|
||||
"games/reversi/match/cancel": { req: TODO; res: TODO };
|
||||
|
||||
// get-online-users-count
|
||||
"get-online-users-count": { req: NoParams; res: { count: number } };
|
||||
|
||||
|
@ -611,8 +591,6 @@ export type Endpoints = {
|
|||
};
|
||||
res: NoteFavorite[];
|
||||
};
|
||||
"i/gallery/likes": { req: TODO; res: TODO };
|
||||
"i/gallery/posts": { req: TODO; res: TODO };
|
||||
"i/get-word-muted-notes-count": { req: TODO; res: TODO };
|
||||
"i/import-following": { req: TODO; res: TODO };
|
||||
"i/import-user-lists": { req: TODO; res: TODO };
|
||||
|
@ -950,7 +928,6 @@ export type Endpoints = {
|
|||
};
|
||||
res: FollowingFolloweePopulated[];
|
||||
};
|
||||
"users/gallery/posts": { req: TODO; res: TODO };
|
||||
"users/get-frequently-replied-users": { req: TODO; res: TODO };
|
||||
"users/lists/create": { req: { name: string }; res: UserList };
|
||||
"users/lists/delete": { req: { listId: UserList["id"] }; res: null };
|
||||
|
|
|
@ -44,12 +44,4 @@ export const permissions = [
|
|||
"read:reactions",
|
||||
"write:reactions",
|
||||
"write:votes",
|
||||
"read:pages",
|
||||
"write:pages",
|
||||
"write:page-likes",
|
||||
"read:page-likes",
|
||||
"read:gallery",
|
||||
"write:gallery",
|
||||
"read:gallery-likes",
|
||||
"write:gallery-likes",
|
||||
];
|
||||
|
|
|
@ -121,8 +121,6 @@ export type DriveFile = {
|
|||
|
||||
export type DriveFolder = TODO;
|
||||
|
||||
export type GalleryPost = TODO;
|
||||
|
||||
export type Note = {
|
||||
id: ID;
|
||||
createdAt: DateString;
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
<template>
|
||||
<MkA :to="`/gallery/${post.id}`" class="ttasepnz _panel">
|
||||
<div class="thumbnail">
|
||||
<ImgWithBlurhash
|
||||
class="img"
|
||||
:src="post.files[0].thumbnailUrl"
|
||||
:hash="post.files[0].blurhash"
|
||||
/>
|
||||
</div>
|
||||
<article>
|
||||
<header>
|
||||
<MagAvatarResolvingProxy :user="post.user" class="avatar" />
|
||||
</header>
|
||||
<footer>
|
||||
<span class="title">{{ post.title }}</span>
|
||||
</footer>
|
||||
</article>
|
||||
</MkA>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ImgWithBlurhash from "@/components/MkImgWithBlurhash.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
post: any;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ttasepnz {
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 200px;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
color: var(--accent);
|
||||
|
||||
> .thumbnail {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
> article {
|
||||
> footer {
|
||||
&:before {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .thumbnail {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
transition: all 0.5s ease;
|
||||
|
||||
> .img {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
> article {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
> header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
|
||||
> .avatar {
|
||||
margin-left: auto;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
> footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
text-shadow: 0 0 8px var(--shadow);
|
||||
background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(rgba(0, 0, 0, 0.4), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
> .title {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -49,7 +49,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { onMounted } from "vue";
|
||||
import * as misskey from "calckey-js";
|
||||
import PhotoSwipeLightbox from "photoswipe/lightbox";
|
||||
import PhotoSwipe from "photoswipe";
|
||||
|
@ -68,7 +68,6 @@ const props = defineProps<{
|
|||
inDm?: boolean;
|
||||
}>();
|
||||
|
||||
const gallery = ref(null);
|
||||
const pswpZIndex = os.claimZIndex("middle");
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -104,7 +103,6 @@ onMounted(() => {
|
|||
}
|
||||
return item;
|
||||
}),
|
||||
gallery: gallery.value,
|
||||
children: ".image",
|
||||
thumbSelector: ".image",
|
||||
loop: false,
|
||||
|
|
|
@ -63,11 +63,6 @@ export const navbarItemDef = reactive({
|
|||
show: computed(() => $i != null),
|
||||
to: "/my/favorites",
|
||||
},
|
||||
gallery: {
|
||||
title: "gallery",
|
||||
icon: "ph-image-square ph-bold ph-lg",
|
||||
to: "/gallery",
|
||||
},
|
||||
clips: {
|
||||
title: "clips",
|
||||
icon: "ph-paperclip ph-bold ph-lg",
|
||||
|
|
|
@ -1,195 +0,0 @@
|
|||
<template>
|
||||
<MkStickyContainer>
|
||||
<template #header
|
||||
><MkPageHeader :actions="headerActions" :tabs="headerTabs"
|
||||
/></template>
|
||||
<MkSpacer :content-max="800" :margin-min="16" :margin-max="32">
|
||||
<FormSuspense :p="init">
|
||||
<FormInput v-model="title">
|
||||
<template #label>{{ i18n.ts.title }}</template>
|
||||
</FormInput>
|
||||
|
||||
<FormTextarea v-model="description" :max="500">
|
||||
<template #label>{{ i18n.ts.description }}</template>
|
||||
</FormTextarea>
|
||||
|
||||
<div class="">
|
||||
<div
|
||||
v-for="file in files"
|
||||
:key="file.id"
|
||||
class="wqugxsfx"
|
||||
:style="{
|
||||
backgroundImage: file
|
||||
? `url(${file.thumbnailUrl})`
|
||||
: null,
|
||||
}"
|
||||
>
|
||||
<div class="name">{{ file.name }}</div>
|
||||
<button
|
||||
v-tooltip="i18n.ts.remove"
|
||||
class="remove _button"
|
||||
@click="remove(file)"
|
||||
>
|
||||
<i class="ph-x ph-bold ph-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
<FormButton primary @click="selectFile"
|
||||
><i class="ph-plus ph-bold ph-lg"></i>
|
||||
{{ i18n.ts.attachFile }}</FormButton
|
||||
>
|
||||
</div>
|
||||
|
||||
<FormSwitch v-model="isSensitive">{{
|
||||
i18n.ts.markAsSensitive
|
||||
}}</FormSwitch>
|
||||
|
||||
<FormButton v-if="postId" primary @click="save"
|
||||
><i class="ph-floppy-disk-back ph-bold ph-lg"></i>
|
||||
{{ i18n.ts.save }}</FormButton
|
||||
>
|
||||
<FormButton v-else primary @click="save"
|
||||
><i class="ph-floppy-disk-back ph-bold ph-lg"></i>
|
||||
{{ i18n.ts.publish }}</FormButton
|
||||
>
|
||||
|
||||
<FormButton v-if="postId" danger @click="del"
|
||||
><i class="ph-trash ph-bold ph-lg"></i>
|
||||
{{ i18n.ts.delete }}</FormButton
|
||||
>
|
||||
</FormSuspense>
|
||||
</MkSpacer>
|
||||
</MkStickyContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, watch } from "vue";
|
||||
import FormButton from "@/components/MkButton.vue";
|
||||
import FormInput from "@/components/form/input.vue";
|
||||
import FormTextarea from "@/components/form/textarea.vue";
|
||||
import FormSwitch from "@/components/form/switch.vue";
|
||||
import FormSuspense from "@/components/form/suspense.vue";
|
||||
import { selectFiles } from "@/scripts/select-file";
|
||||
import * as os from "@/os";
|
||||
import { useRouter } from "@/router";
|
||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||
import { i18n } from "@/i18n";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps<{
|
||||
postId?: string;
|
||||
}>();
|
||||
|
||||
let init = $ref(null);
|
||||
let files = $ref([]);
|
||||
let description = $ref(null);
|
||||
let title = $ref(null);
|
||||
let isSensitive = $ref(false);
|
||||
|
||||
function selectFile(evt) {
|
||||
selectFiles(evt.currentTarget ?? evt.target, null).then((selected) => {
|
||||
files = files.concat(selected);
|
||||
});
|
||||
}
|
||||
|
||||
function remove(file) {
|
||||
files = files.filter((f) => f.id !== file.id);
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (props.postId) {
|
||||
await os.apiWithDialog("gallery/posts/update", {
|
||||
postId: props.postId,
|
||||
title: title,
|
||||
description: description,
|
||||
fileIds: files.map((file) => file.id),
|
||||
isSensitive: isSensitive,
|
||||
});
|
||||
router.push(`/gallery/${props.postId}`);
|
||||
} else {
|
||||
const created = await os.apiWithDialog("gallery/posts/create", {
|
||||
title: title,
|
||||
description: description,
|
||||
fileIds: files.map((file) => file.id),
|
||||
isSensitive: isSensitive,
|
||||
});
|
||||
router.push(`/gallery/${created.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function del() {
|
||||
const { canceled } = await os.confirm({
|
||||
type: "warning",
|
||||
text: i18n.ts.deleteConfirm,
|
||||
});
|
||||
if (canceled) return;
|
||||
await os.apiWithDialog("gallery/posts/delete", {
|
||||
postId: props.postId,
|
||||
});
|
||||
router.push("/gallery");
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.postId,
|
||||
() => {
|
||||
init = () =>
|
||||
props.postId
|
||||
? os
|
||||
.api("gallery/posts/show", {
|
||||
postId: props.postId,
|
||||
})
|
||||
.then((post) => {
|
||||
files = post.files;
|
||||
title = post.title;
|
||||
description = post.description;
|
||||
isSensitive = post.isSensitive;
|
||||
})
|
||||
: Promise.resolve(null);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const headerActions = $computed(() => []);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
|
||||
definePageMetadata(
|
||||
computed(() =>
|
||||
props.postId
|
||||
? {
|
||||
title: i18n.ts.edit,
|
||||
icon: "ph-pencil ph-bold ph-lg",
|
||||
}
|
||||
: {
|
||||
title: i18n.ts.postToGallery,
|
||||
icon: "ph-pencil ph-bold ph-lg",
|
||||
}
|
||||
)
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wqugxsfx {
|
||||
height: 200px;
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
position: relative;
|
||||
|
||||
> .name {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 9px;
|
||||
padding: 8px;
|
||||
background: var(--panel);
|
||||
}
|
||||
|
||||
> .remove {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 9px;
|
||||
padding: 8px;
|
||||
background: var(--panel);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,223 +0,0 @@
|
|||
<template>
|
||||
<MkStickyContainer>
|
||||
<template #header
|
||||
><MkPageHeader
|
||||
v-model:tab="tab"
|
||||
:actions="headerActions"
|
||||
:tabs="headerTabs"
|
||||
:display-back-button="true"
|
||||
/></template>
|
||||
<MkSpacer :content-max="1200">
|
||||
<swiper
|
||||
:round-lengths="true"
|
||||
:touch-angle="25"
|
||||
:threshold="10"
|
||||
:centeredSlides="true"
|
||||
:modules="[Virtual]"
|
||||
:space-between="20"
|
||||
:virtual="true"
|
||||
:allow-touch-move="
|
||||
!(
|
||||
deviceKind === 'desktop' &&
|
||||
!defaultStore.state.swipeOnDesktop
|
||||
)
|
||||
"
|
||||
@swiper="setSwiperRef"
|
||||
@slide-change="onSlideChange"
|
||||
>
|
||||
<swiper-slide>
|
||||
<MkFolder class="_gap">
|
||||
<template #header
|
||||
><i class="ph-clock ph-bold ph-lg"></i>
|
||||
{{ i18n.ts.recentPosts }}</template
|
||||
>
|
||||
<MkPagination
|
||||
v-slot="{ items }"
|
||||
:pagination="recentPostsPagination"
|
||||
:disable-auto-load="true"
|
||||
>
|
||||
<div class="vfpdbgtk">
|
||||
<MkGalleryPostPreview
|
||||
v-for="post in items"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
class="post"
|
||||
/>
|
||||
</div>
|
||||
</MkPagination>
|
||||
</MkFolder>
|
||||
<MkFolder class="_gap">
|
||||
<template #header
|
||||
><i class="ph-fire-simple ph-bold ph-lg"></i>
|
||||
{{ i18n.ts.popularPosts }}</template
|
||||
>
|
||||
<MkPagination
|
||||
v-slot="{ items }"
|
||||
:pagination="popularPostsPagination"
|
||||
:disable-auto-load="true"
|
||||
>
|
||||
<div class="vfpdbgtk">
|
||||
<MkGalleryPostPreview
|
||||
v-for="post in items"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
class="post"
|
||||
/>
|
||||
</div>
|
||||
</MkPagination>
|
||||
</MkFolder>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<MkPagination
|
||||
v-slot="{ items }"
|
||||
:pagination="likedPostsPagination"
|
||||
>
|
||||
<div class="vfpdbgtk">
|
||||
<MkGalleryPostPreview
|
||||
v-for="like in items"
|
||||
:key="like.id"
|
||||
:post="like.post"
|
||||
class="post"
|
||||
/>
|
||||
</div>
|
||||
</MkPagination>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<MkA to="/gallery/new" class="_link" style="margin: 16px"
|
||||
><i class="ph-plus ph-bold ph-lg"></i>
|
||||
{{ i18n.ts.postToGallery }}</MkA
|
||||
>
|
||||
<MkPagination
|
||||
v-slot="{ items }"
|
||||
:pagination="myPostsPagination"
|
||||
>
|
||||
<div class="vfpdbgtk">
|
||||
<MkGalleryPostPreview
|
||||
v-for="post in items"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
class="post"
|
||||
/>
|
||||
</div>
|
||||
</MkPagination>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</MkSpacer>
|
||||
</MkStickyContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, defineComponent, watch, onMounted } from "vue";
|
||||
import { Virtual } from "swiper";
|
||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||
import MkFolder from "@/components/MkFolder.vue";
|
||||
import MkPagination from "@/components/MkPagination.vue";
|
||||
import MkGalleryPostPreview from "@/components/MkGalleryPostPreview.vue";
|
||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||
import { deviceKind } from "@/scripts/device-kind";
|
||||
import { i18n } from "@/i18n";
|
||||
import { useRouter } from "@/router";
|
||||
import { defaultStore } from "@/store";
|
||||
import "swiper/scss";
|
||||
import "swiper/scss/virtual";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps<{
|
||||
tag?: string;
|
||||
}>();
|
||||
|
||||
const tabs = ["explore", "liked", "my"];
|
||||
let tab = $ref(tabs[0]);
|
||||
watch($$(tab), () => syncSlide(tabs.indexOf(tab)));
|
||||
|
||||
let tagsRef = $ref();
|
||||
|
||||
const recentPostsPagination = {
|
||||
endpoint: "gallery/posts" as const,
|
||||
limit: 6,
|
||||
};
|
||||
const popularPostsPagination = {
|
||||
endpoint: "gallery/featured" as const,
|
||||
limit: 5,
|
||||
};
|
||||
const myPostsPagination = {
|
||||
endpoint: "i/gallery/posts" as const,
|
||||
limit: 5,
|
||||
};
|
||||
const likedPostsPagination = {
|
||||
endpoint: "i/gallery/likes" as const,
|
||||
limit: 5,
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.tag,
|
||||
() => {
|
||||
if (tagsRef) tagsRef.tags.toggleContent(props.tag == null);
|
||||
}
|
||||
);
|
||||
|
||||
const headerActions = $computed(() => [
|
||||
{
|
||||
icon: "ph-plus ph-bold ph-lg",
|
||||
text: i18n.ts.create,
|
||||
handler: () => {
|
||||
router.push("/gallery/new");
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const headerTabs = $computed(() => [
|
||||
{
|
||||
key: "explore",
|
||||
title: i18n.ts.gallery,
|
||||
icon: "ph-image-square ph-bold ph-lg",
|
||||
},
|
||||
{
|
||||
key: "liked",
|
||||
title: i18n.ts._gallery.liked,
|
||||
icon: "ph-heart ph-bold ph-lg",
|
||||
},
|
||||
{
|
||||
key: "my",
|
||||
title: i18n.ts._gallery.my,
|
||||
icon: "ph-crown-simple ph-bold ph-lg",
|
||||
},
|
||||
]);
|
||||
|
||||
definePageMetadata({
|
||||
title: i18n.ts.gallery,
|
||||
icon: "ph-image-square ph-bold ph-lg",
|
||||
});
|
||||
|
||||
let swiperRef = null;
|
||||
|
||||
function setSwiperRef(swiper) {
|
||||
swiperRef = swiper;
|
||||
syncSlide(tabs.indexOf(tab));
|
||||
}
|
||||
|
||||
function onSlideChange() {
|
||||
tab = tabs[swiperRef.activeIndex];
|
||||
}
|
||||
|
||||
function syncSlide(index) {
|
||||
swiperRef.slideTo(index);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
syncSlide(tabs.indexOf(swiperRef.activeIndex));
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.vfpdbgtk {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
grid-gap: 12px;
|
||||
margin: 0 var(--margin);
|
||||
|
||||
> .post {
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,360 +0,0 @@
|
|||
<template>
|
||||
<MkStickyContainer>
|
||||
<template #header
|
||||
><MkPageHeader :actions="headerActions" :tabs="headerTabs"
|
||||
/></template>
|
||||
<MkSpacer :content-max="1000" :margin-min="16" :margin-max="32">
|
||||
<div class="_root">
|
||||
<transition
|
||||
:name="$store.state.animation ? 'fade' : ''"
|
||||
mode="out-in"
|
||||
>
|
||||
<div v-if="post" class="rkxwuolj">
|
||||
<div class="files">
|
||||
<div
|
||||
v-for="file in post.files"
|
||||
:key="file.id"
|
||||
class="file"
|
||||
>
|
||||
<img :src="file.url" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="body _block">
|
||||
<div class="title">{{ post.title }}</div>
|
||||
<div class="description">
|
||||
<Mfm :text="post.description" />
|
||||
</div>
|
||||
<div class="info">
|
||||
<i class="ph-clock ph-bold ph-lg"></i>
|
||||
<MkTime :time="post.createdAt" mode="detail" />
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="like">
|
||||
<MkButton
|
||||
v-if="post.isLiked"
|
||||
v-tooltip="i18n.ts._gallery.unlike"
|
||||
class="button"
|
||||
primary
|
||||
@click="unlike()"
|
||||
><i class="ph-heart ph-fill ph-lg"></i
|
||||
><span
|
||||
v-if="post.likedCount > 0"
|
||||
class="count"
|
||||
>{{ post.likedCount }}</span
|
||||
></MkButton
|
||||
>
|
||||
<MkButton
|
||||
v-else
|
||||
v-tooltip="i18n.ts._gallery.like"
|
||||
class="button"
|
||||
@click="like()"
|
||||
><i class="ph-heart ph-bold"></i
|
||||
><span
|
||||
v-if="post.likedCount > 0"
|
||||
class="count"
|
||||
>{{ post.likedCount }}</span
|
||||
></MkButton
|
||||
>
|
||||
</div>
|
||||
<div class="other">
|
||||
<button
|
||||
v-if="$i && $i.id === post.user.id"
|
||||
v-tooltip="i18n.ts.edit"
|
||||
class="_button"
|
||||
@click="edit"
|
||||
>
|
||||
<i
|
||||
class="ph-pencil ph-bold ph-lg ph-fw ph-lg"
|
||||
></i>
|
||||
</button>
|
||||
<button
|
||||
v-tooltip="i18n.ts.shareWithNote"
|
||||
class="_button"
|
||||
@click="shareWithNote"
|
||||
>
|
||||
<i
|
||||
class="ph-repeat ph-bold ph-lg ph-fw ph-lg"
|
||||
></i>
|
||||
</button>
|
||||
<button
|
||||
v-if="shareAvailable()"
|
||||
v-tooltip="i18n.ts.share"
|
||||
class="_button"
|
||||
@click="share"
|
||||
>
|
||||
<i
|
||||
class="ph-share-network ph-bold ph-lg ph-fw ph-lg"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user">
|
||||
<MagAvatarResolvingProxy
|
||||
:user="post.user"
|
||||
class="avatar"
|
||||
/>
|
||||
<div class="name">
|
||||
<MkUserName
|
||||
:user="post.user"
|
||||
style="display: block"
|
||||
/>
|
||||
<MkAcct :user="post.user" />
|
||||
</div>
|
||||
<MkFollowButton
|
||||
v-if="!$i || $i.id != post.user.id"
|
||||
:user="post.user"
|
||||
:inline="true"
|
||||
:transparent="false"
|
||||
:full="true"
|
||||
large
|
||||
class="koudoku"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<MkContainer
|
||||
:max-height="300"
|
||||
:foldable="true"
|
||||
class="other"
|
||||
>
|
||||
<template #header
|
||||
><i class="ph-clock ph-bold ph-lg"></i>
|
||||
{{ i18n.ts.recentPosts }}</template
|
||||
>
|
||||
<MkPagination
|
||||
v-slot="{ items }"
|
||||
:pagination="otherPostsPagination"
|
||||
>
|
||||
<div class="sdrarzaf">
|
||||
<MkGalleryPostPreview
|
||||
v-for="post in items"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
class="post"
|
||||
/>
|
||||
</div>
|
||||
</MkPagination>
|
||||
</MkContainer>
|
||||
</div>
|
||||
<MkError v-else-if="error" @retry="fetch()" />
|
||||
<MkLoading v-else />
|
||||
</transition>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</MkStickyContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, watch } from "vue";
|
||||
import MkButton from "@/components/MkButton.vue";
|
||||
import * as os from "@/os";
|
||||
import MkContainer from "@/components/MkContainer.vue";
|
||||
import MkPagination from "@/components/MkPagination.vue";
|
||||
import MkGalleryPostPreview from "@/components/MkGalleryPostPreview.vue";
|
||||
import MkFollowButton from "@/components/MkFollowButton.vue";
|
||||
import { url } from "@/config";
|
||||
import { useRouter } from "@/router";
|
||||
import { i18n } from "@/i18n";
|
||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||
import { shareAvailable } from "@/scripts/share-available";
|
||||
import { $i } from "@/account";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps<{
|
||||
postId: string;
|
||||
}>();
|
||||
|
||||
let post = $ref(null);
|
||||
let error = $ref(null);
|
||||
const otherPostsPagination = {
|
||||
endpoint: "users/gallery/posts" as const,
|
||||
limit: 6,
|
||||
params: computed(() => ({
|
||||
userId: post.user.id,
|
||||
})),
|
||||
};
|
||||
|
||||
function fetchPost() {
|
||||
post = null;
|
||||
os.api("gallery/posts/show", {
|
||||
postId: props.postId,
|
||||
})
|
||||
.then((_post) => {
|
||||
post = _post;
|
||||
})
|
||||
.catch((_error) => {
|
||||
error = _error;
|
||||
});
|
||||
}
|
||||
|
||||
function share() {
|
||||
navigator.share({
|
||||
title: post.title,
|
||||
text: post.description,
|
||||
url: `${url}/gallery/${post.id}`,
|
||||
});
|
||||
}
|
||||
|
||||
function shareWithNote() {
|
||||
os.post({
|
||||
initialText: `${post.title} ${url}/gallery/${post.id}`,
|
||||
});
|
||||
}
|
||||
|
||||
function like() {
|
||||
os.api("gallery/posts/like", {
|
||||
postId: props.postId,
|
||||
}).then(() => {
|
||||
post.isLiked = true;
|
||||
post.likedCount++;
|
||||
});
|
||||
}
|
||||
|
||||
async function unlike() {
|
||||
os.api("gallery/posts/unlike", {
|
||||
postId: props.postId,
|
||||
}).then(() => {
|
||||
post.isLiked = false;
|
||||
post.likedCount--;
|
||||
});
|
||||
}
|
||||
|
||||
function edit() {
|
||||
router.push(`/gallery/${post.id}/edit`);
|
||||
}
|
||||
|
||||
watch(() => props.postId, fetchPost, { immediate: true });
|
||||
|
||||
const headerActions = $computed(() => [
|
||||
{
|
||||
icon: "ph-pencil ph-bold ph-lg",
|
||||
text: i18n.ts.edit,
|
||||
handler: edit,
|
||||
},
|
||||
]);
|
||||
|
||||
const headerTabs = $computed(() => []);
|
||||
|
||||
definePageMetadata(
|
||||
computed(() =>
|
||||
post
|
||||
? {
|
||||
title: post.title,
|
||||
avatar: post.user,
|
||||
}
|
||||
: null
|
||||
)
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.125s ease;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.rkxwuolj {
|
||||
> .files {
|
||||
> .file {
|
||||
> img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
max-height: 500px;
|
||||
margin: 0 auto;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
& + .file {
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .body {
|
||||
padding: 32px;
|
||||
|
||||
> .title {
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
> .info {
|
||||
margin-top: 16px;
|
||||
font-size: 90%;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
> .actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 16px;
|
||||
padding: 16px 0 0 0;
|
||||
border-top: solid 0.5px var(--divider);
|
||||
|
||||
> .like {
|
||||
> .button {
|
||||
--accent: #eb6f92;
|
||||
--X8: #eb6f92;
|
||||
--buttonBg: rgb(216 71 106 / 5%);
|
||||
--buttonHoverBg: rgb(216 71 106 / 10%);
|
||||
color: #eb6f92;
|
||||
|
||||
::v-deep(.count) {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .other {
|
||||
margin-left: auto;
|
||||
|
||||
> button {
|
||||
padding: 8px;
|
||||
margin: 0 8px;
|
||||
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .user {
|
||||
margin-top: 16px;
|
||||
padding: 16px 0 0 0;
|
||||
border-top: solid 0.5px var(--divider);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> .avatar {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
> .name {
|
||||
margin: 0 0 0 12px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
> .koudoku {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sdrarzaf {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
grid-gap: 12px;
|
||||
margin: var(--margin);
|
||||
|
||||
> .post {
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,45 +0,0 @@
|
|||
<template>
|
||||
<MkSpacer :content-max="800">
|
||||
<MkPagination v-slot="{ items }" :pagination="pagination">
|
||||
<div class="jrnovfpt">
|
||||
<MkGalleryPostPreview
|
||||
v-for="post in items"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
class="post"
|
||||
/>
|
||||
</div>
|
||||
</MkPagination>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import MkGalleryPostPreview from "@/components/MkGalleryPostPreview.vue";
|
||||
import MkPagination from "@/components/MkPagination.vue";
|
||||
import { packed } from "magnetar-common";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
user: packed.PackUserBase;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const pagination = {
|
||||
endpoint: "users/gallery/posts" as const,
|
||||
limit: 6,
|
||||
params: computed(() => ({
|
||||
userId: props.user.id,
|
||||
})),
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.jrnovfpt {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
grid-gap: 12px;
|
||||
margin: var(--margin);
|
||||
}
|
||||
</style>
|
|
@ -18,7 +18,6 @@
|
|||
/>
|
||||
<XReactions v-else-if="tab === 'reactions'" :user="user" />
|
||||
<XClips v-else-if="tab === 'clips'" :user="user" />
|
||||
<XGallery v-else-if="tab === 'gallery'" :user="user" />
|
||||
</div>
|
||||
<MkError v-else-if="error" @retry="fetchUser()" />
|
||||
<MkLoading v-else />
|
||||
|
@ -41,7 +40,6 @@ import * as Acct from "calckey-js/built/acct";
|
|||
const XHome = defineAsyncComponent(() => import("./home.vue"));
|
||||
const XReactions = defineAsyncComponent(() => import("./reactions.vue"));
|
||||
const XClips = defineAsyncComponent(() => import("./clips.vue"));
|
||||
const XGallery = defineAsyncComponent(() => import("./gallery.vue"));
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
@ -142,11 +140,6 @@ const headerTabs = $computed(() =>
|
|||
title: i18n.ts.clips,
|
||||
icon: "ph-paperclip ph-bold ph-lg",
|
||||
},
|
||||
{
|
||||
key: "gallery",
|
||||
title: i18n.ts.gallery,
|
||||
icon: "ph-image-square ph-bold ph-lg",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
|
|
|
@ -364,24 +364,6 @@ export const routes = [
|
|||
path: "/tags/:tag",
|
||||
component: page(() => import("./pages/tag.vue")),
|
||||
},
|
||||
{
|
||||
path: "/gallery/:postId/edit",
|
||||
component: page(() => import("./pages/gallery/edit.vue")),
|
||||
loginRequired: true,
|
||||
},
|
||||
{
|
||||
path: "/gallery/new",
|
||||
component: page(() => import("./pages/gallery/edit.vue")),
|
||||
loginRequired: true,
|
||||
},
|
||||
{
|
||||
path: "/gallery/:postId",
|
||||
component: page(() => import("./pages/gallery/post.vue")),
|
||||
},
|
||||
{
|
||||
path: "/gallery",
|
||||
component: page(() => import("./pages/gallery/index.vue")),
|
||||
},
|
||||
{
|
||||
path: "/registry/keys/system/:path(*)?",
|
||||
component: page(() => import("./pages/registry.keys.vue")),
|
||||
|
|
|
@ -47,10 +47,6 @@
|
|||
><i class="ph-compass ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.explore }}</MkA
|
||||
>
|
||||
<MkA to="/gallery" class="link" active-class="active"
|
||||
><i class="ph-image-square ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.gallery }}</MkA
|
||||
>
|
||||
<div class="action">
|
||||
<button class="_buttonPrimary" @click="signup()">
|
||||
{{ i18n.ts.signup }}
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
><i class="ph-compass ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.explore }}</MkA
|
||||
>
|
||||
<MkA to="/gallery" class="link" active-class="active"
|
||||
><i class="ph-image-square ph-bold ph-lg icon"></i
|
||||
>{{ i18n.ts.gallery }}</MkA
|
||||
>
|
||||
<div v-if="info" class="page active link">
|
||||
<div class="title">
|
||||
<i v-if="info.icon" class="icon" :class="info.icon"></i>
|
||||
|
|
Loading…
Reference in New Issue