From 99dcd7bb27da3b12d51b685f9562a89b15cd138f Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 29 Jun 2022 23:28:52 +0900
Subject: feat(client): add instance-cloud widget

---
 packages/client/src/widgets/instance-cloud.vue | 80 ++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)
 create mode 100644 packages/client/src/widgets/instance-cloud.vue

(limited to 'packages/client/src/widgets/instance-cloud.vue')

diff --git a/packages/client/src/widgets/instance-cloud.vue b/packages/client/src/widgets/instance-cloud.vue
new file mode 100644
index 0000000000..aa76c37a00
--- /dev/null
+++ b/packages/client/src/widgets/instance-cloud.vue
@@ -0,0 +1,80 @@
+<template>
+<MkContainer :naked="widgetProps.transparent" :show-header="widgetProps.showHeader" class="mkw-instance-cloud">
+	<div class="">
+		<MkTagCloud v-if="activeInstances">
+			<li v-for="instance in activeInstances">
+				<a @click.prevent="onInstanceClick(instance)">
+					<img style="width: 32px;" :src="instance.iconUrl">
+				</a>
+			</li>
+		</MkTagCloud>
+	</div>
+</MkContainer>
+</template>
+
+<script lang="ts" setup>
+import { } from 'vue';
+import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { GetFormResultType } from '@/scripts/form';
+import MkContainer from '@/components/ui/container.vue';
+import MkTagCloud from '@/components/tag-cloud.vue';
+import * as os from '@/os';
+import { useInterval } from '@/scripts/use-interval';
+
+const name = 'instanceCloud';
+
+const widgetPropsDef = {
+	transparent: {
+		type: 'boolean' as const,
+		default: false,
+	},
+	showHeader: {
+		type: 'boolean' as const,
+		default: true,
+	},
+};
+
+type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
+
+// 現時点ではvueの制限によりimportしたtypeをジェネリックに渡せない
+//const props = defineProps<WidgetComponentProps<WidgetProps>>();
+//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
+const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
+
+const { widgetProps, configure } = useWidgetPropsManager(name,
+	widgetPropsDef,
+	props,
+	emit,
+);
+
+let cloud = $ref<InstanceType<typeof MkTagCloud> | null>();
+let activeInstances = $shallowRef(null);
+
+function onInstanceClick(i) {
+	os.pageWindow(`/instance-info/${i.host}`);
+}
+
+useInterval(() => {
+	os.api('federation/instances', {
+		sort: '+lastCommunicatedAt',
+		limit: 25,
+	}).then(res => {
+		activeInstances = res;
+		if (cloud) cloud.update();
+	});
+}, 1000 * 60 * 3, {
+	immediate: true,
+	afterMounted: true,
+});
+
+defineExpose<WidgetComponentExpose>({
+	name,
+	configure,
+	id: props.widget ? props.widget.id : null,
+});
+</script>
+
+<style lang="scss" scoped>
+
+</style>
-- 
cgit v1.2.3-freya


From 9ac526b6b6d66ceaceda6ef0d3662fd8484510da Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 30 Jun 2022 10:13:27 +0900
Subject: tweak client

---
 packages/client/src/components/tag-cloud.vue   | 14 ++++++++++++--
 packages/client/src/ui/visitor/b.vue           |  4 ++++
 packages/client/src/widgets/instance-cloud.vue |  6 +-----
 3 files changed, 17 insertions(+), 7 deletions(-)

(limited to 'packages/client/src/widgets/instance-cloud.vue')

diff --git a/packages/client/src/components/tag-cloud.vue b/packages/client/src/components/tag-cloud.vue
index 43ab49357b..8df8d0b05b 100644
--- a/packages/client/src/components/tag-cloud.vue
+++ b/packages/client/src/components/tag-cloud.vue
@@ -1,6 +1,6 @@
 <template>
-<div class="root">
-	<canvas :id="idForCanvas" ref="canvasEl" class="canvas" width="300" height="300"></canvas>
+<div ref="rootEl" class="root">
+	<canvas :id="idForCanvas" ref="canvasEl" class="canvas" :width="width" height="300" @contextmenu.prevent="() => {}"></canvas>
 	<div :id="idForTags" ref="tagsEl" class="tags">
 		<ul>
 			<slot></slot>
