From 48163872eddf803e36aea5221e33c29ba9d4fb51 Mon Sep 17 00:00:00 2001
From: Johann150 <johann.galle@protonmail.com>
Date: Mon, 16 Jan 2023 18:30:45 +0100
Subject: [PATCH 1/4] client: remove galleries

---
 locales/en-US.yml                             |  10 -
 .../src/components/gallery-post-preview.vue   | 113 --------
 packages/client/src/menu.ts                   |   5 -
 packages/client/src/pages/gallery/edit.vue    | 153 ----------
 packages/client/src/pages/gallery/index.vue   | 137 ---------
 packages/client/src/pages/gallery/post.vue    | 264 ------------------
 packages/client/src/pages/user/gallery.vue    |  38 ---
 packages/client/src/pages/user/index.vue      |   6 -
 packages/client/src/router.ts                 |  14 -
 9 files changed, 740 deletions(-)
 delete mode 100644 packages/client/src/components/gallery-post-preview.vue
 delete mode 100644 packages/client/src/pages/gallery/edit.vue
 delete mode 100644 packages/client/src/pages/gallery/index.vue
 delete mode 100644 packages/client/src/pages/gallery/post.vue
 delete mode 100644 packages/client/src/pages/user/gallery.vue

diff --git a/locales/en-US.yml b/locales/en-US.yml
index 45d6af9bf..9999a5115 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -679,7 +679,6 @@ editCode: "Edit code"
 apply: "Apply"
 receiveAnnouncementFromInstance: "Receive notifications from this instance"
 emailNotification: "Email notifications"
-publish: "Publish"
 useReactionPickerForContextMenu: "Open reaction picker on right-click"
 typingUsers: "{users} is/are typing..."
 jumpToSpecifiedDate: "Jump to specific date"
@@ -720,11 +719,7 @@ switch: "Switch"
 noMaintainerInformationWarning: "Maintainer information is not configured."
 noBotProtectionWarning: "Bot protection is not configured."
 configure: "Configure"
-postToGallery: "Create new gallery post"
-attachmentRequired: "At least 1 attachment is required."
-gallery: "Gallery"
 recentPosts: "Recent posts"
-popularPosts: "Popular posts"
 shareWithNote: "Share with note"
 emailNotConfiguredWarning: "Email address not set."
 ratio: "Ratio"
@@ -863,11 +858,6 @@ _forgotPassword:
     \ instance administrator instead."
   contactAdmin: "This instance does not support using email addresses, please contact\
     \ the instance administrator to reset your password instead."
-_gallery:
-  my: "My Gallery"
-  liked: "Liked Posts"
-  like: "Like"
-  unlike: "Remove like"
 _email:
   _follow:
     title: "You've got a new follower"
diff --git a/packages/client/src/components/gallery-post-preview.vue b/packages/client/src/components/gallery-post-preview.vue
deleted file mode 100644
index 6acd9646b..000000000
--- a/packages/client/src/components/gallery-post-preview.vue
+++ /dev/null
@@ -1,113 +0,0 @@
-<template>
-<MkA :to="`/gallery/${post.id}`" class="ttasepnz _panel" tabindex="-1">
-	<div class="thumbnail">
-		<ImgWithBlurhash class="img" :src="post.files[0].thumbnailUrl" :hash="post.files[0].blurhash"/>
-	</div>
-	<article>
-		<header>
-			<MkAvatar :user="post.user" class="avatar"/>
-		</header>
-		<footer>
-			<span class="title">{{ post.title }}</span>
-		</footer>
-	</article>
-</MkA>
-</template>
-
-<script lang="ts" setup>
-import * as foundkey from 'foundkey-js';
-import ImgWithBlurhash from '@/components/img-with-blurhash.vue';
-
-defineProps<{
-	post: foundkey.entities.GalleryPost;
-}>();
-</script>
-
-<style lang="scss" scoped>
-.ttasepnz {
-	display: block;
-	position: relative;
-	height: 200px;
-
-	&:hover {
-		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 {
-			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 #000;
-			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>
diff --git a/packages/client/src/menu.ts b/packages/client/src/menu.ts
index b730fdd8e..1de63b4dc 100644
--- a/packages/client/src/menu.ts
+++ b/packages/client/src/menu.ts
@@ -131,11 +131,6 @@ export const menuDef = reactive({
 		icon: 'fas fa-file-alt',
 		to: '/pages',
 	},
-	gallery: {
-		title: 'gallery',
-		icon: 'fas fa-icons',
-		to: '/gallery',
-	},
 	clips: {
 		title: 'clip',
 		icon: 'fas fa-paperclip',
diff --git a/packages/client/src/pages/gallery/edit.vue b/packages/client/src/pages/gallery/edit.vue
deleted file mode 100644
index 964f6d4ff..000000000
--- a/packages/client/src/pages/gallery/edit.vue
+++ /dev/null
@@ -1,153 +0,0 @@
-<template>
-<MkStickyContainer>
-	<template #header><MkPageHeader/></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="fas fa-times"></i></button>
-				</div>
-				<MkButton primary @click="selectFile"><i class="fas fa-plus"></i> {{ i18n.ts.attachFile }}</MkButton>
-			</div>
-
-			<FormSwitch v-model="isSensitive">{{ i18n.ts.markAsSensitive }}</FormSwitch>
-
-			<MkButton v-if="postId" primary @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
-			<MkButton v-else primary @click="save"><i class="fas fa-save"></i> {{ i18n.ts.publish }}</MkButton>
-
-			<MkButton v-if="postId" danger @click="del"><i class="fas fa-trash-alt"></i> {{ i18n.ts.delete }}</MkButton>
-		</FormSuspense>
-	</MkSpacer>
-</MkStickyContainer>
-</template>
-
-<script lang="ts" setup>
-import { computed, inject, watch } from 'vue';
-import MkButton from '@/components/ui/button.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 (files.length === 0) {
-		os.alert({
-			type: 'error',
-			text: i18n.ts.attachmentRequired,
-		});
-		return;
-	}
-
-	if (props.postId) {
-		await os.apiWithDialog('gallery/posts/update', {
-			postId: props.postId,
-			title,
-			description,
-			fileIds: files.map(file => file.id),
-			isSensitive,
-		});
-		router.push(`/gallery/${props.postId}`);
-	} else {
-		const created = await os.apiWithDialog('gallery/posts/create', {
-			title,
-			description,
-			fileIds: files.map(file => file.id),
-			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 });
-
-definePageMetadata(computed(() => props.postId ? {
-	title: i18n.ts.edit,
-	icon: 'fas fa-pencil-alt',
-} : {
-	title: i18n.ts.postToGallery,
-	icon: 'fas fa-pencil-alt',
-}));
-</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>
diff --git a/packages/client/src/pages/gallery/index.vue b/packages/client/src/pages/gallery/index.vue
deleted file mode 100644
index 2e2a72897..000000000
--- a/packages/client/src/pages/gallery/index.vue
+++ /dev/null
@@ -1,137 +0,0 @@
-<template>
-<MkStickyContainer>
-	<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
-	<MkSpacer :content-max="1400">
-		<div class="_root">
-			<div v-if="tab === 'explore'">
-				<MkFolder class="_gap">
-					<template #header><i class="fas fa-clock"></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="fas fa-fire-alt"></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>
-			</div>
-			<div v-else-if="tab === 'liked'">
-				<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>
-			</div>
-			<div v-else-if="tab === 'my'">
-				<MkA to="/gallery/new" class="_link" style="margin: 16px;"><i class="fas fa-plus"></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>
-			</div>
-		</div>
-	</MkSpacer>
-</MkStickyContainer>
-</template>
-
-<script lang="ts" setup>
-import { computed, defineComponent, watch } from 'vue';
-import XUserList from '@/components/user-list.vue';
-import MkFolder from '@/components/ui/folder.vue';
-import MkButton from '@/components/ui/button.vue';
-import MkPagination from '@/components/ui/pagination.vue';
-import MkGalleryPostPreview from '@/components/gallery-post-preview.vue';
-import number from '@/filters/number';
-import * as os from '@/os';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
-import { useRouter } from '@/router';
-
-const router = useRouter();
-
-const props = defineProps<{
-	tag?: string;
-}>();
-
-let tab = $ref('explore');
-let tags = $ref([]);
-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,
-};
-
-const tagUsersPagination = $computed(() => ({
-	endpoint: 'hashtags/users' as const,
-	limit: 30,
-	params: {
-		tag: this.tag,
-		origin: 'combined',
-		sort: '+follower',
-	},
-}));
-
-watch(() => props.tag, () => {
-	if (tagsRef) tagsRef.tags.toggleContent(props.tag == null);
-});
-
-const headerActions = $computed(() => [{
-	icon: 'fas fa-plus',
-	text: i18n.ts.create,
-	handler: () => {
-		router.push('/gallery/new');
-	},
-}]);
-
-const headerTabs = $computed(() => [{
-	key: 'explore',
-	title: i18n.ts.gallery,
-	icon: 'fas fa-icons',
-}, {
-	key: 'liked',
-	title: i18n.ts._gallery.liked,
-	icon: 'fas fa-heart',
-}, {
-	key: 'my',
-	title: i18n.ts._gallery.my,
-	icon: 'fas fa-edit',
-}]);
-
-definePageMetadata({
-	title: i18n.ts.gallery,
-	icon: 'fas fa-icons',
-});
-</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>
diff --git a/packages/client/src/pages/gallery/post.vue b/packages/client/src/pages/gallery/post.vue
deleted file mode 100644
index 2886321d2..000000000
--- a/packages/client/src/pages/gallery/post.vue
+++ /dev/null
@@ -1,264 +0,0 @@
-<template>
-<MkStickyContainer>
-	<template #header><MkPageHeader :actions="headerActions"/></template>
-	<MkSpacer :content-max="1000" :margin-min="16" :margin-max="32">
-		<div class="_root">
-			<transition :name="defaultStore.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="fas fa-clock"></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="fas fa-heart"></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="far fa-heart"></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="fas fa-pencil-alt fa-fw"></i></button>
-								<button v-tooltip="i18n.ts.shareWithNote" class="_button" @click="shareWithNote"><i class="fas fa-retweet fa-fw"></i></button>
-								<button v-tooltip="i18n.ts.share" class="_button" @click="share"><i class="fas fa-share-alt fa-fw"></i></button>
-							</div>
-						</div>
-						<div class="user">
-							<MkAvatar :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>
-					<MkAd :prefer="['horizontal', 'horizontal-big']"/>
-					<MkContainer :max-height="300" :foldable="true" class="other">
-						<template #header><i class="fas fa-clock"></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/ui/button.vue';
-import * as os from '@/os';
-import MkContainer from '@/components/ui/container.vue';
-import ImgWithBlurhash from '@/components/img-with-blurhash.vue';
-import MkPagination from '@/components/ui/pagination.vue';
-import MkGalleryPostPreview from '@/components/gallery-post-preview.vue';
-import MkFollowButton from '@/components/follow-button.vue';
-import { url } from '@/config';
-import { useRouter } from '@/router';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { defaultStore } from '@/store';
-
-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.apiWithDialog('gallery/posts/like', {
-		postId: props.postId,
-	}).then(() => {
-		post.isLiked = true;
-		post.likedCount++;
-	});
-}
-
-async function unlike() {
-	const confirm = await os.confirm({
-		type: 'warning',
-		text: i18n.ts.unlikeConfirm,
-	});
-	if (confirm.canceled) return;
-	os.apiWithDialog('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: 'fas fa-pencil-alt',
-	text: i18n.ts.edit,
-	handler: edit,
-}]);
-
-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;
-			}
-
-			& + .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: rgb(241 97 132);
-					--X8: rgb(241 92 128);
-					--buttonBg: rgb(216 71 106 / 5%);
-					--buttonHoverBg: rgb(216 71 106 / 10%);
-					color: #ff002f;
-
-					::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>
diff --git a/packages/client/src/pages/user/gallery.vue b/packages/client/src/pages/user/gallery.vue
deleted file mode 100644
index 1d053b279..000000000
--- a/packages/client/src/pages/user/gallery.vue
+++ /dev/null
@@ -1,38 +0,0 @@
-<template>
-<div>
-	<MkPagination v-slot="{items}" :pagination="pagination">
-		<div class="jrnovfpt">
-			<MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
-		</div>
-	</MkPagination>
-</div>
-</template>
-
-<script lang="ts" setup>
-import { computed } from 'vue';
-import * as foundkey from 'foundkey-js';
-import MkGalleryPostPreview from '@/components/gallery-post-preview.vue';
-import MkPagination from '@/components/ui/pagination.vue';
-
-const props = withDefaults(defineProps<{
-	user: foundkey.entities.User;
-}>(), {
-});
-
-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>
diff --git a/packages/client/src/pages/user/index.vue b/packages/client/src/pages/user/index.vue
index 3b47bb91a..24c88e75d 100644
--- a/packages/client/src/pages/user/index.vue
+++ b/packages/client/src/pages/user/index.vue
@@ -8,7 +8,6 @@
 				<XReactions v-else-if="tab === 'reactions'" :user="user"/>
 				<XClips v-else-if="tab === 'clips'" :user="user"/>
 				<XPages v-else-if="tab === 'pages'" :user="user"/>
-				<XGallery v-else-if="tab === 'gallery'" :user="user"/>
 			</div>
 			<MkError v-else-if="error" @retry="fetchUser()"/>
 			<MkLoading v-else/>
@@ -33,7 +32,6 @@ const XHome = defineAsyncComponent(() => import('./home.vue'));
 const XReactions = defineAsyncComponent(() => import('./reactions.vue'));
 const XClips = defineAsyncComponent(() => import('./clips.vue'));
 const XPages = defineAsyncComponent(() => import('./pages.vue'));
-const XGallery = defineAsyncComponent(() => import('./gallery.vue'));
 
 const props = withDefaults(defineProps<{
 	acct: string;
@@ -82,10 +80,6 @@ const headerTabs = $computed(() => [{
 	key: 'pages',
 	title: i18n.ts.pages,
 	icon: 'fas fa-file-alt',
-}, {
-	key: 'gallery',
-	title: i18n.ts.gallery,
-	icon: 'fas fa-icons',
 }]);
 
 definePageMetadata(computed(() => user ? {
diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts
index 791076e71..10869a3cf 100644
--- a/packages/client/src/router.ts
+++ b/packages/client/src/router.ts
@@ -123,20 +123,6 @@ export const routes = [{
 }, {
 	path: '/pages',
 	component: page(() => import('./pages/pages.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: '/channels/:channelId/edit',
 	component: page(() => import('./pages/channel-editor.vue')),

From 70fb1e9a5cf6633e8b77794b561c9fa529355e51 Mon Sep 17 00:00:00 2001
From: Johann150 <johann.galle@protonmail.com>
Date: Mon, 16 Jan 2023 18:47:29 +0100
Subject: [PATCH 2/4] foundkey-js: remove galleries

---
 locales/en-US.yml                     |  4 ----
 packages/foundkey-js/src/api.types.ts | 14 +-------------
 packages/foundkey-js/src/consts.ts    |  4 ----
 packages/foundkey-js/src/entities.ts  |  2 --
 4 files changed, 1 insertion(+), 23 deletions(-)

diff --git a/locales/en-US.yml b/locales/en-US.yml
index 9999a5115..14df6ca9c 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -1099,10 +1099,6 @@ _permissions:
   "write:user-groups": "Create, modify, delete, transfer, join and leave groups. Invite and ban others from groups. Accept and reject group invitations."
   "read:channels": "List and read followed and joined channels"
   "write:channels": "Create, modify, follow and unfollow channels"
-  "read:gallery": "List and read gallery posts"
-  "write:gallery": "Create, modify and delete gallery posts"
-  "read:gallery-likes": "List and read gallery post likes"
-  "write:gallery-likes": "Like and unlike gallery posts"
 _auth:
   shareAccess: "Would you like to authorize \"{name}\" to access this account?"
   shareAccessAsk: "Are you sure you want to authorize this application to access your\
diff --git a/packages/foundkey-js/src/api.types.ts b/packages/foundkey-js/src/api.types.ts
index 8798113a5..64a5f98aa 100644
--- a/packages/foundkey-js/src/api.types.ts
+++ b/packages/foundkey-js/src/api.types.ts
@@ -1,5 +1,5 @@
 import {
-	Ad, Announcement, Antenna, App, AuthSession, Blocking, Channel, Clip, DateString, DetailedInstanceMetadata, DriveFile, DriveFolder, Following, FollowingFolloweePopulated, FollowingFollowerPopulated, FollowRequest, GalleryPost, Instance, InstanceMetadata,
+	Ad, Announcement, Antenna, App, AuthSession, Blocking, Channel, Clip, DateString, DetailedInstanceMetadata, DriveFile, DriveFolder, Following, FollowingFolloweePopulated, FollowingFollowerPopulated, FollowRequest, Instance, InstanceMetadata,
 	LiteInstanceMetadata,
 	MeDetailed,
 	Note, NoteFavorite, OriginType, Page, ServerInfo, Stats, User, UserDetailed, UserGroup, UserList, UserSorting, Notification, NoteReaction, Signin, MessagingMessage,
@@ -282,15 +282,6 @@ export type Endpoints = {
 	'following/requests/cancel': { req: { userId: User['id'] }; res: User; };
 	'following/requests/list': { req: NoParams; res: FollowRequest[]; };
 	'following/requests/reject': { req: { userId: User['id'] }; res: null; };
-	'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; };
 	'get-online-users-count': { req: TODO; res: TODO; };
 	'hashtags/list': { req: TODO; res: TODO; };
 	'hashtags/search': { req: TODO; res: TODO; };
@@ -315,8 +306,6 @@ export type Endpoints = {
 	'i/export-notes': { req: TODO; res: TODO; };
 	'i/export-user-lists': { req: TODO; res: TODO; };
 	'i/favorites': { req: { limit?: number; sinceId?: NoteFavorite['id']; untilId?: NoteFavorite['id']; }; 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-blocking': { req: TODO; res: TODO; };
 	'i/import-following': { req: TODO; res: TODO; };
@@ -488,7 +477,6 @@ export type Endpoints = {
 	'users/clips': { req: TODO; res: TODO; };
 	'users/followers': { req: { userId?: User['id']; username?: User['username']; host?: User['host'] | null; limit?: number; sinceId?: Following['id']; untilId?: Following['id']; }; res: FollowingFollowerPopulated[]; };
 	'users/following': { req: { userId?: User['id']; username?: User['username']; host?: User['host'] | null; limit?: number; sinceId?: Following['id']; untilId?: Following['id']; }; res: FollowingFolloweePopulated[]; };
-	'users/gallery/posts': { req: TODO; res: TODO; };
 	'users/groups/create': { req: TODO; res: TODO; };
 	'users/groups/delete': { req: { groupId: UserGroup['id'] }; res: null; };
 	'users/groups/invitations/accept': { req: TODO; res: TODO; };
diff --git a/packages/foundkey-js/src/consts.ts b/packages/foundkey-js/src/consts.ts
index 645bdf223..a90b8720a 100644
--- a/packages/foundkey-js/src/consts.ts
+++ b/packages/foundkey-js/src/consts.ts
@@ -35,8 +35,4 @@ export const permissions = [
 	'write:user-groups',
 	'read:channels',
 	'write:channels',
-	'read:gallery',
-	'write:gallery',
-	'read:gallery-likes',
-	'write:gallery-likes',
 ];
diff --git a/packages/foundkey-js/src/entities.ts b/packages/foundkey-js/src/entities.ts
index c92e60082..3d2404b47 100644
--- a/packages/foundkey-js/src/entities.ts
+++ b/packages/foundkey-js/src/entities.ts
@@ -126,8 +126,6 @@ export type DriveFile = {
 
 export type DriveFolder = TODO;
 
-export type GalleryPost = TODO;
-
 export type Note = {
 	id: ID;
 	createdAt: DateString;

From 2bbb85b47209bb01d9b822d536a9f96028a4161d Mon Sep 17 00:00:00 2001
From: Johann150 <johann.galle@protonmail.com>
Date: Mon, 16 Jan 2023 18:53:57 +0100
Subject: [PATCH 3/4] backend: remove galleries

---
 packages/backend/src/db/postgre.ts            |  4 -
 packages/backend/src/misc/api-permissions.ts  |  4 -
 packages/backend/src/misc/schema.ts           |  2 -
 .../src/models/entities/gallery-like.ts       | 33 --------
 .../src/models/entities/gallery-post.ts       | 79 -------------------
 packages/backend/src/models/index.ts          |  4 -
 .../src/models/repositories/gallery-like.ts   | 24 ------
 .../src/models/repositories/gallery-post.ts   | 39 ---------
 .../backend/src/models/schema/gallery-post.ts | 69 ----------------
 packages/backend/src/server/api/endpoints.ts  | 24 ------
 .../server/api/endpoints/gallery/featured.ts  | 37 ---------
 .../server/api/endpoints/gallery/popular.ts   | 35 --------
 .../src/server/api/endpoints/gallery/posts.ts | 37 ---------
 .../api/endpoints/gallery/posts/create.ts     | 72 -----------------
 .../api/endpoints/gallery/posts/delete.ts     | 33 --------
 .../api/endpoints/gallery/posts/like.ts       | 46 -----------
 .../api/endpoints/gallery/posts/show.ts       | 36 ---------
 .../api/endpoints/gallery/posts/unlike.ts     | 39 ---------
 .../api/endpoints/gallery/posts/update.ts     | 75 ------------------
 .../server/api/endpoints/i/gallery/likes.ts   | 55 -------------
 .../server/api/endpoints/i/gallery/posts.ts   | 43 ----------
 .../api/endpoints/users/gallery/posts.ts      | 42 ----------
 packages/backend/src/server/api/error.ts      |  8 +-
 packages/backend/src/server/web/index.ts      | 27 +------
 .../src/server/web/views/gallery-post.pug     | 33 --------
 25 files changed, 3 insertions(+), 897 deletions(-)
 delete mode 100644 packages/backend/src/models/entities/gallery-like.ts
 delete mode 100644 packages/backend/src/models/entities/gallery-post.ts
 delete mode 100644 packages/backend/src/models/repositories/gallery-like.ts
 delete mode 100644 packages/backend/src/models/repositories/gallery-post.ts
 delete mode 100644 packages/backend/src/models/schema/gallery-post.ts
 delete mode 100644 packages/backend/src/server/api/endpoints/gallery/featured.ts
 delete mode 100644 packages/backend/src/server/api/endpoints/gallery/popular.ts
 delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts.ts
 delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts/create.ts
 delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts/delete.ts
 delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts/like.ts
 delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts/show.ts
 delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts
 delete mode 100644 packages/backend/src/server/api/endpoints/gallery/posts/update.ts
 delete mode 100644 packages/backend/src/server/api/endpoints/i/gallery/likes.ts
 delete mode 100644 packages/backend/src/server/api/endpoints/i/gallery/posts.ts
 delete mode 100644 packages/backend/src/server/api/endpoints/users/gallery/posts.ts
 delete mode 100644 packages/backend/src/server/web/views/gallery-post.pug

diff --git a/packages/backend/src/db/postgre.ts b/packages/backend/src/db/postgre.ts
index 90b123ae8..b59142597 100644
--- a/packages/backend/src/db/postgre.ts
+++ b/packages/backend/src/db/postgre.ts
@@ -50,8 +50,6 @@ import { UserSecurityKey } from '@/models/entities/user-security-key.js';
 import { AttestationChallenge } from '@/models/entities/attestation-challenge.js';
 import { Page } from '@/models/entities/page.js';
 import { PageLike } from '@/models/entities/page-like.js';
-import { GalleryPost } from '@/models/entities/gallery-post.js';
-import { GalleryLike } from '@/models/entities/gallery-like.js';
 import { ModerationLog } from '@/models/entities/moderation-log.js';
 import { UsedUsername } from '@/models/entities/used-username.js';
 import { Announcement } from '@/models/entities/announcement.js';
@@ -143,8 +141,6 @@ export const entities = [
 	NoteUnread,
 	Page,
 	PageLike,
-	GalleryPost,
-	GalleryLike,
 	DriveFile,
 	DriveFolder,
 	Poll,
diff --git a/packages/backend/src/misc/api-permissions.ts b/packages/backend/src/misc/api-permissions.ts
index 160cdf9fd..d7c115a50 100644
--- a/packages/backend/src/misc/api-permissions.ts
+++ b/packages/backend/src/misc/api-permissions.ts
@@ -27,9 +27,5 @@ export const kinds = [
 	'write:user-groups',
 	'read:channels',
 	'write:channels',
-	'read:gallery',
-	'write:gallery',
-	'read:gallery-likes',
-	'write:gallery-likes',
 ];
 // IF YOU ADD KINDS(PERMISSIONS), YOU MUST ADD TRANSLATIONS (under _permissions).
diff --git a/packages/backend/src/misc/schema.ts b/packages/backend/src/misc/schema.ts
index ad5dcb067..bad291b4a 100644
--- a/packages/backend/src/misc/schema.ts
+++ b/packages/backend/src/misc/schema.ts
@@ -28,7 +28,6 @@ import { packedAntennaSchema } from '@/models/schema/antenna.js';
 import { packedClipSchema } from '@/models/schema/clip.js';
 import { packedFederationInstanceSchema } from '@/models/schema/federation-instance.js';
 import { packedQueueCountSchema } from '@/models/schema/queue.js';
-import { packedGalleryPostSchema } from '@/models/schema/gallery-post.js';
 import { packedEmojiSchema } from '@/models/schema/emoji.js';
 
 export const refs = {
@@ -61,7 +60,6 @@ export const refs = {
 	Antenna: packedAntennaSchema,
 	Clip: packedClipSchema,
 	FederationInstance: packedFederationInstanceSchema,
-	GalleryPost: packedGalleryPostSchema,
 	Emoji: packedEmojiSchema,
 };
 
diff --git a/packages/backend/src/models/entities/gallery-like.ts b/packages/backend/src/models/entities/gallery-like.ts
deleted file mode 100644
index 259981392..000000000
--- a/packages/backend/src/models/entities/gallery-like.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
-import { id } from '../id.js';
-import { User } from './user.js';
-import { GalleryPost } from './gallery-post.js';
-
-@Entity()
-@Index(['userId', 'postId'], { unique: true })
-export class GalleryLike {
-	@PrimaryColumn(id())
-	public id: string;
-
-	@Column('timestamp with time zone')
-	public createdAt: Date;
-
-	@Index()
-	@Column(id())
-	public userId: User['id'];
-
-	@ManyToOne(() => User, {
-		onDelete: 'CASCADE',
-	})
-	@JoinColumn()
-	public user: User | null;
-
-	@Column(id())
-	public postId: GalleryPost['id'];
-
-	@ManyToOne(() => GalleryPost, {
-		onDelete: 'CASCADE',
-	})
-	@JoinColumn()
-	public post: GalleryPost | null;
-}
diff --git a/packages/backend/src/models/entities/gallery-post.ts b/packages/backend/src/models/entities/gallery-post.ts
deleted file mode 100644
index 315bcd371..000000000
--- a/packages/backend/src/models/entities/gallery-post.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
-import { id } from '../id.js';
-import { User } from './user.js';
-import { DriveFile } from './drive-file.js';
-
-@Entity()
-export class GalleryPost {
-	@PrimaryColumn(id())
-	public id: string;
-
-	@Index()
-	@Column('timestamp with time zone', {
-		comment: 'The created date of the GalleryPost.',
-	})
-	public createdAt: Date;
-
-	@Index()
-	@Column('timestamp with time zone', {
-		comment: 'The updated date of the GalleryPost.',
-	})
-	public updatedAt: Date;
-
-	@Column('varchar', {
-		length: 256,
-	})
-	public title: string;
-
-	@Column('varchar', {
-		length: 2048, nullable: true,
-	})
-	public description: string | null;
-
-	@Index()
-	@Column({
-		...id(),
-		comment: 'The ID of author.',
-	})
-	public userId: User['id'];
-
-	@ManyToOne(() => User, {
-		onDelete: 'CASCADE',
-	})
-	@JoinColumn()
-	public user: User | null;
-
-	@Index()
-	@Column({
-		...id(),
-		array: true, default: '{}',
-	})
-	public fileIds: DriveFile['id'][];
-
-	@Index()
-	@Column('boolean', {
-		default: false,
-		comment: 'Whether the post is sensitive.',
-	})
-	public isSensitive: boolean;
-
-	@Index()
-	@Column('integer', {
-		default: 0,
-	})
-	public likedCount: number;
-
-	@Index()
-	@Column('varchar', {
-		length: 128, array: true, default: '{}',
-	})
-	public tags: string[];
-
-	constructor(data: Partial<GalleryPost>) {
-		if (data == null) return;
-
-		for (const [k, v] of Object.entries(data)) {
-			(this as any)[k] = v;
-		}
-	}
-}
diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts
index 993f3e0f2..f4aa84d5b 100644
--- a/packages/backend/src/models/index.ts
+++ b/packages/backend/src/models/index.ts
@@ -42,8 +42,6 @@ import { UserSecurityKey } from './entities/user-security-key.js';
 import { HashtagRepository } from './repositories/hashtag.js';
 import { PageRepository } from './repositories/page.js';
 import { PageLikeRepository } from './repositories/page-like.js';
-import { GalleryPostRepository } from './repositories/gallery-post.js';
-import { GalleryLikeRepository } from './repositories/gallery-like.js';
 import { ModerationLogRepository } from './repositories/moderation-logs.js';
 import { UsedUsername } from './entities/used-username.js';
 import { ClipRepository } from './repositories/clip.js';
@@ -108,8 +106,6 @@ export const Signins = (SigninRepository);
 export const MessagingMessages = (MessagingMessageRepository);
 export const Pages = (PageRepository);
 export const PageLikes = (PageLikeRepository);
-export const GalleryPosts = (GalleryPostRepository);
-export const GalleryLikes = (GalleryLikeRepository);
 export const ModerationLogs = (ModerationLogRepository);
 export const Clips = (ClipRepository);
 export const ClipNotes = db.getRepository(ClipNote);
diff --git a/packages/backend/src/models/repositories/gallery-like.ts b/packages/backend/src/models/repositories/gallery-like.ts
deleted file mode 100644
index 33f5b3ebb..000000000
--- a/packages/backend/src/models/repositories/gallery-like.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { db } from '@/db/postgre.js';
-import { GalleryLike } from '@/models/entities/gallery-like.js';
-import { GalleryPosts } from '../index.js';
-
-export const GalleryLikeRepository = db.getRepository(GalleryLike).extend({
-	async pack(
-		src: GalleryLike['id'] | GalleryLike,
-		me?: any,
-	) {
-		const like = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
-
-		return {
-			id: like.id,
-			post: await GalleryPosts.pack(like.post || like.postId, me),
-		};
-	},
-
-	packMany(
-		likes: any[],
-		me: any,
-	) {
-		return Promise.all(likes.map(x => this.pack(x, me)));
-	},
-});
diff --git a/packages/backend/src/models/repositories/gallery-post.ts b/packages/backend/src/models/repositories/gallery-post.ts
deleted file mode 100644
index 7c54001ec..000000000
--- a/packages/backend/src/models/repositories/gallery-post.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { db } from '@/db/postgre.js';
-import { Packed } from '@/misc/schema.js';
-import { GalleryPost } from '@/models/entities/gallery-post.js';
-import { User } from '@/models/entities/user.js';
-import { awaitAll } from '@/prelude/await-all.js';
-import { Users, DriveFiles, GalleryLikes } from '../index.js';
-
-export const GalleryPostRepository = db.getRepository(GalleryPost).extend({
-	async pack(
-		src: GalleryPost['id'] | GalleryPost,
-		me?: { id: User['id'] } | null | undefined,
-	): Promise<Packed<'GalleryPost'>> {
-		const meId = me ? me.id : null;
-		const post = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
-
-		return await awaitAll({
-			id: post.id,
-			createdAt: post.createdAt.toISOString(),
-			updatedAt: post.updatedAt.toISOString(),
-			userId: post.userId,
-			user: Users.pack(post.user || post.userId, me),
-			title: post.title,
-			description: post.description,
-			fileIds: post.fileIds,
-			files: DriveFiles.packMany(post.fileIds),
-			tags: post.tags.length > 0 ? post.tags : undefined,
-			isSensitive: post.isSensitive,
-			likedCount: post.likedCount,
-			isLiked: meId ? await GalleryLikes.findOneBy({ postId: post.id, userId: meId }).then(x => x != null) : undefined,
-		});
-	},
-
-	packMany(
-		posts: GalleryPost[],
-		me?: { id: User['id'] } | null | undefined,
-	) {
-		return Promise.all(posts.map(x => this.pack(x, me)));
-	},
-});
diff --git a/packages/backend/src/models/schema/gallery-post.ts b/packages/backend/src/models/schema/gallery-post.ts
deleted file mode 100644
index fc503d4a6..000000000
--- a/packages/backend/src/models/schema/gallery-post.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-export const packedGalleryPostSchema = {
-	type: 'object',
-	properties: {
-		id: {
-			type: 'string',
-			optional: false, nullable: false,
-			format: 'id',
-			example: 'xxxxxxxxxx',
-		},
-		createdAt: {
-			type: 'string',
-			optional: false, nullable: false,
-			format: 'date-time',
-		},
-		updatedAt: {
-			type: 'string',
-			optional: false, nullable: false,
-			format: 'date-time',
-		},
-		title: {
-			type: 'string',
-			optional: false, nullable: false,
-		},
-		description: {
-			type: 'string',
-			optional: false, nullable: true,
-		},
-		userId: {
-			type: 'string',
-			optional: false, nullable: false,
-			format: 'id',
-		},
-		user: {
-			type: 'object',
-			ref: 'UserLite',
-			optional: false, nullable: false,
-		},
-		fileIds: {
-			type: 'array',
-			optional: true, nullable: false,
-			items: {
-				type: 'string',
-				optional: false, nullable: false,
-				format: 'id',
-			},
-		},
-		files: {
-			type: 'array',
-			optional: true, nullable: false,
-			items: {
-				type: 'object',
-				optional: false, nullable: false,
-				ref: 'DriveFile',
-			},
-		},
-		tags: {
-			type: 'array',
-			optional: true, nullable: false,
-			items: {
-				type: 'string',
-				optional: false, nullable: false,
-			},
-		},
-		isSensitive: {
-			type: 'boolean',
-			optional: false, nullable: false,
-		},
-	},
-} as const;
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index 59c4305c2..926d8d66f 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -138,15 +138,6 @@ import * as ep___following_requests_accept from './endpoints/following/requests/
 import * as ep___following_requests_cancel from './endpoints/following/requests/cancel.js';
 import * as ep___following_requests_list from './endpoints/following/requests/list.js';
 import * as ep___following_requests_reject from './endpoints/following/requests/reject.js';
-import * as ep___gallery_featured from './endpoints/gallery/featured.js';
-import * as ep___gallery_popular from './endpoints/gallery/popular.js';
-import * as ep___gallery_posts from './endpoints/gallery/posts.js';
-import * as ep___gallery_posts_create from './endpoints/gallery/posts/create.js';
-import * as ep___gallery_posts_delete from './endpoints/gallery/posts/delete.js';
-import * as ep___gallery_posts_like from './endpoints/gallery/posts/like.js';
-import * as ep___gallery_posts_show from './endpoints/gallery/posts/show.js';
-import * as ep___gallery_posts_unlike from './endpoints/gallery/posts/unlike.js';
-import * as ep___gallery_posts_update from './endpoints/gallery/posts/update.js';
 import * as ep___getOnlineUsersCount from './endpoints/get-online-users-count.js';
 import * as ep___hashtags_list from './endpoints/hashtags/list.js';
 import * as ep___hashtags_search from './endpoints/hashtags/search.js';
@@ -171,8 +162,6 @@ import * as ep___i_exportMute from './endpoints/i/export-mute.js';
 import * as ep___i_exportNotes from './endpoints/i/export-notes.js';
 import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js';
 import * as ep___i_favorites from './endpoints/i/favorites.js';
-import * as ep___i_gallery_likes from './endpoints/i/gallery/likes.js';
-import * as ep___i_gallery_posts from './endpoints/i/gallery/posts.js';
 import * as ep___i_getWordMutedNotesCount from './endpoints/i/get-word-muted-notes-count.js';
 import * as ep___i_importBlocking from './endpoints/i/import-blocking.js';
 import * as ep___i_importFollowing from './endpoints/i/import-following.js';
@@ -276,7 +265,6 @@ import * as ep___users from './endpoints/users.js';
 import * as ep___users_clips from './endpoints/users/clips.js';
 import * as ep___users_followers from './endpoints/users/followers.js';
 import * as ep___users_following from './endpoints/users/following.js';
-import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js';
 import * as ep___users_groups_create from './endpoints/users/groups/create.js';
 import * as ep___users_groups_delete from './endpoints/users/groups/delete.js';
 import * as ep___users_groups_invitations_accept from './endpoints/users/groups/invitations/accept.js';
@@ -446,15 +434,6 @@ const eps = [
 	['following/requests/cancel', ep___following_requests_cancel],
 	['following/requests/list', ep___following_requests_list],
 	['following/requests/reject', ep___following_requests_reject],
-	['gallery/featured', ep___gallery_featured],
-	['gallery/popular', ep___gallery_popular],
-	['gallery/posts', ep___gallery_posts],
-	['gallery/posts/create', ep___gallery_posts_create],
-	['gallery/posts/delete', ep___gallery_posts_delete],
-	['gallery/posts/like', ep___gallery_posts_like],
-	['gallery/posts/show', ep___gallery_posts_show],
-	['gallery/posts/unlike', ep___gallery_posts_unlike],
-	['gallery/posts/update', ep___gallery_posts_update],
 	['get-online-users-count', ep___getOnlineUsersCount],
 	['hashtags/list', ep___hashtags_list],
 	['hashtags/search', ep___hashtags_search],
@@ -479,8 +458,6 @@ const eps = [
 	['i/export-notes', ep___i_exportNotes],
 	['i/export-user-lists', ep___i_exportUserLists],
 	['i/favorites', ep___i_favorites],
-	['i/gallery/likes', ep___i_gallery_likes],
-	['i/gallery/posts', ep___i_gallery_posts],
 	['i/get-word-muted-notes-count', ep___i_getWordMutedNotesCount],
 	['i/import-blocking', ep___i_importBlocking],
 	['i/import-following', ep___i_importFollowing],
@@ -584,7 +561,6 @@ const eps = [
 	['users/clips', ep___users_clips],
 	['users/followers', ep___users_followers],
 	['users/following', ep___users_following],
-	['users/gallery/posts', ep___users_gallery_posts],
 	['users/groups/create', ep___users_groups_create],
 	['users/groups/delete', ep___users_groups_delete],
 	['users/groups/invitations/accept', ep___users_groups_invitations_accept],
diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts
deleted file mode 100644
index baa70c14e..000000000
--- a/packages/backend/src/server/api/endpoints/gallery/featured.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { DAY } from '@/const.js';
-import { GalleryPosts } from '@/models/index.js';
-import define from '../../define.js';
-
-export const meta = {
-	tags: ['gallery'],
-
-	requireCredential: false,
-
-	res: {
-		type: 'array',
-		optional: false, nullable: false,
-		items: {
-			type: 'object',
-			optional: false, nullable: false,
-			ref: 'GalleryPost',
-		},
-	},
-} as const;
-
-export const paramDef = {
-	type: 'object',
-	properties: {},
-	required: [],
-} as const;
-
-// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, me) => {
-	const query = GalleryPosts.createQueryBuilder('post')
-		.andWhere('post.createdAt > :date', { date: new Date(Date.now() - 3 * DAY) })
-		.andWhere('post.likedCount > 0')
-		.orderBy('post.likedCount', 'DESC');
-
-	const posts = await query.take(10).getMany();
-
-	return await GalleryPosts.packMany(posts, me);
-});
diff --git a/packages/backend/src/server/api/endpoints/gallery/popular.ts b/packages/backend/src/server/api/endpoints/gallery/popular.ts
deleted file mode 100644
index 552810e54..000000000
--- a/packages/backend/src/server/api/endpoints/gallery/popular.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { GalleryPosts } from '@/models/index.js';
-import define from '../../define.js';
-
-export const meta = {
-	tags: ['gallery'],
-
-	requireCredential: false,
-
-	res: {
-		type: 'array',
-		optional: false, nullable: false,
-		items: {
-			type: 'object',
-			optional: false, nullable: false,
-			ref: 'GalleryPost',
-		},
-	},
-} as const;
-
-export const paramDef = {
-	type: 'object',
-	properties: {},
-	required: [],
-} as const;
-
-// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, me) => {
-	const query = GalleryPosts.createQueryBuilder('post')
-		.andWhere('post.likedCount > 0')
-		.orderBy('post.likedCount', 'DESC');
-
-	const posts = await query.take(10).getMany();
-
-	return await GalleryPosts.packMany(posts, me);
-});
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts
deleted file mode 100644
index 3a21afae1..000000000
--- a/packages/backend/src/server/api/endpoints/gallery/posts.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { GalleryPosts } from '@/models/index.js';
-import define from '../../define.js';
-import { makePaginationQuery } from '../../common/make-pagination-query.js';
-
-export const meta = {
-	tags: ['gallery'],
-
-	res: {
-		type: 'array',
-		optional: false, nullable: false,
-		items: {
-			type: 'object',
-			optional: false, nullable: false,
-			ref: 'GalleryPost',
-		},
-	},
-} as const;
-
-export const paramDef = {
-	type: 'object',
-	properties: {
-		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
-		sinceId: { type: 'string', format: 'misskey:id' },
-		untilId: { type: 'string', format: 'misskey:id' },
-	},
-	required: [],
-} as const;
-
-// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, me) => {
-	const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId)
-		.innerJoinAndSelect('post.user', 'user');
-
-	const posts = await query.take(ps.limit).getMany();
-
-	return await GalleryPosts.packMany(posts, me);
-});
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
deleted file mode 100644
index 230b7bec3..000000000
--- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { DriveFiles, GalleryPosts } from '@/models/index.js';
-import { genId } from '@/misc/gen-id.js';
-import { GalleryPost } from '@/models/entities/gallery-post.js';
-import { DriveFile } from '@/models/entities/drive-file.js';
-import { HOUR } from '@/const.js';
-import { ApiError } from '@/server/api/error.js';
-import define from '../../../define.js';
-
-export const meta = {
-	tags: ['gallery'],
-
-	requireCredential: true,
-
-	kind: 'write:gallery',
-
-	limit: {
-		duration: HOUR,
-		max: 300,
-	},
-
-	res: {
-		type: 'object',
-		optional: false, nullable: false,
-		ref: 'GalleryPost',
-	},
-} as const;
-
-export const paramDef = {
-	type: 'object',
-	properties: {
-		title: { type: 'string', minLength: 1 },
-		description: { type: 'string', nullable: true },
-		fileIds: { type: 'array', uniqueItems: true, minItems: 1, maxItems: 32, items: {
-			type: 'string', format: 'misskey:id',
-		} },
-		isSensitive: { type: 'boolean', default: false },
-	},
-	required: ['title', 'fileIds'],
-} as const;
-
-// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, user) => {
-	const files = (await Promise.all(ps.fileIds.map(fileId =>
-		DriveFiles.findOneBy({
-			id: fileId,
-			userId: user.id,
-		}),
-	))).filter((file): file is DriveFile => file != null);
-
-	if (files.length !== ps.fileIds.length) {
-		throw new ApiError(
-			'INVALID_PARAM',
-			{
-				param: '#/properties/fileIds/items',
-				reason: 'contains invalid file IDs',
-			},
-		);
-	}
-
-	const post = await GalleryPosts.insert(new GalleryPost({
-		id: genId(),
-		createdAt: new Date(),
-		updatedAt: new Date(),
-		title: ps.title,
-		description: ps.description,
-		userId: user.id,
-		isSensitive: ps.isSensitive,
-		fileIds: files.map(file => file.id),
-	})).then(x => GalleryPosts.findOneByOrFail(x.identifiers[0]));
-
-	return await GalleryPosts.pack(post, user);
-});
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts
deleted file mode 100644
index 65c0e62d0..000000000
--- a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { GalleryPosts } from '@/models/index.js';
-import define from '../../../define.js';
-import { ApiError } from '../../../error.js';
-
-export const meta = {
-	tags: ['gallery'],
-
-	requireCredential: true,
-
-	kind: 'write:gallery',
-
-	errors: ['NO_SUCH_POST'],
-} as const;
-
-export const paramDef = {
-	type: 'object',
-	properties: {
-		postId: { type: 'string', format: 'misskey:id' },
-	},
-	required: ['postId'],
-} as const;
-
-// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, user) => {
-	const post = await GalleryPosts.findOneBy({
-		id: ps.postId,
-		userId: user.id,
-	});
-
-	if (post == null) throw new ApiError('NO_SUCH_POST');
-
-	await GalleryPosts.delete(post.id);
-});
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
deleted file mode 100644
index 1525d4dad..000000000
--- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { GalleryPosts, GalleryLikes } from '@/models/index.js';
-import { genId } from '@/misc/gen-id.js';
-import define from '../../../define.js';
-import { ApiError } from '../../../error.js';
-
-export const meta = {
-	tags: ['gallery'],
-
-	requireCredential: true,
-
-	kind: 'write:gallery-likes',
-
-	errors: ['NO_SUCH_POST', 'ALREADY_LIKED'],
-} as const;
-
-export const paramDef = {
-	type: 'object',
-	properties: {
-		postId: { type: 'string', format: 'misskey:id' },
-	},
-	required: ['postId'],
-} as const;
-
-// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, user) => {
-	const post = await GalleryPosts.findOneBy({ id: ps.postId });
-	if (post == null) throw new ApiError('NO_SUCH_POST');
-
-	// if already liked
-	const exist = await GalleryLikes.countBy({
-		postId: post.id,
-		userId: user.id,
-	});
-
-	if (exist) throw new ApiError('ALREADY_LIKED');
-
-	// Create like
-	await GalleryLikes.insert({
-		id: genId(),
-		createdAt: new Date(),
-		postId: post.id,
-		userId: user.id,
-	});
-
-	GalleryPosts.increment({ id: post.id }, 'likedCount', 1);
-});
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts
deleted file mode 100644
index 640048028..000000000
--- a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { GalleryPosts } from '@/models/index.js';
-import define from '../../../define.js';
-import { ApiError } from '../../../error.js';
-
-export const meta = {
-	tags: ['gallery'],
-
-	requireCredential: false,
-
-	errors: ['NO_SUCH_POST'],
-
-	res: {
-		type: 'object',
-		optional: false, nullable: false,
-		ref: 'GalleryPost',
-	},
-} as const;
-
-export const paramDef = {
-	type: 'object',
-	properties: {
-		postId: { type: 'string', format: 'misskey:id' },
-	},
-	required: ['postId'],
-} as const;
-
-// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, me) => {
-	const post = await GalleryPosts.findOneBy({
-		id: ps.postId,
-	});
-
-	if (post == null) throw new ApiError('NO_SUCH_POST');
-
-	return await GalleryPosts.pack(post, me);
-});
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts
deleted file mode 100644
index 61d71905e..000000000
--- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { GalleryPosts, GalleryLikes } from '@/models/index.js';
-import define from '../../../define.js';
-import { ApiError } from '../../../error.js';
-
-export const meta = {
-	tags: ['gallery'],
-
-	requireCredential: true,
-
-	kind: 'write:gallery-likes',
-
-	errors: ['NO_SUCH_POST', 'NOT_LIKED'],
-} as const;
-
-export const paramDef = {
-	type: 'object',
-	properties: {
-		postId: { type: 'string', format: 'misskey:id' },
-	},
-	required: ['postId'],
-} as const;
-
-// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, user) => {
-	const post = await GalleryPosts.findOneBy({ id: ps.postId });
-	if (post == null) throw new ApiError('NO_SUCH_POST');
-
-	const exist = await GalleryLikes.findOneBy({
-		postId: post.id,
-		userId: user.id,
-	});
-
-	if (exist == null) throw new ApiError('NOT_LIKED');
-
-	// Delete like
-	await GalleryLikes.delete(exist.id);
-
-	GalleryPosts.decrement({ id: post.id }, 'likedCount', 1);
-});
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
deleted file mode 100644
index 071229f89..000000000
--- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import { DriveFiles, GalleryPosts } from '@/models/index.js';
-import { DriveFile } from '@/models/entities/drive-file.js';
-import { HOUR } from '@/const.js';
-import { ApiError } from '@/server/api/error.js';
-import define from '../../../define.js';
-
-export const meta = {
-	tags: ['gallery'],
-
-	requireCredential: true,
-
-	kind: 'write:gallery',
-
-	limit: {
-		duration: HOUR,
-		max: 300,
-	},
-
-	res: {
-		type: 'object',
-		optional: false, nullable: false,
-		ref: 'GalleryPost',
-	},
-
-	errors: ['INVALID_PARAM'],
-} as const;
-
-export const paramDef = {
-	type: 'object',
-	properties: {
-		postId: { type: 'string', format: 'misskey:id' },
-		title: { type: 'string', minLength: 1 },
-		description: { type: 'string', nullable: true },
-		fileIds: { type: 'array', uniqueItems: true, minItems: 1, maxItems: 32, items: {
-			type: 'string', format: 'misskey:id',
-		} },
-		isSensitive: { type: 'boolean', default: false },
-	},
-	required: ['postId', 'title', 'fileIds'],
-} as const;
-
-// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, user) => {
-	const files = (await Promise.all(ps.fileIds.map(fileId =>
-		DriveFiles.findOneBy({
-			id: fileId,
-			userId: user.id,
-		}),
-	))).filter((file): file is DriveFile => file != null);
-
-	if (files.length !== ps.fileIds.length) {
-		throw new ApiError(
-			'INVALID_PARAM',
-			{
-				param: '#/properties/fileIds/items',
-				reason: 'contains invalid file IDs',
-			},
-		);
-	}
-
-	await GalleryPosts.update({
-		id: ps.postId,
-		userId: user.id,
-	}, {
-		updatedAt: new Date(),
-		title: ps.title,
-		description: ps.description,
-		isSensitive: ps.isSensitive,
-		fileIds: files.map(file => file.id),
-	});
-
-	const post = await GalleryPosts.findOneByOrFail({ id: ps.postId });
-
-	return await GalleryPosts.pack(post, user);
-});
diff --git a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts
deleted file mode 100644
index f5ceddd43..000000000
--- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { GalleryLikes } from '@/models/index.js';
-import define from '../../../define.js';
-import { makePaginationQuery } from '../../../common/make-pagination-query.js';
-
-export const meta = {
-	tags: ['account', 'gallery'],
-
-	requireCredential: true,
-
-	kind: 'read:gallery-likes',
-
-	res: {
-		type: 'array',
-		optional: false, nullable: false,
-		items: {
-			type: 'object',
-			optional: false, nullable: false,
-			properties: {
-				id: {
-					type: 'string',
-					optional: false, nullable: false,
-					format: 'id',
-				},
-				post: {
-					type: 'object',
-					optional: false, nullable: false,
-					ref: 'GalleryPost',
-				},
-			},
-		},
-	},
-} as const;
-
-export const paramDef = {
-	type: 'object',
-	properties: {
-		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
-		sinceId: { type: 'string', format: 'misskey:id' },
-		untilId: { type: 'string', format: 'misskey:id' },
-	},
-	required: [],
-} as const;
-
-// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, user) => {
-	const query = makePaginationQuery(GalleryLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId)
-		.andWhere('like.userId = :meId', { meId: user.id })
-		.leftJoinAndSelect('like.post', 'post');
-
-	const likes = await query
-		.take(ps.limit)
-		.getMany();
-
-	return await GalleryLikes.packMany(likes, user);
-});
diff --git a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts
deleted file mode 100644
index 2ecd47f1b..000000000
--- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { GalleryPosts } from '@/models/index.js';
-import define from '../../../define.js';
-import { makePaginationQuery } from '../../../common/make-pagination-query.js';
-
-export const meta = {
-	tags: ['account', 'gallery'],
-
-	requireCredential: true,
-
-	kind: 'read:gallery',
-
-	res: {
-		type: 'array',
-		optional: false, nullable: false,
-		items: {
-			type: 'object',
-			optional: false, nullable: false,
-			ref: 'GalleryPost',
-		},
-	},
-} as const;
-
-export const paramDef = {
-	type: 'object',
-	properties: {
-		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
-		sinceId: { type: 'string', format: 'misskey:id' },
-		untilId: { type: 'string', format: 'misskey:id' },
-	},
-	required: [],
-} as const;
-
-// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, user) => {
-	const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId)
-		.andWhere('post.userId = :meId', { meId: user.id });
-
-	const posts = await query
-		.take(ps.limit)
-		.getMany();
-
-	return await GalleryPosts.packMany(posts, user);
-});
diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts
deleted file mode 100644
index 8184c4ce4..000000000
--- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { GalleryPosts } from '@/models/index.js';
-import define from '../../../define.js';
-import { makePaginationQuery } from '../../../common/make-pagination-query.js';
-
-export const meta = {
-	tags: ['users', 'gallery'],
-
-	description: 'Show all gallery posts by the given user.',
-
-	res: {
-		type: 'array',
-		optional: false, nullable: false,
-		items: {
-			type: 'object',
-			optional: false, nullable: false,
-			ref: 'GalleryPost',
-		},
-	},
-} as const;
-
-export const paramDef = {
-	type: 'object',
-	properties: {
-		userId: { type: 'string', format: 'misskey:id' },
-		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
-		sinceId: { type: 'string', format: 'misskey:id' },
-		untilId: { type: 'string', format: 'misskey:id' },
-	},
-	required: ['userId'],
-} as const;
-
-// eslint-disable-next-line import/no-default-export
-export default define(meta, paramDef, async (ps, user) => {
-	const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId)
-		.andWhere('post.userId = :userId', { userId: ps.userId });
-
-	const posts = await query
-		.take(ps.limit)
-		.getMany();
-
-	return await GalleryPosts.packMany(posts, user);
-});
diff --git a/packages/backend/src/server/api/error.ts b/packages/backend/src/server/api/error.ts
index c015c7608..a98354df9 100644
--- a/packages/backend/src/server/api/error.ts
+++ b/packages/backend/src/server/api/error.ts
@@ -73,7 +73,7 @@ export const errors: Record<string, { message: string, httpStatusCode: number }>
 		httpStatusCode: 409,
 	},
 	ALREADY_LIKED: {
-		message: 'You already liked that page or gallery post.',
+		message: 'You already liked that page.',
 		httpStatusCode: 409,
 	},
 	ALREADY_MUTING: {
@@ -292,10 +292,6 @@ export const errors: Record<string, { message: string, httpStatusCode: number }>
 		message: 'No such parent folder.',
 		httpStatusCode: 404,
 	},