@@ -21,8 +21,10 @@ const computedStyle = getComputedStyle(document.documentElement);
 const idForCanvas = Array.from(Array(16)).map(() => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
 const idForTags = Array.from(Array(16)).map(() => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
 let available = $ref(false);
+let rootEl = $ref<HTMLElement | null>(null);
 let canvasEl = $ref<HTMLCanvasElement | null>(null);
 let tagsEl = $ref<HTMLElement | null>(null);
+let width = $ref(300);
 
 watch($$(available), () => {
 	window.TagCanvas.Start(idForCanvas, idForTags, {
@@ -45,6 +47,8 @@ watch($$(available), () => {
 });
 
 onMounted(() => {
+	width = rootEl.offsetWidth;
+
 	if (loaded) {
 		available = true;
 	} else {
@@ -58,6 +62,12 @@ onMounted(() => {
 onBeforeUnmount(() => {
 	window.TagCanvas.Delete(idForCanvas);
 });
+
+defineExpose({
+	update: () => {
+		window.TagCanvas.Update(idForCanvas);
+	},
+});
 </script>
 
 <style lang="scss" scoped>
diff --git a/packages/client/src/ui/visitor/b.vue b/packages/client/src/ui/visitor/b.vue
index 28933f272a..09be12d689 100644
--- a/packages/client/src/ui/visitor/b.vue
+++ b/packages/client/src/ui/visitor/b.vue
@@ -116,6 +116,10 @@ onMounted(() => {
 		}, { passive: true });
 	}
 });
+
+defineExpose({
+	showMenu: $$(showMenu),
+});
 </script>
 
 <style>
diff --git a/packages/client/src/widgets/instance-cloud.vue b/packages/client/src/widgets/instance-cloud.vue
index aa76c37a00..cb66c5fa36 100644
--- a/packages/client/src/widgets/instance-cloud.vue
+++ b/packages/client/src/widgets/instance-cloud.vue
@@ -1,5 +1,5 @@
 <template>
-<MkContainer :naked="widgetProps.transparent" :show-header="widgetProps.showHeader" class="mkw-instance-cloud">
+<MkContainer :naked="widgetProps.transparent" class="mkw-instance-cloud">
 	<div class="">
 		<MkTagCloud v-if="activeInstances">
 			<li v-for="instance in activeInstances">
@@ -28,10 +28,6 @@ const widgetPropsDef = {
 		type: 'boolean' as const,
 		default: false,
 	},
-	showHeader: {
-		type: 'boolean' as const,
-		default: true,
-	},
 };
 
 type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
-- 
cgit v1.2.3-freya


From bffe6fb9bfa1fe61eab4f03bd318c010644d7afb Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 30 Jun 2022 10:53:40 +0900
Subject: tweak client

---
 locales/ja-JP.yml                                  |   2 +
 packages/client/src/components/form/group.vue      |  36 ---
 packages/client/src/components/form/range.vue      |  17 +-
 .../client/src/components/global/router-view.vue   |   3 +-
 packages/client/src/menu.ts                        |   2 +
 packages/client/src/pages/admin/object-storage.vue |   1 -
 packages/client/src/pages/gallery/edit.vue         |   7 +-
 packages/client/src/pages/settings/deck.vue        |   6 +-
 packages/client/src/pages/settings/general.vue     |  12 +-
 packages/client/src/scripts/get-note-menu.ts       | 248 ++++++++++-----------
 packages/client/src/store.ts                       |   4 +
 packages/client/src/widgets/instance-cloud.vue     |   2 +-
 12 files changed, 156 insertions(+), 184 deletions(-)
 delete mode 100644 packages/client/src/components/form/group.vue

(limited to 'packages/client/src/widgets/instance-cloud.vue')

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 9de5b99d17..17de04ebc8 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -858,6 +858,8 @@ isSystemAccount: "システムにより自動で作成・管理されている
 typeToConfirm: "この操作を行うには {x} と入力してください"
 deleteAccount: "アカウント削除"
 document: "ドキュメント"
+numberOfPageCache: "ページキャッシュ数"
+numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。"
 
 _emailUnavailable:
   used: "既に使用されています"
diff --git a/packages/client/src/components/form/group.vue b/packages/client/src/components/form/group.vue
deleted file mode 100644
index 1e8376ca44..0000000000
--- a/packages/client/src/components/form/group.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-<template>
-<div v-sticky-container class="adfeebaf _formBlock">
-	<div class="label"><slot name="label"></slot></div>
-	<div class="main _formRoot">
-		<slot></slot>
-	</div>
-</div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-
-export default defineComponent({
-});
-</script>
-
-<style lang="scss" scoped>
-.adfeebaf {
-	padding: 24px 24px;
-	border: solid 1px var(--divider);
-	border-radius: var(--radius);
-
-	> .label {
-		font-weight: bold;
-		padding: 0 0 16px 0;
-
-		&:empty {
-			display: none;
-		}
-	}
-
-	> .main {
-
-	}
-}
-</style>
diff --git a/packages/client/src/components/form/range.vue b/packages/client/src/components/form/range.vue
index ac4a781e32..9bb0164a2e 100644
--- a/packages/client/src/components/form/range.vue
+++ b/packages/client/src/components/form/range.vue
@@ -4,7 +4,7 @@
 	<div v-adaptive-border class="body">
 		<div ref="containerEl" class="container">
 			<div class="track">
-				<div class="highlight" :style="{ width: (steppedValue * 100) + '%' }"></div>
+				<div class="highlight" :style="{ width: (steppedRawValue * 100) + '%' }"></div>
 			</div>
 			<div v-if="steps" class="ticks">
 				<div v-for="i in (steps + 1)" class="tick" :style="{ left: (((i - 1) / steps) * 100) + '%' }"></div>
@@ -12,6 +12,7 @@
 			<div ref="thumbEl" v-tooltip="textConverter(finalValue)" class="thumb" :style="{ left: thumbPosition + 'px' }" @mousedown="onMousedown" @touchstart="onMousedown"></div>
 		</div>
 	</div>
+	<div class="caption"><slot name="caption"></slot></div>
 </div>
 </template>
 
@@ -62,7 +63,7 @@ export default defineComponent({
 		const thumbEl = ref<HTMLElement>();
 
 		const rawValue = ref((props.modelValue - props.min) / (props.max - props.min));
-		const steppedValue = computed(() => {
+		const steppedRawValue = computed(() => {
 			if (props.step) {
 				const step = props.step / (props.max - props.min);
 				return (step * Math.round(rawValue.value / step));
@@ -71,7 +72,11 @@ export default defineComponent({
 			}
 		});
 		const finalValue = computed(() => {
-			return (steppedValue.value * (props.max - props.min)) + props.min;
+			if (Number.isInteger(props.step)) {
+				return Math.round((steppedRawValue.value * (props.max - props.min)) + props.min);
+			} else {
+				return (steppedRawValue.value * (props.max - props.min)) + props.min;
+			}
 		});
 		watch(finalValue, () => {
 			context.emit('update:modelValue', finalValue.value);
@@ -86,10 +91,10 @@ export default defineComponent({
 			if (containerEl.value == null) {
 				thumbPosition.value = 0;
 			} else {
-				thumbPosition.value = (containerEl.value.offsetWidth - thumbWidth.value) * steppedValue.value;
+				thumbPosition.value = (containerEl.value.offsetWidth - thumbWidth.value) * steppedRawValue.value;
 			}
 		};
-		watch([steppedValue, containerEl], calcThumbPosition);
+		watch([steppedRawValue, containerEl], calcThumbPosition);
 
 		let ro: ResizeObserver | undefined;
 
@@ -154,7 +159,7 @@ export default defineComponent({
 		return {
 			rawValue,
 			finalValue,
-			steppedValue,
+			steppedRawValue,
 			onMousedown,
 			containerEl,
 			thumbEl,
diff --git a/packages/client/src/components/global/router-view.vue b/packages/client/src/components/global/router-view.vue
index 56b53e0127..7138faaa9d 100644
--- a/packages/client/src/components/global/router-view.vue
+++ b/packages/client/src/components/global/router-view.vue
@@ -1,5 +1,5 @@
 <template>
-<KeepAlive max="5">
+<KeepAlive :max="defaultStore.state.numberOfPageCache">
 	<component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/>
 </KeepAlive>
 </template>
@@ -7,6 +7,7 @@
 <script lang="ts" setup>
 import { inject, nextTick, onMounted, onUnmounted, watch } from 'vue';
 import { Router } from '@/nirax';
+import { defaultStore } from '@/store';
 
 const props = defineProps<{
 	router?: Router;
diff --git a/packages/client/src/menu.ts b/packages/client/src/menu.ts
index 2c0126eb84..72e3951602 100644
--- a/packages/client/src/menu.ts
+++ b/packages/client/src/menu.ts
@@ -81,12 +81,14 @@ export const menuDef = reactive({
 			os.popupMenu(items, ev.currentTarget ?? ev.target);
 		},
 	},
+	/*
 	groups: {
 		title: 'groups',
 		icon: 'fas fa-users',
 		show: computed(() => $i != null),
 		to: '/my/groups',
 	},
+	*/
 	antennas: {
 		title: 'antennas',
 		icon: 'fas fa-satellite',
diff --git a/packages/client/src/pages/admin/object-storage.vue b/packages/client/src/pages/admin/object-storage.vue
index bae5277f49..450fd134e1 100644
--- a/packages/client/src/pages/admin/object-storage.vue
+++ b/packages/client/src/pages/admin/object-storage.vue
@@ -73,7 +73,6 @@ import { } from 'vue';
 import XHeader from './_header_.vue';
 import FormSwitch from '@/components/form/switch.vue';
 import FormInput from '@/components/form/input.vue';
-import FormGroup from '@/components/form/group.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import FormSplit from '@/components/form/split.vue';
 import FormSection from '@/components/form/section.vue';
diff --git a/packages/client/src/pages/gallery/edit.vue b/packages/client/src/pages/gallery/edit.vue
index 6d1140ba3a..fa3063bde0 100644
--- a/packages/client/src/pages/gallery/edit.vue
+++ b/packages/client/src/pages/gallery/edit.vue
@@ -9,13 +9,13 @@
 			<template #label>{{ $ts.description }}</template>
 		</FormTextarea>
 
-		<FormGroup>
-			<div v-for="file in files" :key="file.id" class="_formGroup wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }">
+		<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="$ts.remove" class="remove _button" @click="remove(file)"><i class="fas fa-times"></i></button>
 			</div>
 			<FormButton primary @click="selectFile"><i class="fas fa-plus"></i> {{ $ts.attachFile }}</FormButton>
-		</FormGroup>
+		</div>
 
 		<FormSwitch v-model="isSensitive">{{ $ts.markAsSensitive }}</FormSwitch>
 
@@ -33,7 +33,6 @@ import FormButton 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 FormGroup from '@/components/form/group.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import { selectFiles } from '@/scripts/select-file';
 import * as os from '@/os';
diff --git a/packages/client/src/pages/settings/deck.vue b/packages/client/src/pages/settings/deck.vue
index edada683ae..295357377e 100644
--- a/packages/client/src/pages/settings/deck.vue
+++ b/packages/client/src/pages/settings/deck.vue
@@ -1,9 +1,6 @@
 <template>
 <div class="_formRoot">
-	<FormGroup>
-		<template #label>{{ i18n.ts.defaultNavigationBehaviour }}</template>
-		<FormSwitch v-model="navWindow">{{ i18n.ts.openInWindow }}</FormSwitch>
-	</FormGroup>
+	<FormSwitch v-model="navWindow">{{ i18n.ts.defaultNavigationBehaviour }}: {{ i18n.ts.openInWindow }}</FormSwitch>
 
 	<FormSwitch v-model="alwaysShowMainColumn" class="_formBlock">{{ i18n.ts._deck.alwaysShowMainColumn }}</FormSwitch>
 
@@ -35,7 +32,6 @@ import FormSwitch from '@/components/form/switch.vue';
 import FormLink from '@/components/form/link.vue';
 import FormRadios from '@/components/form/radios.vue';
 import FormInput from '@/components/form/input.vue';
-import FormGroup from '@/components/form/group.vue';
 import { deckStore } from '@/ui/deck/deck-store';
 import * as os from '@/os';
 import { unisonReload } from '@/scripts/unison-reload';
diff --git a/packages/client/src/pages/settings/general.vue b/packages/client/src/pages/settings/general.vue
index ac2e3a4968..e7339af14e 100644
--- a/packages/client/src/pages/settings/general.vue
+++ b/packages/client/src/pages/settings/general.vue
@@ -81,10 +81,10 @@
 		<option value="force">{{ i18n.ts._nsfw.force }}</option>
 	</FormSelect>
 
-	<FormGroup>
-		<template #label>{{ i18n.ts.defaultNavigationBehaviour }}</template>
-		<FormSwitch v-model="defaultSideView">{{ i18n.ts.openInSideView }}</FormSwitch>
-	</FormGroup>
+	<FormRange v-model="numberOfPageCache" :min="1" :max="10" :step="1" class="_formBlock">
+		<template #label>{{ i18n.ts.numberOfPageCache }}</template>
+		<template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template>
+	</FormRange>
 
 	<FormLink to="/settings/deck" class="_formBlock">{{ i18n.ts.deck }}</FormLink>
 
@@ -97,7 +97,7 @@ import { computed, ref, watch } from 'vue';
 import FormSwitch from '@/components/form/switch.vue';
 import FormSelect from '@/components/form/select.vue';
 import FormRadios from '@/components/form/radios.vue';
-import FormGroup from '@/components/form/group.vue';
+import FormRange from '@/components/form/range.vue';
 import FormSection from '@/components/form/section.vue';
 import FormLink from '@/components/form/link.vue';
 import MkLink from '@/components/link.vue';
@@ -137,7 +137,7 @@ const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab'));
 const nsfw = computed(defaultStore.makeGetterSetter('nsfw'));
 const disablePagesScript = computed(defaultStore.makeGetterSetter('disablePagesScript'));
 const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm'));
-const defaultSideView = computed(defaultStore.makeGetterSetter('defaultSideView'));
+const numberOfPageCache = computed(defaultStore.makeGetterSetter('numberOfPageCache'));
 const instanceTicker = computed(defaultStore.makeGetterSetter('instanceTicker'));
 const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfiniteScroll'));
 const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu'));
diff --git a/packages/client/src/scripts/get-note-menu.ts b/packages/client/src/scripts/get-note-menu.ts
index 283c90362c..632143f514 100644
--- a/packages/client/src/scripts/get-note-menu.ts
+++ b/packages/client/src/scripts/get-note-menu.ts
@@ -1,5 +1,6 @@
 import { defineAsyncComponent, Ref, inject } from 'vue';
 import * as misskey from 'misskey-js';
+import { pleaseLogin } from './please-login';
 import { $i } from '@/account';
 import { i18n } from '@/i18n';
 import { instance } from '@/instance';
@@ -7,7 +8,6 @@ import * as os from '@/os';
 import copyToClipboard from '@/scripts/copy-to-clipboard';
 import { url } from '@/config';
 import { noteActions } from '@/store';
-import { pleaseLogin } from './please-login';
 
 export function getNoteMenu(props: {
 	note: misskey.entities.Note;
@@ -34,7 +34,7 @@ export function getNoteMenu(props: {
 			if (canceled) return;
 
 			os.api('notes/delete', {
-				noteId: appearNote.id
+				noteId: appearNote.id,
 			});
 		});
 	}
@@ -47,7 +47,7 @@ export function getNoteMenu(props: {
 			if (canceled) return;
 
 			os.api('notes/delete', {
-				noteId: appearNote.id
+				noteId: appearNote.id,
 			});
 
 			os.post({ initialNote: appearNote, renote: appearNote.renote, reply: appearNote.reply, channel: appearNote.channel });
@@ -56,19 +56,19 @@ export function getNoteMenu(props: {
 
 	function toggleFavorite(favorite: boolean): void {
 		os.apiWithDialog(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', {
-			noteId: appearNote.id
+			noteId: appearNote.id,
 		});
 	}
 
 	function toggleWatch(watch: boolean): void {
 		os.apiWithDialog(watch ? 'notes/watching/create' : 'notes/watching/delete', {
-			noteId: appearNote.id
+			noteId: appearNote.id,
 		});
 	}
 
 	function toggleThreadMute(mute: boolean): void {
 		os.apiWithDialog(mute ? 'notes/thread-muting/create' : 'notes/thread-muting/delete', {
-			noteId: appearNote.id
+			noteId: appearNote.id,
 		});
 	}
 
@@ -84,12 +84,12 @@ export function getNoteMenu(props: {
 
 	function togglePin(pin: boolean): void {
 		os.apiWithDialog(pin ? 'i/pin' : 'i/unpin', {
-			noteId: appearNote.id
+			noteId: appearNote.id,
 		}, undefined, null, res => {
 			if (res.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
 				os.alert({
 					type: 'error',
-					text: i18n.ts.pinLimitExceeded
+					text: i18n.ts.pinLimitExceeded,
 				});
 			}
 		});
@@ -104,26 +104,26 @@ export function getNoteMenu(props: {
 				const { canceled, result } = await os.form(i18n.ts.createNewClip, {
 					name: {
 						type: 'string',
-						label: i18n.ts.name
+						label: i18n.ts.name,
 					},
 					description: {
 						type: 'string',
 						required: false,
 						multiline: true,
-						label: i18n.ts.description
+						label: i18n.ts.description,
 					},
 					isPublic: {
 						type: 'boolean',
 						label: i18n.ts.public,
-						default: false
-					}
+						default: false,
+					},
 				});
 				if (canceled) return;
 
 				const clip = await os.apiWithDialog('clips/create', result);
 
 				os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id });
-			}
+			},
 		}, null, ...clips.map(clip => ({
 			text: clip.name,
 			action: () => {
@@ -146,9 +146,9 @@ export function getNoteMenu(props: {
 								text: err.message + '\n' + err.id,
 							});
 						}
-					}
+					},
 				);
-			}
+			},
 		}))], props.menuButton.value, {
 		}).then(focus);
 	}
@@ -193,86 +193,86 @@ export function getNoteMenu(props: {
 	let menu;
 	if ($i) {
 		const statePromise = os.api('notes/state', {
-			noteId: appearNote.id
+			noteId: appearNote.id,
 		});
 
 		menu = [
-		...(
-			props.currentClipPage?.value.userId === $i.id ? [{
-				icon: 'fas fa-circle-minus',
-				text: i18n.ts.unclip,
-				danger: true,
-				action: unclip,
-			}, null] : []
-		),
-		{
-			icon: 'fas fa-copy',
-			text: i18n.ts.copyContent,
-			action: copyContent
-		}, {
-			icon: 'fas fa-link',
-			text: i18n.ts.copyLink,
-			action: copyLink
-		}, (appearNote.url || appearNote.uri) ? {
-			icon: 'fas fa-external-link-square-alt',
-			text: i18n.ts.showOnRemote,
-			action: () => {
-				window.open(appearNote.url || appearNote.uri, '_blank');
-			}
-		} : undefined,
-		{
-			icon: 'fas fa-share-alt',
-			text: i18n.ts.share,
-			action: share
-		},
-		instance.translatorAvailable ? {
-			icon: 'fas fa-language',
-			text: i18n.ts.translate,
-			action: translate
-		} : undefined,
-		null,
-		statePromise.then(state => state.isFavorited ? {
-			icon: 'fas fa-star',
-			text: i18n.ts.unfavorite,
-			action: () => toggleFavorite(false)
-		} : {
-			icon: 'fas fa-star',
-			text: i18n.ts.favorite,
-			action: () => toggleFavorite(true)
-		}),
-		{
-			icon: 'fas fa-paperclip',
-			text: i18n.ts.clip,
-			action: () => clip()
-		},
-		(appearNote.userId !== $i.id) ? statePromise.then(state => state.isWatching ? {
-			icon: 'fas fa-eye-slash',
-			text: i18n.ts.unwatch,
-			action: () => toggleWatch(false)
-		} : {
-			icon: 'fas fa-eye',
-			text: i18n.ts.watch,
-			action: () => toggleWatch(true)
-		}) : undefined,
-		statePromise.then(state => state.isMutedThread ? {
-			icon: 'fas fa-comment-slash',
-			text: i18n.ts.unmuteThread,
-			action: () => toggleThreadMute(false)
-		} : {
-			icon: 'fas fa-comment-slash',
-			text: i18n.ts.muteThread,
-			action: () => toggleThreadMute(true)
-		}),
-		appearNote.userId === $i.id ? ($i.pinnedNoteIds || []).includes(appearNote.id) ? {
-			icon: 'fas fa-thumbtack',
-			text: i18n.ts.unpin,
-			action: () => togglePin(false)
-		} : {
-			icon: 'fas fa-thumbtack',
-			text: i18n.ts.pin,
-			action: () => togglePin(true)
-		} : undefined,
-		/*
+			...(
+				props.currentClipPage?.value.userId === $i.id ? [{
+					icon: 'fas fa-circle-minus',
+					text: i18n.ts.unclip,
+					danger: true,
+					action: unclip,
+				}, null] : []
+			),
+			{
+				icon: 'fas fa-copy',
+				text: i18n.ts.copyContent,
+				action: copyContent,
+			}, {
+				icon: 'fas fa-link',
+				text: i18n.ts.copyLink,
+				action: copyLink,
+			}, (appearNote.url || appearNote.uri) ? {
+				icon: 'fas fa-external-link-square-alt',
+				text: i18n.ts.showOnRemote,
+				action: () => {
+					window.open(appearNote.url || appearNote.uri, '_blank');
+				},
+			} : undefined,
+			{
+				icon: 'fas fa-share-alt',
+				text: i18n.ts.share,
+				action: share,
+			},
+			instance.translatorAvailable ? {
+				icon: 'fas fa-language',
+				text: i18n.ts.translate,
+				action: translate,
+			} : undefined,
+			null,
+			statePromise.then(state => state.isFavorited ? {
+				icon: 'fas fa-star',
+				text: i18n.ts.unfavorite,
+				action: () => toggleFavorite(false),
+			} : {
+				icon: 'fas fa-star',
+				text: i18n.ts.favorite,
+				action: () => toggleFavorite(true),
+			}),
+			{
+				icon: 'fas fa-paperclip',
+				text: i18n.ts.clip,
+				action: () => clip(),
+			},
+			(appearNote.userId !== $i.id) ? statePromise.then(state => state.isWatching ? {
+				icon: 'fas fa-eye-slash',
+				text: i18n.ts.unwatch,
+				action: () => toggleWatch(false),
+			} : {
+				icon: 'fas fa-eye',
+				text: i18n.ts.watch,
+				action: () => toggleWatch(true),
+			}) : undefined,
+			statePromise.then(state => state.isMutedThread ? {
+				icon: 'fas fa-comment-slash',
+				text: i18n.ts.unmuteThread,
+				action: () => toggleThreadMute(false),
+			} : {
+				icon: 'fas fa-comment-slash',
+				text: i18n.ts.muteThread,
+				action: () => toggleThreadMute(true),
+			}),
+			appearNote.userId === $i.id ? ($i.pinnedNoteIds || []).includes(appearNote.id) ? {
+				icon: 'fas fa-thumbtack',
+				text: i18n.ts.unpin,
+				action: () => togglePin(false),
+			} : {
+				icon: 'fas fa-thumbtack',
+				text: i18n.ts.pin,
+				action: () => togglePin(true),
+			} : undefined,
+			/*
 		...($i.isModerator || $i.isAdmin ? [
 			null,
 			{
@@ -282,52 +282,52 @@ export function getNoteMenu(props: {
 			}]
 			: []
 		),*/
-		...(appearNote.userId !== $i.id ? [
-			null,
-			{
-				icon: 'fas fa-exclamation-circle',
-				text: i18n.ts.reportAbuse,
-				action: () => {
-					const u = appearNote.url || appearNote.uri || `${url}/notes/${appearNote.id}`;
-					os.popup(defineAsyncComponent(() => import('@/components/abuse-report-window.vue')), {
-						user: appearNote.user,
-						initialComment: `Note: ${u}\n-----\n`
-					}, {}, 'closed');
-				}
-			}]
+			...(appearNote.userId !== $i.id ? [
+				null,
+				{
+					icon: 'fas fa-exclamation-circle',
+					text: i18n.ts.reportAbuse,
+					action: () => {
+						const u = appearNote.url || appearNote.uri || `${url}/notes/${appearNote.id}`;
+						os.popup(defineAsyncComponent(() => import('@/components/abuse-report-window.vue')), {
+							user: appearNote.user,
+							initialComment: `Note: ${u}\n-----\n`,
+						}, {}, 'closed');
+					},
+				}]
 			: []
-		),
-		...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [
-			null,
-			appearNote.userId === $i.id ? {
-				icon: 'fas fa-edit',
-				text: i18n.ts.deleteAndEdit,
-				action: delEdit
-			} : undefined,
-			{
-				icon: 'fas fa-trash-alt',
-				text: i18n.ts.delete,
-				danger: true,
-				action: del
-			}]
+			),
+			...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [
+				null,
+				appearNote.userId === $i.id ? {
+					icon: 'fas fa-edit',
+					text: i18n.ts.deleteAndEdit,
+					action: delEdit,
+				} : undefined,
+				{
+					icon: 'fas fa-trash-alt',
+					text: i18n.ts.delete,
+					danger: true,
+					action: del,
+				}]
 			: []
-		)]
+			)]
 		.filter(x => x !== undefined);
 	} else {
 		menu = [{
 			icon: 'fas fa-copy',
 			text: i18n.ts.copyContent,
-			action: copyContent
+			action: copyContent,
 		}, {
 			icon: 'fas fa-link',
 			text: i18n.ts.copyLink,
-			action: copyLink
+			action: copyLink,
 		}, (appearNote.url || appearNote.uri) ? {
 			icon: 'fas fa-external-link-square-alt',
 			text: i18n.ts.showOnRemote,
 			action: () => {
 				window.open(appearNote.url || appearNote.uri, '_blank');
-			}
+			},
 		} : undefined]
 		.filter(x => x !== undefined);
 	}
@@ -338,7 +338,7 @@ export function getNoteMenu(props: {
 			text: action.title,
 			action: () => {
 				action.handler(appearNote);
-			}
+			},
 		}))]);
 	}
 
diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts
index a273a46c7c..94d9d91385 100644
--- a/packages/client/src/store.ts
+++ b/packages/client/src/store.ts
@@ -233,6 +233,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: true,
 	},
+	numberOfPageCache: {
+		where: 'device',
+		default: 5,
+	},
 	aiChanMode: {
 		where: 'device',
 		default: false,
diff --git a/packages/client/src/widgets/instance-cloud.vue b/packages/client/src/widgets/instance-cloud.vue
index cb66c5fa36..7dc4b4ce91 100644
--- a/packages/client/src/widgets/instance-cloud.vue
+++ b/packages/client/src/widgets/instance-cloud.vue
@@ -1,5 +1,5 @@
 <template>
-<MkContainer :naked="widgetProps.transparent" class="mkw-instance-cloud">
+<MkContainer :naked="widgetProps.transparent" :show-header="false" class="mkw-instance-cloud">
 	<div class="">
 		<MkTagCloud v-if="activeInstances">
 			<li v-for="instance in activeInstances">
-- 
cgit v1.2.3-freya


From c0eba655274c195f809438c2e23f95b4c3f87691 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 7 Jul 2022 20:19:50 +0900
Subject: chore(client): tweak ui

---
 packages/client/src/pages/follow-requests.vue  | 57 ++++++++++++++------------
 packages/client/src/ui/_common_/statusbars.vue |  4 ++
 packages/client/src/widgets/instance-cloud.vue |  2 +-
 3 files changed, 35 insertions(+), 28 deletions(-)

(limited to 'packages/client/src/widgets/instance-cloud.vue')

diff --git a/packages/client/src/pages/follow-requests.vue b/packages/client/src/pages/follow-requests.vue
index f21c68c4c7..1f4dc9e938 100644
--- a/packages/client/src/pages/follow-requests.vue
+++ b/packages/client/src/pages/follow-requests.vue
@@ -1,34 +1,37 @@
 <template>
-<div>
-	<MkPagination ref="paginationComponent" :pagination="pagination">
-		<template #empty>
-			<div class="_fullinfo">
-				<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
-				<div>{{ $ts.noFollowRequests }}</div>
-			</div>
-		</template>
-		<template #default="{items}">
-			<div class="mk-follow-requests">
-				<div v-for="req in items" :key="req.id" class="user _panel">
-					<MkAvatar class="avatar" :user="req.follower" :show-indicator="true"/>
-					<div class="body">
-						<div class="name">
-							<MkA v-user-preview="req.follower.id" class="name" :to="userPage(req.follower)"><MkUserName :user="req.follower"/></MkA>
-							<p class="acct">@{{ acct(req.follower) }}</p>
-						</div>
-						<div v-if="req.follower.description" class="description" :title="req.follower.description">
-							<Mfm :text="req.follower.description" :is-note="false" :author="req.follower" :i="$i" :custom-emojis="req.follower.emojis" :plain="true" :nowrap="true"/>
-						</div>
-						<div class="actions">
-							<button class="_button" @click="accept(req.follower)"><i class="fas fa-check"></i></button>
-							<button class="_button" @click="reject(req.follower)"><i class="fas fa-times"></i></button>
+<MkStickyContainer>
+	<template #header><MkPageHeader/></template>
+	<MkSpacer :content-max="800">
+		<MkPagination ref="paginationComponent" :pagination="pagination">
+			<template #empty>
+				<div class="_fullinfo">
+					<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+					<div>{{ $ts.noFollowRequests }}</div>
+				</div>
+			</template>
+			<template #default="{items}">
+				<div class="mk-follow-requests">
+					<div v-for="req in items" :key="req.id" class="user _panel">
+						<MkAvatar class="avatar" :user="req.follower" :show-indicator="true"/>
+						<div class="body">
+							<div class="name">
+								<MkA v-user-preview="req.follower.id" class="name" :to="userPage(req.follower)"><MkUserName :user="req.follower"/></MkA>
+								<p class="acct">@{{ acct(req.follower) }}</p>
+							</div>
+							<div v-if="req.follower.description" class="description" :title="req.follower.description">
+								<Mfm :text="req.follower.description" :is-note="false" :author="req.follower" :i="$i" :custom-emojis="req.follower.emojis" :plain="true" :nowrap="true"/>
+							</div>
+							<div class="actions">
+								<button class="_button" @click="accept(req.follower)"><i class="fas fa-check"></i></button>
+								<button class="_button" @click="reject(req.follower)"><i class="fas fa-times"></i></button>
+							</div>
 						</div>
 					</div>
 				</div>
-			</div>
-		</template>
-	</MkPagination>
-</div>
+			</template>
+		</MkPagination>
+	</MkSpacer>
+</MkStickyContainer>
 </template>
 
 <script lang="ts" setup>
diff --git a/packages/client/src/ui/_common_/statusbars.vue b/packages/client/src/ui/_common_/statusbars.vue
index c448b09563..80949665b3 100644
--- a/packages/client/src/ui/_common_/statusbars.vue
+++ b/packages/client/src/ui/_common_/statusbars.vue
@@ -71,6 +71,10 @@ const XUserList = defineAsyncComponent(() => import('./statusbar-user-list.vue')
 			padding: 0 var(--nameMargin);
 			font-weight: bold;
 			color: var(--accent);
+
+			&:empty {
+				display: none;
+			}
 		}
 
 		> .body {
diff --git a/packages/client/src/widgets/instance-cloud.vue b/packages/client/src/widgets/instance-cloud.vue
index 7dc4b4ce91..597ce0e824 100644
--- a/packages/client/src/widgets/instance-cloud.vue
+++ b/packages/client/src/widgets/instance-cloud.vue
@@ -2,7 +2,7 @@
 <MkContainer :naked="widgetProps.transparent" :show-header="false" class="mkw-instance-cloud">
 	<div class="">
 		<MkTagCloud v-if="activeInstances">
-			<li v-for="instance in activeInstances">
+			<li v-for="instance in activeInstances" :key="instance.id">
 				<a @click.prevent="onInstanceClick(instance)">
 					<img style="width: 32px;" :src="instance.iconUrl">
 				</a>
-- 
cgit v1.2.3-freya