-	NO_SUCH_POST: {
-		message: 'No such gallery post.',
-		httpStatusCode: 404,
-	},
 	NO_SUCH_RESET_REQUEST: {
 		message: 'No such password reset request.',
 		httpStatusCode: 404,
@@ -337,7 +333,7 @@ export const errors: Record<string, { message: string, httpStatusCode: number }>
 		httpStatusCode: 409,
 	},
 	NOT_LIKED: {
-		message: 'You have not liked that page or gallery post.',
+		message: 'You have not liked that page.',
 		httpStatusCode: 409,
 	},
 	NOT_MUTING: {
diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts
index 3776c24f2..d126d2650 100644
--- a/packages/backend/src/server/web/index.ts
+++ b/packages/backend/src/server/web/index.ts
@@ -18,7 +18,7 @@ import { KoaAdapter } from '@bull-board/koa';
 import { In, IsNull } from 'typeorm';
 import { fetchMeta } from '@/misc/fetch-meta.js';
 import config from '@/config/index.js';
-import { Users, Notes, UserProfiles, Pages, Channels, Clips, GalleryPosts } from '@/models/index.js';
+import { Users, Notes, UserProfiles, Pages, Channels, Clips } from '@/models/index.js';
 import * as Acct from '@/misc/acct.js';
 import { getNoteSummary } from '@/misc/get-note-summary.js';
 import { queues } from '@/queue/queues.js';
@@ -421,31 +421,6 @@ router.get('/clips/:clip', async (ctx, next) => {
 	await next();
 });
 
-// Gallery post
-router.get('/gallery/:post', async (ctx, next) => {
-	const post = await GalleryPosts.findOneBy({ id: ctx.params.post });
-
-	if (post) {
-		const _post = await GalleryPosts.pack(post);
-		const profile = await UserProfiles.findOneByOrFail({ userId: post.userId });
-		const meta = await fetchMeta();
-		await ctx.render('gallery-post', {
-			post: _post,
-			profile,
-			avatarUrl: await Users.getAvatarUrl(await Users.findOneByOrFail({ id: post.userId })),
-			instanceName: meta.name || 'FoundKey',
-			icon: meta.iconUrl,
-			themeColor: meta.themeColor,
-		});
-
-		ctx.set('Cache-Control', 'public, max-age=15');
-
-		return;
-	}
-
-	await next();
-});
-
 // Channel
 router.get('/channels/:channel', async (ctx, next) => {
 	const channel = await Channels.findOneBy({
diff --git a/packages/backend/src/server/web/views/gallery-post.pug b/packages/backend/src/server/web/views/gallery-post.pug
deleted file mode 100644
index ca0663a48..000000000
--- a/packages/backend/src/server/web/views/gallery-post.pug
+++ /dev/null
@@ -1,33 +0,0 @@
-extends ./base
-
-block vars
-	- const user = post.user;
-	- const title = post.title;
-	- const url = `${config.url}/gallery/${post.id}`;
-
-block title
-	= `${title} | ${instanceName}`
-
-block desc
-	meta(name='description' content= post.description)
-
-block og
-	meta(property='og:type'        content='article')
-	meta(property='og:title'       content= title)
-	meta(property='og:description' content= post.description)
-	meta(property='og:url'         content= url)
-	meta(property='og:image'       content= post.files[0].thumbnailUrl)
-
-block meta
-	if user.host || profile.noCrawle
-		meta(name='robots' content='noindex')
-
-	meta(name='misskey:user-username' content=user.username)
-	meta(name='misskey:user-id' content=user.id)
-
-	// todo
-	if user.twitter
-		meta(name='twitter:creator' content=`@${user.twitter.screenName}`)
-
-	if !user.host
-		link(rel='alternate' href=url type='application/activity+json')

From c4b5952788976cc50ac0281e8f1f21c3264ccbd1 Mon Sep 17 00:00:00 2001
From: Johann150 <johann.galle@protonmail.com>
Date: Tue, 17 Jan 2023 21:17:00 +0100
Subject: [PATCH 4/4] migrate galleries to notes/clips

---
 .../migration/1673892262930-remove-groups.js  | 65 +++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 packages/backend/migration/1673892262930-remove-groups.js

diff --git a/packages/backend/migration/1673892262930-remove-groups.js b/packages/backend/migration/1673892262930-remove-groups.js
new file mode 100644
index 000000000..9779dd6e0
--- /dev/null
+++ b/packages/backend/migration/1673892262930-remove-groups.js
@@ -0,0 +1,65 @@
+import { genId } from '../built/misc/gen-id.js';
+
+export class removeGroups1673892262930 {
+	name = 'removeGroups1673892262930';
+
+	async up(queryRunner) {
+		// migrate gallery posts into notes, keeping the ids
+		await queryRunner.query(`
+			INSERT INTO "note" (
+				"id", "createdAt", "text", "cw", "userId", "visibility", "fileIds", "attachedFileTypes", "tags"
+			)
+			WITH "file_types" ("id", "types") AS (
+				SELECT "gallery_post"."id", ARRAY_AGG("drive_file"."type")
+				FROM "gallery_post"
+				JOIN "drive_file" ON "drive_file"."id" = ANY("gallery_post"."fileIds")
+				GROUP BY "gallery_post"."id"
+			)
+			SELECT "gallery_post"."id", "gallery_post"."createdAt",
+				CASE
+					WHEN "gallery_post"."title" IS NULL THEN "gallery_post"."description"
+					ELSE '<b>' || "gallery_post"."title" || E'</b>\\n\\n' ||  "gallery_post"."description"
+				END,
+				CASE
+					WHEN "gallery_post"."isSensitive" THEN 'NSFW'
+					ELSE NULL
+				END,
+				"gallery_post"."userId", 'home', "gallery_post"."fileIds", "file_types"."types", "gallery_post"."tags"
+			FROM "gallery_post"
+			JOIN "file_types" ON "gallery_post"."id" = "file_types"."id"
+		`);
+		// make a clip for each users gallery
+		await queryRunner.query(`SELECT DISTINCT "userId" FROM "gallery_post"`).then(userIds =>
+			Promise.all(userIds.map(({ userId }) => {
+				const clipId = genId();
+
+				// generate the clip itself
+				return queryRunner.query(`INSERT INTO "clip" ("id", "createdAt", "userId", "name", "isPublic") VALUES ($1, now(), $2, 'Gallery', true)`, [clipId, userId])
+					// and add all the previous gallery posts to it
+					// to not have to use genId for each gallery post, we just prepend a zero, something that could never be generated by genId
+					.then(() => queryRunner.query(`INSERT INTO "clip_note" ("id", "noteId", "clipId") SELECT '0' || "id", "id", $1 FROM "gallery_post" WHERE "userId" = $2`, [clipId, userId]));
+			}))
+		);
+
+		await queryRunner.query(`DROP TABLE "gallery_like"`);
+		await queryRunner.query(`DROP TABLE "gallery_post"`);
+	}
+
+	async down(queryRunner) {
+		// can only restore the table structure
+		await queryRunner.query(`CREATE TABLE "gallery_post" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "title" character varying(256) NOT NULL, "description" character varying(2048), "userId" character varying(32) NOT NULL, "fileIds" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "isSensitive" boolean NOT NULL DEFAULT false, "likedCount" integer NOT NULL DEFAULT '0', "tags" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], CONSTRAINT "PK_8e90d7b6015f2c4518881b14753" PRIMARY KEY ("id")); COMMENT ON COLUMN "gallery_post"."createdAt" IS 'The created date of the GalleryPost.'; COMMENT ON COLUMN "gallery_post"."updatedAt" IS 'The updated date of the GalleryPost.'; COMMENT ON COLUMN "gallery_post"."userId" IS 'The ID of author.'; COMMENT ON COLUMN "gallery_post"."isSensitive" IS 'Whether the post is sensitive.'`);
+		await queryRunner.query(`CREATE INDEX "IDX_8f1a239bd077c8864a20c62c2c" ON "gallery_post" ("createdAt") `);
+		await queryRunner.query(`CREATE INDEX "IDX_f631d37835adb04792e361807c" ON "gallery_post" ("updatedAt") `);
+		await queryRunner.query(`CREATE INDEX "IDX_985b836dddd8615e432d7043dd" ON "gallery_post" ("userId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_3ca50563facd913c425e7a89ee" ON "gallery_post" ("fileIds") `);
+		await queryRunner.query(`CREATE INDEX "IDX_f2d744d9a14d0dfb8b96cb7fc5" ON "gallery_post" ("isSensitive") `);
+		await queryRunner.query(`CREATE INDEX "IDX_1a165c68a49d08f11caffbd206" ON "gallery_post" ("likedCount") `);
+		await queryRunner.query(`CREATE INDEX "IDX_05cca34b985d1b8edc1d1e28df" ON "gallery_post" ("tags") `);
+		await queryRunner.query(`CREATE TABLE "gallery_like" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "postId" character varying(32) NOT NULL, CONSTRAINT "PK_853ab02be39b8de45cd720cc15f" PRIMARY KEY ("id"))`);
+		await queryRunner.query(`CREATE INDEX "IDX_8fd5215095473061855ceb948c" ON "gallery_like" ("userId") `);
+		await queryRunner.query(`CREATE UNIQUE INDEX "IDX_df1b5f4099e99fb0bc5eae53b6" ON "gallery_like" ("userId", "postId") `);
+		await queryRunner.query(`ALTER TABLE "gallery_post" ADD CONSTRAINT "FK_985b836dddd8615e432d7043ddb" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
+		await queryRunner.query(`ALTER TABLE "gallery_like" ADD CONSTRAINT "FK_8fd5215095473061855ceb948cf" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
+		await queryRunner.query(`ALTER TABLE "gallery_like" ADD CONSTRAINT "FK_b1cb568bfe569e47b7051699fc8" FOREIGN KEY ("postId") REFERENCES "gallery_post"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
+	}
+}