Storage improve (#6976)
* wip
* wip
* wip
* wip
* wip
* Update storage.ts
* wip
* wip
* wip
* wip
* Update storage.ts
* Update storage.ts
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Update storage.ts
* wip
* wip
* wip
* wip
* 🍕
* wip
* wip
* wip
* wip
* wip
* wip
* Update deck-storage.ts
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Update store.ts
* wip
* wip
* wip
* wip
* Update init.ts
* wip
* wip
* Update pizzax.ts
* wip
* wip
* Update timeline.vue
* Update init.ts
* wip
* wip
* Update init.ts
			
			
This commit is contained in:
		
							parent
							
								
									57d0c19a98
								
							
						
					
					
						commit
						43930e6a84
					
				
					 146 changed files with 1458 additions and 1519 deletions
				
			
		|  | @ -251,8 +251,6 @@ | ||||||
| 		"vue-router": "4.0.1", | 		"vue-router": "4.0.1", | ||||||
| 		"vue-style-loader": "4.1.2", | 		"vue-style-loader": "4.1.2", | ||||||
| 		"vuedraggable": "4.0.1", | 		"vuedraggable": "4.0.1", | ||||||
| 		"vuex": "4.0.0-rc.2", |  | ||||||
| 		"vuex-persistedstate": "3.1.0", |  | ||||||
| 		"web-push": "3.4.4", | 		"web-push": "3.4.4", | ||||||
| 		"webpack": "5.10.1", | 		"webpack": "5.10.1", | ||||||
| 		"webpack-cli": "4.2.0", | 		"webpack-cli": "4.2.0", | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								src/client/@types/vuex-shim.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								src/client/@types/vuex-shim.d.ts
									
										
									
									
										vendored
									
									
								
							|  | @ -1,12 +0,0 @@ | ||||||
| import { ComponentCustomProperties } from 'vue'; |  | ||||||
| import { Store } from 'vuex'; |  | ||||||
| 
 |  | ||||||
| declare module '@vue/runtime-core' { |  | ||||||
| 	// tslint:disable-next-line:no-empty-interface
 |  | ||||||
| 	interface State { |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	interface ComponentCustomProperties { |  | ||||||
| 		$store: Store<State>; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										86
									
								
								src/client/account.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/client/account.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | ||||||
|  | import { reactive } from 'vue'; | ||||||
|  | import { apiUrl } from '@/config'; | ||||||
|  | import { waiting } from '@/os'; | ||||||
|  | 
 | ||||||
|  | // TODO: 他のタブと永続化されたstateを同期
 | ||||||
|  | 
 | ||||||
|  | type Account = { | ||||||
|  | 	id: string; | ||||||
|  | 	token: string; | ||||||
|  | 	clientData: Record<string, any>; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const data = localStorage.getItem('account'); | ||||||
|  | 
 | ||||||
|  | // TODO: 外部からはreadonlyに
 | ||||||
|  | export const $i = data ? reactive(JSON.parse(data) as Account) : null; | ||||||
|  | 
 | ||||||
|  | export function signout() { | ||||||
|  | 	localStorage.removeItem('account'); | ||||||
|  | 	document.cookie = `igi=; path=/`; | ||||||
|  | 	location.href = '/'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getAccounts() { | ||||||
|  | 	const accountsData = localStorage.getItem('accounts'); | ||||||
|  | 	const accounts: { id: Account['id'], token: Account['token'] }[] = accountsData ? JSON.parse(accountsData) : []; | ||||||
|  | 	return accounts; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function addAccount(id: Account['id'], token: Account['token']) { | ||||||
|  | 	const accounts = getAccounts(); | ||||||
|  | 	if (!accounts.some(x => x.id === id)) { | ||||||
|  | 		localStorage.setItem('accounts', JSON.stringify(accounts.concat([{ id, token }]))); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function fetchAccount(token): Promise<Account> { | ||||||
|  | 	return new Promise((done, fail) => { | ||||||
|  | 		// Fetch user
 | ||||||
|  | 		fetch(`${apiUrl}/i`, { | ||||||
|  | 			method: 'POST', | ||||||
|  | 			body: JSON.stringify({ | ||||||
|  | 				i: token | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 		.then(res => { | ||||||
|  | 			// When failed to authenticate user
 | ||||||
|  | 			if (res.status !== 200 && res.status < 500) { | ||||||
|  | 				return signout(); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Parse response
 | ||||||
|  | 			res.json().then(i => { | ||||||
|  | 				i.token = token; | ||||||
|  | 				done(i); | ||||||
|  | 			}); | ||||||
|  | 		}) | ||||||
|  | 		.catch(fail); | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function updateAccount(data) { | ||||||
|  | 	for (const [key, value] of Object.entries(data)) { | ||||||
|  | 		$i[key] = value; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function refreshAccount() { | ||||||
|  | 	fetchAccount($i.token).then(updateAccount); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export async function login(token: Account['token']) { | ||||||
|  | 	waiting(); | ||||||
|  | 	if (_DEV_) console.log('logging as token ', token); | ||||||
|  | 	const me = await fetchAccount(token); | ||||||
|  | 	localStorage.setItem('account', JSON.stringify(me)); | ||||||
|  | 	addAccount(me.id, token); | ||||||
|  | 	location.reload(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // このファイルに書きたくないけどここに書かないと何故かVeturが認識しない
 | ||||||
|  | declare module '@vue/runtime-core' { | ||||||
|  | 	interface ComponentCustomProperties { | ||||||
|  | 		$i: typeof $i; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -1,34 +0,0 @@ | ||||||
| // 常にメモリにロードしておく必要がないような設定情報を保管するストレージ
 |  | ||||||
| 
 |  | ||||||
| const PREFIX = 'miux:'; |  | ||||||
| 
 |  | ||||||
| export const defaultDeviceSettings = { |  | ||||||
| 	sound_masterVolume: 0.3, |  | ||||||
| 	sound_note: { type: 'syuilo/down', volume: 1 }, |  | ||||||
| 	sound_noteMy: { type: 'syuilo/up', volume: 1 }, |  | ||||||
| 	sound_notification: { type: 'syuilo/pope2', volume: 1 }, |  | ||||||
| 	sound_chat: { type: 'syuilo/pope1', volume: 1 }, |  | ||||||
| 	sound_chatBg: { type: 'syuilo/waon', volume: 1 }, |  | ||||||
| 	sound_antenna: { type: 'syuilo/triple', volume: 1 }, |  | ||||||
| 	sound_channel: { type: 'syuilo/square-pico', volume: 1 }, |  | ||||||
| 	sound_reversiPutBlack: { type: 'syuilo/kick', volume: 0.3 }, |  | ||||||
| 	sound_reversiPutWhite: { type: 'syuilo/snare', volume: 0.3 }, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export const device = { |  | ||||||
| 	get<T extends keyof typeof defaultDeviceSettings>(key: T): typeof defaultDeviceSettings[T] { |  | ||||||
| 		// TODO: indexedDBにする
 |  | ||||||
| 		//       ただしその際はnullチェックではなくキー存在チェックにしないとダメ
 |  | ||||||
| 		//       (indexedDBはnullを保存できるため、ユーザーが意図してnullを格納した可能性がある)
 |  | ||||||
| 		const value = localStorage.getItem(PREFIX + key); |  | ||||||
| 		if (value == null) { |  | ||||||
| 			return defaultDeviceSettings[key]; |  | ||||||
| 		} else { |  | ||||||
| 			return JSON.parse(value); |  | ||||||
| 		} |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	set(key: keyof typeof defaultDeviceSettings, value: any): any { |  | ||||||
| 		localStorage.setItem(PREFIX + key, JSON.stringify(value)); |  | ||||||
| 	}, |  | ||||||
| }; |  | ||||||
|  | @ -116,16 +116,6 @@ export default defineComponent({ | ||||||
| 			} | 			} | ||||||
| 		}; | 		}; | ||||||
| 		update(); | 		update(); | ||||||
| 
 |  | ||||||
| 		this.$store.subscribe((mutation, state) => { |  | ||||||
| 			if (mutation.type !== 'device/set') return; |  | ||||||
| 
 |  | ||||||
| 			if (mutation?.payload?.key !== 'theme') return; |  | ||||||
| 
 |  | ||||||
| 			setTimeout(() => { |  | ||||||
| 				this.computedStyle = getComputedStyle(document.documentElement); |  | ||||||
| 			}, 250); |  | ||||||
| 		}); |  | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	beforeUnmount() { | 	beforeUnmount() { | ||||||
|  |  | ||||||
|  | @ -17,8 +17,8 @@ | ||||||
| 	</ol> | 	</ol> | ||||||
| 	<ol class="emojis" ref="suggests" v-if="emojis.length > 0"> | 	<ol class="emojis" ref="suggests" v-if="emojis.length > 0"> | ||||||
| 		<li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1"> | 		<li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1"> | ||||||
| 			<span class="emoji" v-if="emoji.isCustomEmoji"><img :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url" :alt="emoji.emoji"/></span> | 			<span class="emoji" v-if="emoji.isCustomEmoji"><img :src="$store.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url" :alt="emoji.emoji"/></span> | ||||||
| 			<span class="emoji" v-else-if="!useOsNativeEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span> | 			<span class="emoji" v-else-if="!$store.state.useOsNativeEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span> | ||||||
| 			<span class="emoji" v-else>{{ emoji.emoji }}</span> | 			<span class="emoji" v-else>{{ emoji.emoji }}</span> | ||||||
| 			<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span> | 			<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span> | ||||||
| 			<span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span> | 			<span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span> | ||||||
|  | @ -128,12 +128,6 @@ export default defineComponent({ | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	computed: { |  | ||||||
| 		useOsNativeEmojis(): boolean { |  | ||||||
| 			return this.$store.state.device.useOsNativeEmojis; |  | ||||||
| 		} |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	watch: { | 	watch: { | ||||||
| 		showing() { | 		showing() { | ||||||
| 			if (!this.showing) { | 			if (!this.showing) { | ||||||
|  | @ -151,7 +145,7 @@ export default defineComponent({ | ||||||
| 		this.setPosition(); | 		this.setPosition(); | ||||||
| 
 | 
 | ||||||
| 		//#region Construct Emoji DB | 		//#region Construct Emoji DB | ||||||
| 		const customEmojis = this.$store.state.instance.meta.emojis; | 		const customEmojis = this.$instance.emojis; | ||||||
| 		const emojiDefinitions: EmojiDef[] = []; | 		const emojiDefinitions: EmojiDef[] = []; | ||||||
| 
 | 
 | ||||||
| 		for (const x of customEmojis) { | 		for (const x of customEmojis) { | ||||||
|  |  | ||||||
|  | @ -28,7 +28,6 @@ declare global { | ||||||
| 	interface Window extends CaptchaContainer { | 	interface Window extends CaptchaContainer { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| import * as os from '@/os'; |  | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	props: { | 	props: { | ||||||
|  | @ -101,7 +100,7 @@ export default defineComponent({ | ||||||
| 			if (this.captcha.render && this.$refs.captcha instanceof Element) { | 			if (this.captcha.render && this.$refs.captcha instanceof Element) { | ||||||
| 				this.captcha.render(this.$refs.captcha, { | 				this.captcha.render(this.$refs.captcha, { | ||||||
| 					sitekey: this.sitekey, | 					sitekey: this.sitekey, | ||||||
| 					theme: this.$store.state.device.darkMode ? 'dark' : 'light', | 					theme: this.$store.state.darkMode ? 'dark' : 'light', | ||||||
| 					callback: this.callback, | 					callback: this.callback, | ||||||
| 					'expired-callback': this.callback, | 					'expired-callback': this.callback, | ||||||
| 					'error-callback': this.callback, | 					'error-callback': this.callback, | ||||||
|  |  | ||||||
|  | @ -37,12 +37,14 @@ export default defineComponent({ | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return h(this.$store.state.device.animation ? TransitionGroup : 'div', { | 		return h(this.$store.state.animation ? TransitionGroup : 'div', this.$store.state.animation ? { | ||||||
| 			class: 'sqadhkmv _list_', | 			class: 'sqadhkmv _list_', | ||||||
| 			name: 'list', | 			name: 'list', | ||||||
| 			tag: 'div', | 			tag: 'div', | ||||||
| 			'data-direction': this.direction, | 			'data-direction': this.direction, | ||||||
| 			'data-reversed': this.reversed ? 'true' : 'false', | 			'data-reversed': this.reversed ? 'true' : 'false', | ||||||
|  | 		} : { | ||||||
|  | 			class: 'sqadhkmv _list_', | ||||||
| 		}, this.items.map((item, i) => { | 		}, this.items.map((item, i) => { | ||||||
| 			const el = this.$slots.default({ | 			const el = this.$slots.default({ | ||||||
| 				item: item | 				item: item | ||||||
|  |  | ||||||
|  | @ -27,6 +27,7 @@ import { | ||||||
| 	faFilm | 	faFilm | ||||||
| 	} from '@fortawesome/free-solid-svg-icons'; | 	} from '@fortawesome/free-solid-svg-icons'; | ||||||
| import ImgWithBlurhash from './img-with-blurhash.vue'; | import ImgWithBlurhash from './img-with-blurhash.vue'; | ||||||
|  | import { ColdDeviceStorage } from '@/store'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
|  | @ -89,12 +90,12 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 	mounted() { | 	mounted() { | ||||||
| 		const audioTag = this.$refs.volumectrl as HTMLAudioElement; | 		const audioTag = this.$refs.volumectrl as HTMLAudioElement; | ||||||
| 		if (audioTag) audioTag.volume = this.$store.state.device.mediaVolume; | 		if (audioTag) audioTag.volume = ColdDeviceStorage.get('mediaVolume'); | ||||||
| 	}, | 	}, | ||||||
| 	methods: { | 	methods: { | ||||||
| 		volumechange() { | 		volumechange() { | ||||||
| 			const audioTag = this.$refs.volumectrl as HTMLAudioElement; | 			const audioTag = this.$refs.volumectrl as HTMLAudioElement; | ||||||
| 			this.$store.commit('device/set', { key: 'mediaVolume', value: audioTag.volume }); | 			ColdDeviceStorage.set('mediaVolume', audioTag.volume); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -8,11 +8,11 @@ | ||||||
| 	@dragend="onDragend" | 	@dragend="onDragend" | ||||||
| 	:title="title" | 	:title="title" | ||||||
| > | > | ||||||
| 	<div class="label" v-if="$store.state.i.avatarId == file.id"> | 	<div class="label" v-if="$i.avatarId == file.id"> | ||||||
| 		<img src="/assets/label.svg"/> | 		<img src="/assets/label.svg"/> | ||||||
| 		<p>{{ $t('avatar') }}</p> | 		<p>{{ $t('avatar') }}</p> | ||||||
| 	</div> | 	</div> | ||||||
| 	<div class="label" v-if="$store.state.i.bannerId == file.id"> | 	<div class="label" v-if="$i.bannerId == file.id"> | ||||||
| 		<img src="/assets/label.svg"/> | 		<img src="/assets/label.svg"/> | ||||||
| 		<p>{{ $t('banner') }}</p> | 		<p>{{ $t('banner') }}</p> | ||||||
| 	</div> | 	</div> | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ | ||||||
| 		<template v-if="!hover"><Fa :icon="faFolder" fixed-width/></template> | 		<template v-if="!hover"><Fa :icon="faFolder" fixed-width/></template> | ||||||
| 		{{ folder.name }} | 		{{ folder.name }} | ||||||
| 	</p> | 	</p> | ||||||
| 	<p class="upload" v-if="$store.state.settings.uploadFolder == folder.id"> | 	<p class="upload" v-if="$store.state.uploadFolder == folder.id"> | ||||||
| 		{{ $t('uploadFolder') }} | 		{{ $t('uploadFolder') }} | ||||||
| 	</p> | 	</p> | ||||||
| 	<button v-if="selectMode" class="checkbox _button" :class="{ checked: isSelected }" @click.prevent.stop="checkboxClicked"></button> | 	<button v-if="selectMode" class="checkbox _button" :class="{ checked: isSelected }" @click.prevent.stop="checkboxClicked"></button> | ||||||
|  | @ -213,11 +213,8 @@ export default defineComponent({ | ||||||
| 			os.api('drive/folders/delete', { | 			os.api('drive/folders/delete', { | ||||||
| 				folderId: this.folder.id | 				folderId: this.folder.id | ||||||
| 			}).then(() => { | 			}).then(() => { | ||||||
| 				if (this.$store.state.settings.uploadFolder === this.folder.id) { | 				if (this.$store.state.uploadFolder === this.folder.id) { | ||||||
| 					this.$store.dispatch('settings/set', { | 					this.$store.set('uploadFolder', null); | ||||||
| 						key: 'uploadFolder', |  | ||||||
| 						value: null |  | ||||||
| 					}); |  | ||||||
| 				} | 				} | ||||||
| 			}).catch(err => { | 			}).catch(err => { | ||||||
| 				switch(err.id) { | 				switch(err.id) { | ||||||
|  | @ -238,10 +235,7 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		setAsUploadFolder() { | 		setAsUploadFolder() { | ||||||
| 			this.$store.dispatch('settings/set', { | 			this.$store.set('uploadFolder', this.folder.id); | ||||||
| 				key: 'uploadFolder', |  | ||||||
| 				value: this.folder.id |  | ||||||
| 			}); |  | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		onContextmenu(e) { | 		onContextmenu(e) { | ||||||
|  |  | ||||||
|  | @ -136,7 +136,7 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	mounted() { | 	mounted() { | ||||||
| 		if (this.$store.state.device.enableInfiniteScroll && this.$refs.loadMoreFiles) { | 		if (this.$store.state.enableInfiniteScroll && this.$refs.loadMoreFiles) { | ||||||
| 			this.$nextTick(() => { | 			this.$nextTick(() => { | ||||||
| 				this.ilFilesObserver.observe((this.$refs.loadMoreFiles as Vue).$el) | 				this.ilFilesObserver.observe((this.$refs.loadMoreFiles as Vue).$el) | ||||||
| 			}); | 			}); | ||||||
|  | @ -159,7 +159,7 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	activated() { | 	activated() { | ||||||
| 		if (this.$store.state.device.enableInfiniteScroll) { | 		if (this.$store.state.enableInfiniteScroll) { | ||||||
| 			this.$nextTick(() => { | 			this.$nextTick(() => { | ||||||
| 				this.ilFilesObserver.observe((this.$refs.loadMoreFiles as Vue).$el) | 				this.ilFilesObserver.observe((this.$refs.loadMoreFiles as Vue).$el) | ||||||
| 			}); | 			}); | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ | ||||||
| 						tabindex="0" | 						tabindex="0" | ||||||
| 					> | 					> | ||||||
| 						<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/> | 						<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/> | ||||||
| 						<img v-else :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/> | 						<img v-else :src="$store.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/> | ||||||
| 					</button> | 					</button> | ||||||
| 				</div> | 				</div> | ||||||
| 				<div v-if="searchResultUnicode.length > 0"> | 				<div v-if="searchResultUnicode.length > 0"> | ||||||
|  | @ -45,7 +45,7 @@ | ||||||
| 				<section> | 				<section> | ||||||
| 					<header class="_acrylic"><Fa :icon="faClock" fixed-width/> {{ $t('recentUsed') }}</header> | 					<header class="_acrylic"><Fa :icon="faClock" fixed-width/> {{ $t('recentUsed') }}</header> | ||||||
| 					<div> | 					<div> | ||||||
| 						<button v-for="emoji in $store.state.device.recentlyUsedEmojis" | 						<button v-for="emoji in $store.state.recentlyUsedEmojis" | ||||||
| 							class="_button" | 							class="_button" | ||||||
| 							@click="chosen(emoji, $event)" | 							@click="chosen(emoji, $event)" | ||||||
| 							:key="emoji" | 							:key="emoji" | ||||||
|  | @ -67,7 +67,7 @@ | ||||||
| 						@click="chosen(emoji, $event)" | 						@click="chosen(emoji, $event)" | ||||||
| 						:key="emoji.name" | 						:key="emoji.name" | ||||||
| 					> | 					> | ||||||
| 						<img :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/> | 						<img :src="$store.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/> | ||||||
| 					</button> | 					</button> | ||||||
| 				</div> | 				</div> | ||||||
| 			</section> | 			</section> | ||||||
|  | @ -100,6 +100,7 @@ import MkModal from '@/components/ui/modal.vue'; | ||||||
| import Particle from '@/components/particle.vue'; | import Particle from '@/components/particle.vue'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
| import { isDeviceTouch } from '../scripts/is-device-touch'; | import { isDeviceTouch } from '../scripts/is-device-touch'; | ||||||
|  | import { emojiCategories } from '@/instance'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
|  | @ -125,12 +126,12 @@ export default defineComponent({ | ||||||
| 		return { | 		return { | ||||||
| 			emojilist: markRaw(emojilist), | 			emojilist: markRaw(emojilist), | ||||||
| 			getStaticImageUrl, | 			getStaticImageUrl, | ||||||
| 			pinned: this.$store.state.settings.reactions, | 			pinned: this.$store.state.reactions, | ||||||
| 			width: this.asReactionPicker ? this.$store.state.device.reactionPickerWidth : 3, | 			width: this.asReactionPicker ? this.$store.state.reactionPickerWidth : 3, | ||||||
| 			height: this.asReactionPicker ? this.$store.state.device.reactionPickerHeight : 2, | 			height: this.asReactionPicker ? this.$store.state.reactionPickerHeight : 2, | ||||||
| 			big: this.asReactionPicker ? isDeviceTouch : false, | 			big: this.asReactionPicker ? isDeviceTouch : false, | ||||||
| 			customEmojiCategories: this.$store.getters['instance/emojiCategories'], | 			customEmojiCategories: emojiCategories, | ||||||
| 			customEmojis: this.$store.state.instance.meta.emojis, | 			customEmojis: this.$instance.emojis, | ||||||
| 			visibleCategories: {}, | 			visibleCategories: {}, | ||||||
| 			q: null, | 			q: null, | ||||||
| 			searchResultCustom: [], | 			searchResultCustom: [], | ||||||
|  | @ -346,10 +347,10 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 			// 最近使った絵文字更新 | 			// 最近使った絵文字更新 | ||||||
| 			if (!this.pinned.includes(key)) { | 			if (!this.pinned.includes(key)) { | ||||||
| 				let recents = this.$store.state.device.recentlyUsedEmojis; | 				let recents = this.$store.state.recentlyUsedEmojis; | ||||||
| 				recents = recents.filter((e: any) => e !== key); | 				recents = recents.filter((e: any) => e !== key); | ||||||
| 				recents.unshift(key); | 				recents.unshift(key); | ||||||
| 				this.$store.commit('device/set', { key: 'recentlyUsedEmojis', value: recents.splice(0, 16) }); | 				this.$store.set('recentlyUsedEmojis', recents.splice(0, 16)); | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
| ._formLabel { | ._formLabel { | ||||||
| 	font-size: 80%; | 	font-size: 80%; | ||||||
| 	padding: 0 16px 8px 16px; | 	padding: 0 16px 8px 16px; | ||||||
|  | 	opacity: 0.8; | ||||||
| 
 | 
 | ||||||
| 	&:empty { | 	&:empty { | ||||||
| 		display: none; | 		display: none; | ||||||
|  | @ -21,6 +22,7 @@ | ||||||
| ._formCaption { | ._formCaption { | ||||||
| 	font-size: 80%; | 	font-size: 80%; | ||||||
| 	padding: 8px 16px 0 16px; | 	padding: 8px 16px 0 16px; | ||||||
|  | 	opacity: 0.8; | ||||||
| 
 | 
 | ||||||
| 	&:empty { | 	&:empty { | ||||||
| 		display: none; | 		display: none; | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ import copyToClipboard from '@/scripts/copy-to-clipboard'; | ||||||
| import { router } from '@/router'; | import { router } from '@/router'; | ||||||
| import { ui, url } from '@/config'; | import { ui, url } from '@/config'; | ||||||
| import { popout } from '@/scripts/popout'; | import { popout } from '@/scripts/popout'; | ||||||
|  | import { ColdDeviceStorage } from '@/store'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	inject: { | 	inject: { | ||||||
|  | @ -98,8 +99,8 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 		nav() { | 		nav() { | ||||||
| 			if (this.to.startsWith('/my/messaging')) { | 			if (this.to.startsWith('/my/messaging')) { | ||||||
| 				if (this.$store.state.device.chatOpenBehavior === 'window') return this.window(); | 				if (ColdDeviceStorage.get('chatOpenBehavior') === 'window') return this.window(); | ||||||
| 				if (this.$store.state.device.chatOpenBehavior === 'popout') return this.popout(); | 				if (ColdDeviceStorage.get('chatOpenBehavior') === 'popout') return this.popout(); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (this.behavior) { | 			if (this.behavior) { | ||||||
|  | @ -111,12 +112,13 @@ export default defineComponent({ | ||||||
| 			if (this.navHook) { | 			if (this.navHook) { | ||||||
| 				this.navHook(this.to); | 				this.navHook(this.to); | ||||||
| 			} else { | 			} else { | ||||||
| 				if (this.$store.state.device.defaultSideView && this.sideViewHook && this.to !== '/') { | 				if (this.$store.state.defaultSideView && this.sideViewHook && this.to !== '/') { | ||||||
| 					return this.sideViewHook(this.to); | 					return this.sideViewHook(this.to); | ||||||
| 				} | 				} | ||||||
| 				if (this.$store.state.device.deckNavWindow && (ui === 'deck') && this.to !== '/') { | 				// TODO: a.vueからdeck-sotreを参照したくないのでなんとかする | ||||||
| 					return this.window(); | 				//if (deckStore.state.device.deckNavWindow && (ui === 'deck') && this.to !== '/') { | ||||||
| 				} | 				//	return this.window(); | ||||||
|  | 				//} | ||||||
| 				if (ui === 'desktop') { | 				if (ui === 'desktop') { | ||||||
| 					return this.window(); | 					return this.window(); | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| <template> | <template> | ||||||
| <span class="mk-acct" v-once> | <span class="mk-acct" v-once> | ||||||
| 	<span class="name">@{{ user.username }}</span> | 	<span class="name">@{{ user.username }}</span> | ||||||
| 	<span class="host" v-if="user.host || detail || $store.state.settings.showFullAcct">@{{ user.host || host }}</span> | 	<span class="host" v-if="user.host || detail || $store.state.showFullAcct">@{{ user.host || host }}</span> | ||||||
| </span> | </span> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -38,7 +38,7 @@ export default defineComponent({ | ||||||
| 			return this.user.isCat; | 			return this.user.isCat; | ||||||
| 		}, | 		}, | ||||||
| 		url(): string { | 		url(): string { | ||||||
| 			return this.$store.state.device.disableShowingAnimatedImages | 			return this.$store.state.disableShowingAnimatedImages | ||||||
| 				? getStaticImageUrl(this.user.avatarUrl) | 				? getStaticImageUrl(this.user.avatarUrl) | ||||||
| 				: this.user.avatarUrl; | 				: this.user.avatarUrl; | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|  | @ -54,13 +54,13 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		useOsNativeEmojis(): boolean { | 		useOsNativeEmojis(): boolean { | ||||||
| 			return this.$store.state.device.useOsNativeEmojis && !this.isReaction; | 			return this.$store.state.useOsNativeEmojis && !this.isReaction; | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		ce() { | 		ce() { | ||||||
| 			let ce = []; | 			let ce = []; | ||||||
| 			if (this.customEmojis) ce = ce.concat(this.customEmojis); | 			if (this.customEmojis) ce = ce.concat(this.customEmojis); | ||||||
| 			if (this.$store.state.instance.meta && this.$store.state.instance.meta.emojis) ce = ce.concat(this.$store.state.instance.meta.emojis); | 			if (this.$instance && this.$instance.emojis) ce = ce.concat(this.$instance.emojis); | ||||||
| 			return ce; | 			return ce; | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
|  | @ -72,7 +72,7 @@ export default defineComponent({ | ||||||
| 					const customEmoji = this.ce.find(x => x.name === this.emoji.substr(1, this.emoji.length - 2)); | 					const customEmoji = this.ce.find(x => x.name === this.emoji.substr(1, this.emoji.length - 2)); | ||||||
| 					if (customEmoji) { | 					if (customEmoji) { | ||||||
| 						this.customEmoji = customEmoji; | 						this.customEmoji = customEmoji; | ||||||
| 						this.url = this.$store.state.device.disableShowingAnimatedImages | 						this.url = this.$store.state.disableShowingAnimatedImages | ||||||
| 							? getStaticImageUrl(customEmoji.url) | 							? getStaticImageUrl(customEmoji.url) | ||||||
| 							: customEmoji.url; | 							: customEmoji.url; | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <template> | <template> | ||||||
| <transition :name="$store.state.device.animation ? 'zoom' : ''" appear> | <transition :name="$store.state.animation ? 'zoom' : ''" appear> | ||||||
| 	<div class="mjndxjcg"> | 	<div class="mjndxjcg"> | ||||||
| 		<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/> | 		<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/> | ||||||
| 		<p><Fa :icon="faExclamationTriangle"/> {{ $t('somethingHappened') }}</p> | 		<p><Fa :icon="faExclamationTriangle"/> {{ $t('somethingHappened') }}</p> | ||||||
|  |  | ||||||
|  | @ -23,7 +23,7 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 	methods: { | 	methods: { | ||||||
| 		search() { | 		search() { | ||||||
| 			const engine = this.$store.state.settings.webSearchEngine || | 			const engine = this.$store.state.webSearchEngine || | ||||||
| 				'https://www.google.com/search?q={{query}}'; | 				'https://www.google.com/search?q={{query}}'; | ||||||
| 			const url = engine.replace('{{query}}', this.query) | 			const url = engine.replace('{{query}}', this.query) | ||||||
| 			window.open(url, '_blank'); | 			window.open(url, '_blank'); | ||||||
|  |  | ||||||
|  | @ -278,7 +278,7 @@ export default defineComponent({ | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			// TODO: var(--panel)の色が暗いか明るいかで判定する | 			// TODO: var(--panel)の色が暗いか明るいかで判定する | ||||||
| 			const gridColor = this.$store.state.device.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; | 			const gridColor = this.$store.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; | ||||||
| 
 | 
 | ||||||
| 			Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg'); | 			Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg'); | ||||||
| 			this.chartInstance = markRaw(new Chart(this.$refs.chart, { | 			this.chartInstance = markRaw(new Chart(this.$refs.chart, { | ||||||
|  |  | ||||||
|  | @ -58,7 +58,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		menu(): string[] { | 		menu(): string[] { | ||||||
| 			return this.$store.state.deviceUser.menu; | 			return this.$store.state.menu; | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ | ||||||
| import { defineComponent } from 'vue'; | import { defineComponent } from 'vue'; | ||||||
| import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; | import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { ColdDeviceStorage } from '@/store'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	props: { | 	props: { | ||||||
|  | @ -45,12 +46,12 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 	mounted() { | 	mounted() { | ||||||
| 		const audioTag = this.$refs.audio as HTMLAudioElement; | 		const audioTag = this.$refs.audio as HTMLAudioElement; | ||||||
| 		if (audioTag) audioTag.volume = this.$store.state.device.mediaVolume; | 		if (audioTag) audioTag.volume = ColdDeviceStorage.get('mediaVolume'); | ||||||
| 	}, | 	}, | ||||||
| 	methods: { | 	methods: { | ||||||
| 		volumechange() { | 		volumechange() { | ||||||
| 			const audioTag = this.$refs.audio as HTMLAudioElement; | 			const audioTag = this.$refs.audio as HTMLAudioElement; | ||||||
| 			this.$store.commit('device/set', { key: 'mediaVolume', value: audioTag.volume }); | 			ColdDeviceStorage.set('mediaVolume', audioTag.volume); | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| }) | }) | ||||||
|  |  | ||||||
|  | @ -52,13 +52,11 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 	computed: { | 	computed: { | ||||||
| 		url(): any { | 		url(): any { | ||||||
| 			let url = this.$store.state.device.disableShowingAnimatedImages | 			let url = this.$store.state.disableShowingAnimatedImages | ||||||
| 				? getStaticImageUrl(this.image.thumbnailUrl) | 				? getStaticImageUrl(this.image.thumbnailUrl) | ||||||
| 				: this.image.thumbnailUrl; | 				: this.image.thumbnailUrl; | ||||||
| 
 | 
 | ||||||
| 			if (this.$store.state.device.loadRemoteMedia) { | 			if (this.raw || this.$store.state.loadRawImages) { | ||||||
| 				url = null; |  | ||||||
| 			} else if (this.raw || this.$store.state.device.loadRawImages) { |  | ||||||
| 				url = this.image.url; | 				url = this.image.url; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | @ -68,7 +66,7 @@ export default defineComponent({ | ||||||
| 	created() { | 	created() { | ||||||
| 		// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする | 		// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする | ||||||
| 		this.$watch('image', () => { | 		this.$watch('image', () => { | ||||||
| 			this.hide = (this.$store.state.device.nsfw === 'force') ? true : this.image.isSensitive && (this.$store.state.device.nsfw !== 'ignore'); | 			this.hide = (this.$store.state.nsfw === 'force') ? true : this.image.isSensitive && (this.$store.state.nsfw !== 'ignore'); | ||||||
| 			if (this.image.blurhash) { | 			if (this.image.blurhash) { | ||||||
| 				this.color = extractAvgColorFromBlurhash(this.image.blurhash); | 				this.color = extractAvgColorFromBlurhash(this.image.blurhash); | ||||||
| 			} | 			} | ||||||
|  | @ -79,7 +77,7 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 	methods: { | 	methods: { | ||||||
| 		onClick() { | 		onClick() { | ||||||
| 			if (this.$store.state.device.imageNewTab) { | 			if (this.$store.state.imageNewTab) { | ||||||
| 				window.open(this.image.url, '_blank'); | 				window.open(this.image.url, '_blank'); | ||||||
| 			} else { | 			} else { | ||||||
| 				os.popup(ImageViewer, { | 				os.popup(ImageViewer, { | ||||||
|  |  | ||||||
|  | @ -48,7 +48,7 @@ export default defineComponent({ | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 	created() { | 	created() { | ||||||
| 		this.hide = (this.$store.state.device.nsfw === 'force') ? true : this.video.isSensitive && (this.$store.state.device.nsfw !== 'ignore'); | 		this.hide = (this.$store.state.nsfw === 'force') ? true : this.video.isSensitive && (this.$store.state.nsfw !== 'ignore'); | ||||||
| 	}, | 	}, | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
| 	<span class="me" v-if="isMe">{{ $t('you') }}</span> | 	<span class="me" v-if="isMe">{{ $t('you') }}</span> | ||||||
| 	<span class="main"> | 	<span class="main"> | ||||||
| 		<span class="username">@{{ username }}</span> | 		<span class="username">@{{ username }}</span> | ||||||
| 		<span class="host" v-if="(host != localHost) || $store.state.settings.showFullAcct">@{{ toUnicode(host) }}</span> | 		<span class="host" v-if="(host != localHost) || $store.state.showFullAcct">@{{ toUnicode(host) }}</span> | ||||||
| 	</span> | 	</span> | ||||||
| </MkA> | </MkA> | ||||||
| <a class="ldlomzub" :href="url" target="_blank" rel="noopener" v-else> | <a class="ldlomzub" :href="url" target="_blank" rel="noopener" v-else> | ||||||
|  | @ -50,8 +50,8 @@ export default defineComponent({ | ||||||
| 			return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`; | 			return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`; | ||||||
| 		}, | 		}, | ||||||
| 		isMe(): boolean { | 		isMe(): boolean { | ||||||
| 			return this.$store.getters.isSignedIn && ( | 			return this.$i && ( | ||||||
| 				`@${this.username}@${toUnicode(this.host)}` === `@${this.$store.state.i.username}@${toUnicode(localHost)}`.toLowerCase() | 				`@${this.username}@${toUnicode(this.host)}` === `@${this.$i.username}@${toUnicode(localHost)}`.toLowerCase() | ||||||
| 			); | 			); | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | @ -82,22 +82,22 @@ export default defineComponent({ | ||||||
| 					let style; | 					let style; | ||||||
| 					switch (token.node.props.name) { | 					switch (token.node.props.name) { | ||||||
| 						case 'tada': { | 						case 'tada': { | ||||||
| 							style = `font-size: 150%;` + (this.$store.state.device.animatedMfm ? 'animation: tada 1s linear infinite both;' : ''); | 							style = `font-size: 150%;` + (this.$store.state.animatedMfm ? 'animation: tada 1s linear infinite both;' : ''); | ||||||
| 							break; | 							break; | ||||||
| 						} | 						} | ||||||
| 						case 'jelly': { | 						case 'jelly': { | ||||||
| 							const speed = token.node.props.args.speed || '1s'; | 							const speed = token.node.props.args.speed || '1s'; | ||||||
| 							style = (this.$store.state.device.animatedMfm ? `animation: mfm-rubberBand ${speed} linear infinite both;` : ''); | 							style = (this.$store.state.animatedMfm ? `animation: mfm-rubberBand ${speed} linear infinite both;` : ''); | ||||||
| 							break; | 							break; | ||||||
| 						} | 						} | ||||||
| 						case 'twitch': { | 						case 'twitch': { | ||||||
| 							const speed = token.node.props.args.speed || '0.5s'; | 							const speed = token.node.props.args.speed || '0.5s'; | ||||||
| 							style = this.$store.state.device.animatedMfm ? `animation: mfm-twitch ${speed} ease infinite;` : ''; | 							style = this.$store.state.animatedMfm ? `animation: mfm-twitch ${speed} ease infinite;` : ''; | ||||||
| 							break; | 							break; | ||||||
| 						} | 						} | ||||||
| 						case 'shake': { | 						case 'shake': { | ||||||
| 							const speed = token.node.props.args.speed || '0.5s'; | 							const speed = token.node.props.args.speed || '0.5s'; | ||||||
| 							style = this.$store.state.device.animatedMfm ? `animation: mfm-shake ${speed} ease infinite;` : ''; | 							style = this.$store.state.animatedMfm ? `animation: mfm-shake ${speed} ease infinite;` : ''; | ||||||
| 							break; | 							break; | ||||||
| 						} | 						} | ||||||
| 						case 'spin': { | 						case 'spin': { | ||||||
|  | @ -110,15 +110,15 @@ export default defineComponent({ | ||||||
| 								token.node.props.args.y ? 'mfm-spinY' : | 								token.node.props.args.y ? 'mfm-spinY' : | ||||||
| 								'mfm-spin'; | 								'mfm-spin'; | ||||||
| 							const speed = token.node.props.args.speed || '1.5s'; | 							const speed = token.node.props.args.speed || '1.5s'; | ||||||
| 							style = this.$store.state.device.animatedMfm ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};` : ''; | 							style = this.$store.state.animatedMfm ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};` : ''; | ||||||
| 							break; | 							break; | ||||||
| 						} | 						} | ||||||
| 						case 'jump': { | 						case 'jump': { | ||||||
| 							style = this.$store.state.device.animatedMfm ? 'animation: mfm-jump 0.75s linear infinite;' : ''; | 							style = this.$store.state.animatedMfm ? 'animation: mfm-jump 0.75s linear infinite;' : ''; | ||||||
| 							break; | 							break; | ||||||
| 						} | 						} | ||||||
| 						case 'bounce': { | 						case 'bounce': { | ||||||
| 							style = this.$store.state.device.animatedMfm ? 'animation: mfm-bounce 0.75s linear infinite; transform-origin: center bottom;' : ''; | 							style = this.$store.state.animatedMfm ? 'animation: mfm-bounce 0.75s linear infinite; transform-origin: center bottom;' : ''; | ||||||
| 							break; | 							break; | ||||||
| 						} | 						} | ||||||
| 						case 'flip': { | 						case 'flip': { | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ | ||||||
| 			<XNoteHeader class="header" :note="note" :mini="true"/> | 			<XNoteHeader class="header" :note="note" :mini="true"/> | ||||||
| 			<div class="body"> | 			<div class="body"> | ||||||
| 				<p v-if="note.cw != null" class="cw"> | 				<p v-if="note.cw != null" class="cw"> | ||||||
| 					<Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$store.state.i" :custom-emojis="note.emojis" /> | 					<Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis" /> | ||||||
| 					<XCwButton v-model:value="showContent" :note="note"/> | 					<XCwButton v-model:value="showContent" :note="note"/> | ||||||
| 				</p> | 				</p> | ||||||
| 				<div class="content" v-show="note.cw == null || showContent"> | 				<div class="content" v-show="note.cw == null || showContent"> | ||||||
|  |  | ||||||
|  | @ -43,14 +43,14 @@ | ||||||
| 			<MkInstanceTicker v-if="showTicker" class="ticker" :instance="appearNote.user.instance"/> | 			<MkInstanceTicker v-if="showTicker" class="ticker" :instance="appearNote.user.instance"/> | ||||||
| 			<div class="body"> | 			<div class="body"> | ||||||
| 				<p v-if="appearNote.cw != null" class="cw"> | 				<p v-if="appearNote.cw != null" class="cw"> | ||||||
| 					<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$store.state.i" :custom-emojis="appearNote.emojis"/> | 					<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> | ||||||
| 					<XCwButton v-model:value="showContent" :note="appearNote"/> | 					<XCwButton v-model:value="showContent" :note="appearNote"/> | ||||||
| 				</p> | 				</p> | ||||||
| 				<div class="content" v-show="appearNote.cw == null || showContent"> | 				<div class="content" v-show="appearNote.cw == null || showContent"> | ||||||
| 					<div class="text"> | 					<div class="text"> | ||||||
| 						<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ $t('private') }})</span> | 						<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ $t('private') }})</span> | ||||||
| 						<MkA class="reply" v-if="appearNote.replyId" :to="`/notes/${appearNote.replyId}`"><Fa :icon="faReply"/></MkA> | 						<MkA class="reply" v-if="appearNote.replyId" :to="`/notes/${appearNote.replyId}`"><Fa :icon="faReply"/></MkA> | ||||||
| 						<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$store.state.i" :custom-emojis="appearNote.emojis"/> | 						<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> | ||||||
| 						<a class="rp" v-if="appearNote.renote != null">RN:</a> | 						<a class="rp" v-if="appearNote.renote != null">RN:</a> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="files" v-if="appearNote.files.length > 0"> | 					<div class="files" v-if="appearNote.files.length > 0"> | ||||||
|  | @ -182,7 +182,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		rs() { | 		rs() { | ||||||
| 			return this.$store.state.settings.reactions; | 			return this.$store.state.reactions; | ||||||
| 		}, | 		}, | ||||||
| 		keymap(): any { | 		keymap(): any { | ||||||
| 			return { | 			return { | ||||||
|  | @ -222,11 +222,11 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		isMyNote(): boolean { | 		isMyNote(): boolean { | ||||||
| 			return this.$store.getters.isSignedIn && (this.$store.state.i.id === this.appearNote.userId); | 			return this.$i && (this.$i.id === this.appearNote.userId); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		isMyRenote(): boolean { | 		isMyRenote(): boolean { | ||||||
| 			return this.$store.getters.isSignedIn && (this.$store.state.i.id === this.note.userId); | 			return this.$i && (this.$i.id === this.note.userId); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		canRenote(): boolean { | 		canRenote(): boolean { | ||||||
|  | @ -262,14 +262,14 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		showTicker() { | 		showTicker() { | ||||||
| 			if (this.$store.state.device.instanceTicker === 'always') return true; | 			if (this.$store.state.instanceTicker === 'always') return true; | ||||||
| 			if (this.$store.state.device.instanceTicker === 'remote' && this.appearNote.user.instance) return true; | 			if (this.$store.state.instanceTicker === 'remote' && this.appearNote.user.instance) return true; | ||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	async created() { | 	async created() { | ||||||
| 		if (this.$store.getters.isSignedIn) { | 		if (this.$i) { | ||||||
| 			this.connection = os.stream; | 			this.connection = os.stream; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -282,7 +282,7 @@ export default defineComponent({ | ||||||
| 			this.$emit('update:note', Object.freeze(result)); | 			this.$emit('update:note', Object.freeze(result)); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		this.muted = await checkWordMute(this.appearNote, this.$store.state.i, this.$store.state.settings.mutedWords); | 		this.muted = await checkWordMute(this.appearNote, this.$i, this.$store.state.mutedWords); | ||||||
| 
 | 
 | ||||||
| 		if (this.detail) { | 		if (this.detail) { | ||||||
| 			os.api('notes/children', { | 			os.api('notes/children', { | ||||||
|  | @ -305,7 +305,7 @@ export default defineComponent({ | ||||||
| 	mounted() { | 	mounted() { | ||||||
| 		this.capture(true); | 		this.capture(true); | ||||||
| 
 | 
 | ||||||
| 		if (this.$store.getters.isSignedIn) { | 		if (this.$i) { | ||||||
| 			this.connection.on('_connected_', this.onStreamConnected); | 			this.connection.on('_connected_', this.onStreamConnected); | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
|  | @ -313,7 +313,7 @@ export default defineComponent({ | ||||||
| 	beforeUnmount() { | 	beforeUnmount() { | ||||||
| 		this.decapture(true); | 		this.decapture(true); | ||||||
| 
 | 
 | ||||||
| 		if (this.$store.getters.isSignedIn) { | 		if (this.$i) { | ||||||
| 			this.connection.off('_connected_', this.onStreamConnected); | 			this.connection.off('_connected_', this.onStreamConnected); | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
|  | @ -340,14 +340,14 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		capture(withHandler = false) { | 		capture(withHandler = false) { | ||||||
| 			if (this.$store.getters.isSignedIn) { | 			if (this.$i) { | ||||||
| 				this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); | 				this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); | ||||||
| 				if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); | 				if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		decapture(withHandler = false) { | 		decapture(withHandler = false) { | ||||||
| 			if (this.$store.getters.isSignedIn) { | 			if (this.$i) { | ||||||
| 				this.connection.send('un', { | 				this.connection.send('un', { | ||||||
| 					id: this.appearNote.id | 					id: this.appearNote.id | ||||||
| 				}); | 				}); | ||||||
|  | @ -389,7 +389,7 @@ export default defineComponent({ | ||||||
| 						[reaction]: currentCount + 1 | 						[reaction]: currentCount + 1 | ||||||
| 					}; | 					}; | ||||||
| 
 | 
 | ||||||
| 					if (body.userId === this.$store.state.i.id) { | 					if (body.userId === this.$i.id) { | ||||||
| 						n.myReaction = reaction; | 						n.myReaction = reaction; | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
|  | @ -414,7 +414,7 @@ export default defineComponent({ | ||||||
| 						[reaction]: Math.max(0, currentCount - 1) | 						[reaction]: Math.max(0, currentCount - 1) | ||||||
| 					}; | 					}; | ||||||
| 
 | 
 | ||||||
| 					if (body.userId === this.$store.state.i.id) { | 					if (body.userId === this.$i.id) { | ||||||
| 						n.myReaction = null; | 						n.myReaction = null; | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
|  | @ -434,7 +434,7 @@ export default defineComponent({ | ||||||
| 					choices[choice] = { | 					choices[choice] = { | ||||||
| 						...choices[choice], | 						...choices[choice], | ||||||
| 						votes: choices[choice].votes + 1, | 						votes: choices[choice].votes + 1, | ||||||
| 						...(body.userId === this.$store.state.i.id ? { | 						...(body.userId === this.$i.id ? { | ||||||
| 							isVoted: true | 							isVoted: true | ||||||
| 						} : {}) | 						} : {}) | ||||||
| 					}; | 					}; | ||||||
|  | @ -614,7 +614,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 		getMenu() { | 		getMenu() { | ||||||
| 			let menu; | 			let menu; | ||||||
| 			if (this.$store.getters.isSignedIn) { | 			if (this.$i) { | ||||||
| 				const statePromise = os.api('notes/state', { | 				const statePromise = os.api('notes/state', { | ||||||
| 					noteId: this.appearNote.id | 					noteId: this.appearNote.id | ||||||
| 				}); | 				}); | ||||||
|  | @ -649,7 +649,7 @@ export default defineComponent({ | ||||||
| 					text: this.$t('clip'), | 					text: this.$t('clip'), | ||||||
| 					action: () => this.clip() | 					action: () => this.clip() | ||||||
| 				}, | 				}, | ||||||
| 				(this.appearNote.userId != this.$store.state.i.id) ? statePromise.then(state => state.isWatching ? { | 				(this.appearNote.userId != this.$i.id) ? statePromise.then(state => state.isWatching ? { | ||||||
| 					icon: faEyeSlash, | 					icon: faEyeSlash, | ||||||
| 					text: this.$t('unwatch'), | 					text: this.$t('unwatch'), | ||||||
| 					action: () => this.toggleWatch(false) | 					action: () => this.toggleWatch(false) | ||||||
|  | @ -658,7 +658,7 @@ export default defineComponent({ | ||||||
| 					text: this.$t('watch'), | 					text: this.$t('watch'), | ||||||
| 					action: () => this.toggleWatch(true) | 					action: () => this.toggleWatch(true) | ||||||
| 				}) : undefined, | 				}) : undefined, | ||||||
| 				this.appearNote.userId == this.$store.state.i.id ? (this.$store.state.i.pinnedNoteIds || []).includes(this.appearNote.id) ? { | 				this.appearNote.userId == this.$i.id ? (this.$i.pinnedNoteIds || []).includes(this.appearNote.id) ? { | ||||||
| 					icon: faThumbtack, | 					icon: faThumbtack, | ||||||
| 					text: this.$t('unpin'), | 					text: this.$t('unpin'), | ||||||
| 					action: () => this.togglePin(false) | 					action: () => this.togglePin(false) | ||||||
|  | @ -667,7 +667,7 @@ export default defineComponent({ | ||||||
| 					text: this.$t('pin'), | 					text: this.$t('pin'), | ||||||
| 					action: () => this.togglePin(true) | 					action: () => this.togglePin(true) | ||||||
| 				} : undefined, | 				} : undefined, | ||||||
| 				...(this.$store.state.i.isModerator || this.$store.state.i.isAdmin ? [ | 				...(this.$i.isModerator || this.$i.isAdmin ? [ | ||||||
| 					null, | 					null, | ||||||
| 					{ | 					{ | ||||||
| 						icon: faBullhorn, | 						icon: faBullhorn, | ||||||
|  | @ -676,7 +676,7 @@ export default defineComponent({ | ||||||
| 					}] | 					}] | ||||||
| 					: [] | 					: [] | ||||||
| 				), | 				), | ||||||
| 				...(this.appearNote.userId != this.$store.state.i.id ? [ | 				...(this.appearNote.userId != this.$i.id ? [ | ||||||
| 					null, | 					null, | ||||||
| 					{ | 					{ | ||||||
| 						icon: faExclamationCircle, | 						icon: faExclamationCircle, | ||||||
|  | @ -691,9 +691,9 @@ export default defineComponent({ | ||||||
| 					}] | 					}] | ||||||
| 					: [] | 					: [] | ||||||
| 				), | 				), | ||||||
| 				...(this.appearNote.userId == this.$store.state.i.id || this.$store.state.i.isModerator || this.$store.state.i.isAdmin ? [ | 				...(this.appearNote.userId == this.$i.id || this.$i.isModerator || this.$i.isAdmin ? [ | ||||||
| 					null, | 					null, | ||||||
| 					this.appearNote.userId == this.$store.state.i.id ? { | 					this.appearNote.userId == this.$i.id ? { | ||||||
| 						icon: faEdit, | 						icon: faEdit, | ||||||
| 						text: this.$t('deleteAndEdit'), | 						text: this.$t('deleteAndEdit'), | ||||||
| 						action: this.delEdit | 						action: this.delEdit | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ | ||||||
| 	</XList> | 	</XList> | ||||||
| 
 | 
 | ||||||
| 	<div v-show="more && !reversed" style="margin-top: var(--margin);"> | 	<div v-show="more && !reversed" style="margin-top: var(--margin);"> | ||||||
| 		<button class="_loadMore" v-appear="$store.state.device.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }"> | 		<button class="_loadMore" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }"> | ||||||
| 			<template v-if="!moreFetching">{{ $t('loadMore') }}</template> | 			<template v-if="!moreFetching">{{ $t('loadMore') }}</template> | ||||||
| 			<template v-if="moreFetching"><MkLoading inline/></template> | 			<template v-if="moreFetching"><MkLoading inline/></template> | ||||||
| 		</button> | 		</button> | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| 		<XNotification v-else :notification="notification" :with-time="true" :full="true" class="_panel notification" :key="notification.id"/> | 		<XNotification v-else :notification="notification" :with-time="true" :full="true" class="_panel notification" :key="notification.id"/> | ||||||
| 	</XList> | 	</XList> | ||||||
| 
 | 
 | ||||||
| 	<button class="_loadMore" v-appear="$store.state.device.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" v-show="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }"> | 	<button class="_loadMore" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" v-show="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }"> | ||||||
| 		<template v-if="!moreFetching">{{ $t('loadMore') }}</template> | 		<template v-if="!moreFetching">{{ $t('loadMore') }}</template> | ||||||
| 		<template v-if="moreFetching"><MkLoading inline/></template> | 		<template v-if="moreFetching"><MkLoading inline/></template> | ||||||
| 	</button> | 	</button> | ||||||
|  | @ -59,7 +59,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		allIncludeTypes() { | 		allIncludeTypes() { | ||||||
| 			return this.includeTypes ?? notificationTypes.filter(x => !this.$store.state.i.mutingNotificationTypes.includes(x)); | 			return this.includeTypes ?? notificationTypes.filter(x => !this.$i.mutingNotificationTypes.includes(x)); | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | @ -70,9 +70,9 @@ export default defineComponent({ | ||||||
| 			}, | 			}, | ||||||
| 			deep: true | 			deep: true | ||||||
| 		}, | 		}, | ||||||
| 		// TODO: vue/vuexのバグか仕様かは不明なものの、プロフィール更新するなどして $store.state.i が更新されると、 | 		// TODO: vue/vuexのバグか仕様かは不明なものの、プロフィール更新するなどして $i が更新されると、 | ||||||
| 		// mutingNotificationTypes に変化が無くてもこのハンドラーが呼び出され無駄なリロードが発生するのを直す | 		// mutingNotificationTypes に変化が無くてもこのハンドラーが呼び出され無駄なリロードが発生するのを直す | ||||||
| 		'$store.state.i.mutingNotificationTypes': { | 		'$i.mutingNotificationTypes': { | ||||||
| 			handler() { | 			handler() { | ||||||
| 				if (this.includeTypes === null) { | 				if (this.includeTypes === null) { | ||||||
| 					this.reload(); | 					this.reload(); | ||||||
|  |  | ||||||
|  | @ -49,9 +49,9 @@ export default defineComponent({ | ||||||
| 				canvas.toBlob(blob => { | 				canvas.toBlob(blob => { | ||||||
| 					const data = new FormData(); | 					const data = new FormData(); | ||||||
| 					data.append('file', blob); | 					data.append('file', blob); | ||||||
| 					data.append('i', this.$store.state.i.token); | 					data.append('i', this.$i.token); | ||||||
| 					if (this.$store.state.settings.uploadFolder) { | 					if (this.$store.state.uploadFolder) { | ||||||
| 						data.append('folderId', this.$store.state.settings.uploadFolder); | 						data.append('folderId', this.$store.state.uploadFolder); | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					fetch(apiUrl + '/drive/files/create', { | 					fetch(apiUrl + '/drive/files/create', { | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
| <div class="mrdgzndn"> | <div class="mrdgzndn"> | ||||||
| 	<Mfm :text="text" :is-note="false" :i="$store.state.i" :key="text"/> | 	<Mfm :text="text" :is-note="false" :i="$i" :key="text"/> | ||||||
| 	<MkUrlPreview v-for="url in urls" :url="url" :key="url" class="url"/> | 	<MkUrlPreview v-for="url in urls" :url="url" :key="url" class="url"/> | ||||||
| </div> | </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  | @ -35,9 +35,9 @@ export default defineComponent({ | ||||||
| 	created() { | 	created() { | ||||||
| 		this.hpml = new Hpml(this.page, { | 		this.hpml = new Hpml(this.page, { | ||||||
| 			randomSeed: Math.random(), | 			randomSeed: Math.random(), | ||||||
| 			visitor: this.$store.state.i, | 			visitor: this.$i, | ||||||
| 			url: url, | 			url: url, | ||||||
| 			enableAiScript: !this.$store.state.device.disablePagesScript | 			enableAiScript: !this.$store.state.disablePagesScript | ||||||
| 		}); | 		}); | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -135,8 +135,8 @@ export default defineComponent({ | ||||||
| 			poll: null, | 			poll: null, | ||||||
| 			useCw: false, | 			useCw: false, | ||||||
| 			cw: null, | 			cw: null, | ||||||
| 			localOnly: this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.localOnly : this.$store.state.settings.defaultNoteLocalOnly, | 			localOnly: this.$store.state.rememberNoteVisibility ? this.$store.state.localOnly : this.$store.state.defaultNoteLocalOnly, | ||||||
| 			visibility: this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.visibility : this.$store.state.settings.defaultNoteVisibility, | 			visibility: this.$store.state.rememberNoteVisibility ? this.$store.state.visibility : this.$store.state.defaultNoteVisibility, | ||||||
| 			visibleUsers: [], | 			visibleUsers: [], | ||||||
| 			autocomplete: null, | 			autocomplete: null, | ||||||
| 			draghover: false, | 			draghover: false, | ||||||
|  | @ -198,7 +198,7 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		max(): number { | 		max(): number { | ||||||
| 			return this.$store.state.instance.meta ? this.$store.state.instance.meta.maxNoteTextLength : 1000; | 			return this.$instance ? this.$instance.maxNoteTextLength : 1000; | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | @ -223,8 +223,8 @@ export default defineComponent({ | ||||||
| 				const mention = x.host ? `@${x.username}@${toASCII(x.host)}` : `@${x.username}`; | 				const mention = x.host ? `@${x.username}@${toASCII(x.host)}` : `@${x.username}`; | ||||||
| 
 | 
 | ||||||
| 				// 自分は除外 | 				// 自分は除外 | ||||||
| 				if (this.$store.state.i.username == x.username && x.host == null) continue; | 				if (this.$i.username == x.username && x.host == null) continue; | ||||||
| 				if (this.$store.state.i.username == x.username && x.host == host) continue; | 				if (this.$i.username == x.username && x.host == host) continue; | ||||||
| 
 | 
 | ||||||
| 				// 重複は除外 | 				// 重複は除外 | ||||||
| 				if (this.text.indexOf(`${mention} `) != -1) continue; | 				if (this.text.indexOf(`${mention} `) != -1) continue; | ||||||
|  | @ -243,12 +243,12 @@ export default defineComponent({ | ||||||
| 			this.visibility = this.reply.visibility; | 			this.visibility = this.reply.visibility; | ||||||
| 			if (this.reply.visibility === 'specified') { | 			if (this.reply.visibility === 'specified') { | ||||||
| 				os.api('users/show', { | 				os.api('users/show', { | ||||||
| 					userIds: this.reply.visibleUserIds.filter(uid => uid !== this.$store.state.i.id && uid !== this.reply.userId) | 					userIds: this.reply.visibleUserIds.filter(uid => uid !== this.$i.id && uid !== this.reply.userId) | ||||||
| 				}).then(users => { | 				}).then(users => { | ||||||
| 					this.visibleUsers.push(...users); | 					this.visibleUsers.push(...users); | ||||||
| 				}); | 				}); | ||||||
| 
 | 
 | ||||||
| 				if (this.reply.userId !== this.$store.state.i.id) { | 				if (this.reply.userId !== this.$i.id) { | ||||||
| 					os.api('users/show', { userId: this.reply.userId }).then(user => { | 					os.api('users/show', { userId: this.reply.userId }).then(user => { | ||||||
| 						this.visibleUsers.push(user); | 						this.visibleUsers.push(user); | ||||||
| 					}); | 					}); | ||||||
|  | @ -262,7 +262,7 @@ export default defineComponent({ | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// keep cw when reply | 		// keep cw when reply | ||||||
| 		if (this.$store.state.settings.keepCw && this.reply && this.reply.cw) { | 		if (this.$store.keepCw && this.reply && this.reply.cw) { | ||||||
| 			this.useCw = true; | 			this.useCw = true; | ||||||
| 			this.cw = this.reply.cw; | 			this.cw = this.reply.cw; | ||||||
| 		} | 		} | ||||||
|  | @ -376,7 +376,7 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		upload(file: File, name?: string) { | 		upload(file: File, name?: string) { | ||||||
| 			os.upload(file, this.$store.state.settings.uploadFolder, name).then(res => { | 			os.upload(file, this.$store.state.uploadFolder, name).then(res => { | ||||||
| 				this.files.push(res); | 				this.files.push(res); | ||||||
| 			}); | 			}); | ||||||
| 		}, | 		}, | ||||||
|  | @ -399,14 +399,14 @@ export default defineComponent({ | ||||||
| 			}, { | 			}, { | ||||||
| 				changeVisibility: visibility => { | 				changeVisibility: visibility => { | ||||||
| 					this.visibility = visibility; | 					this.visibility = visibility; | ||||||
| 					if (this.$store.state.settings.rememberNoteVisibility) { | 					if (this.$store.state.rememberNoteVisibility) { | ||||||
| 						this.$store.commit('deviceUser/setVisibility', visibility); | 						this.$store.set('visibility', visibility); | ||||||
| 					} | 					} | ||||||
| 				}, | 				}, | ||||||
| 				changeLocalOnly: localOnly => { | 				changeLocalOnly: localOnly => { | ||||||
| 					this.localOnly = localOnly; | 					this.localOnly = localOnly; | ||||||
| 					if (this.$store.state.settings.rememberNoteVisibility) { | 					if (this.$store.state.rememberNoteVisibility) { | ||||||
| 						this.$store.commit('deviceUser/setLocalOnly', localOnly); | 						this.$store.set('localOnly', localOnly); | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			}, 'closed'); | 			}, 'closed'); | ||||||
|  | @ -440,7 +440,7 @@ export default defineComponent({ | ||||||
| 					const file = item.getAsFile(); | 					const file = item.getAsFile(); | ||||||
| 					const lio = file.name.lastIndexOf('.'); | 					const lio = file.name.lastIndexOf('.'); | ||||||
| 					const ext = lio >= 0 ? file.name.slice(lio) : ''; | 					const ext = lio >= 0 ? file.name.slice(lio) : ''; | ||||||
| 					const formatted = `${formatTimeString(new Date(file.lastModified), this.$store.state.settings.pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`; | 					const formatted = `${formatTimeString(new Date(file.lastModified), this.$store.state.pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`; | ||||||
| 					this.upload(file, formatted); | 					this.upload(file, formatted); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | @ -53,7 +53,7 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 	computed: { | 	computed: { | ||||||
| 		canToggle(): boolean { | 		canToggle(): boolean { | ||||||
| 			return !this.reaction.match(/@\w/) && this.$store.getters.isSignedIn; | 			return !this.reaction.match(/@\w/) && this.$i; | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| 	watch: { | 	watch: { | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 	computed: { | 	computed: { | ||||||
| 		isMe(): boolean { | 		isMe(): boolean { | ||||||
| 			return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId; | 			return this.$i && this.$i.id === this.note.userId; | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -51,7 +51,7 @@ export default defineComponent({ | ||||||
| 			text: '', | 			text: '', | ||||||
| 			flag: false, | 			flag: false, | ||||||
| 			radio: 'misskey', | 			radio: 'misskey', | ||||||
| 			mfm: `Hello world! This is an @example mention. BTW you are @${this.$store.state.i.username}.\nAlso, here is ${config.url} and [example link](${config.url}). for more details, see https://example.com.\nAs you know #misskey is open-source software.` | 			mfm: `Hello world! This is an @example mention. BTW you are @${this.$i.username}.\nAlso, here is ${config.url} and [example link](${config.url}). for more details, see https://example.com.\nAs you know #misskey is open-source software.` | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ | ||||||
| 		<nav class="nav" :class="{ iconOnly, hidden }" v-show="showing"> | 		<nav class="nav" :class="{ iconOnly, hidden }" v-show="showing"> | ||||||
| 			<div> | 			<div> | ||||||
| 				<button class="item _button account" @click="openAccountMenu"> | 				<button class="item _button account" @click="openAccountMenu"> | ||||||
| 					<MkAvatar :user="$store.state.i" class="avatar"/><MkAcct class="text" :user="$store.state.i"/> | 					<MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/> | ||||||
| 				</button> | 				</button> | ||||||
| 				<MkA class="item index" active-class="active" to="/" exact> | 				<MkA class="item index" active-class="active" to="/" exact> | ||||||
| 					<Fa :icon="faHome" fixed-width/><span class="text">{{ $t('timeline') }}</span> | 					<Fa :icon="faHome" fixed-width/><span class="text">{{ $t('timeline') }}</span> | ||||||
|  | @ -25,7 +25,7 @@ | ||||||
| 					</component> | 					</component> | ||||||
| 				</template> | 				</template> | ||||||
| 				<div class="divider"></div> | 				<div class="divider"></div> | ||||||
| 				<button class="item _button" :class="{ active: $route.path === '/instance' || $route.path.startsWith('/instance/') }" v-if="$store.state.i.isAdmin || $store.state.i.isModerator" @click="oepnInstanceMenu"> | 				<button class="item _button" :class="{ active: $route.path === '/instance' || $route.path.startsWith('/instance/') }" v-if="$i.isAdmin || $i.isModerator" @click="oepnInstanceMenu"> | ||||||
| 					<Fa :icon="faServer" fixed-width/><span class="text">{{ $t('instance') }}</span> | 					<Fa :icon="faServer" fixed-width/><span class="text">{{ $t('instance') }}</span> | ||||||
| 				</button> | 				</button> | ||||||
| 				<button class="item _button" @click="more"> | 				<button class="item _button" @click="more"> | ||||||
|  | @ -49,6 +49,7 @@ import { host } from '@/config'; | ||||||
| import { search } from '@/scripts/search'; | import { search } from '@/scripts/search'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
| import { sidebarDef } from '@/sidebar'; | import { sidebarDef } from '@/sidebar'; | ||||||
|  | import { getAccounts, addAccount, login } from '@/account'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	data() { | 	data() { | ||||||
|  | @ -67,7 +68,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		menu(): string[] { | 		menu(): string[] { | ||||||
| 			return this.$store.state.deviceUser.menu; | 			return this.$store.state.menu; | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		otherNavItemIndicated(): boolean { | 		otherNavItemIndicated(): boolean { | ||||||
|  | @ -84,7 +85,7 @@ export default defineComponent({ | ||||||
| 			this.showing = false; | 			this.showing = false; | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		'$store.state.device.sidebarDisplay'() { | 		'$store.reactiveState.sidebarDisplay'() { | ||||||
| 			this.calcViewState(); | 			this.calcViewState(); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
|  | @ -108,8 +109,8 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	methods: { | 	methods: { | ||||||
| 		calcViewState() { | 		calcViewState() { | ||||||
| 			this.iconOnly = (window.innerWidth <= 1279) || (this.$store.state.device.sidebarDisplay === 'icon'); | 			this.iconOnly = (window.innerWidth <= 1279) || (this.$store.state.sidebarDisplay === 'icon'); | ||||||
| 			this.hidden = (window.innerWidth <= 650) || (this.$store.state.device.sidebarDisplay === 'hide'); | 			this.hidden = (window.innerWidth <= 650); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		show() { | 		show() { | ||||||
|  | @ -133,7 +134,8 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		async openAccountMenu(ev) { | 		async openAccountMenu(ev) { | ||||||
| 			const accounts = (await os.api('users/show', { userIds: this.$store.state.device.accounts.map(x => x.id) })).filter(x => x.id !== this.$store.state.i.id); | 			const storedAccounts = getAccounts(); | ||||||
|  | 			const accounts = (await os.api('users/show', { userIds: storedAccounts.map(x => x.id) })).filter(x => x.id !== this.$i.id); | ||||||
| 
 | 
 | ||||||
| 			const accountItems = accounts.map(account => ({ | 			const accountItems = accounts.map(account => ({ | ||||||
| 				type: 'user', | 				type: 'user', | ||||||
|  | @ -144,8 +146,8 @@ export default defineComponent({ | ||||||
| 			os.modalMenu([...[{ | 			os.modalMenu([...[{ | ||||||
| 				type: 'link', | 				type: 'link', | ||||||
| 				text: this.$t('profile'), | 				text: this.$t('profile'), | ||||||
| 				to: `/@${ this.$store.state.i.username }`, | 				to: `/@${ this.$i.username }`, | ||||||
| 				avatar: this.$store.state.i, | 				avatar: this.$i, | ||||||
| 			}, null, ...accountItems, { | 			}, null, ...accountItems, { | ||||||
| 				icon: faPlus, | 				icon: faPlus, | ||||||
| 				text: this.$t('addAcount'), | 				text: this.$t('addAcount'), | ||||||
|  | @ -169,7 +171,7 @@ export default defineComponent({ | ||||||
| 				text: this.$t('dashboard'), | 				text: this.$t('dashboard'), | ||||||
| 				to: '/instance', | 				to: '/instance', | ||||||
| 				icon: faTachometerAlt, | 				icon: faTachometerAlt, | ||||||
| 			}, null, this.$store.state.i.isAdmin ? { | 			}, null, this.$i.isAdmin ? { | ||||||
| 				type: 'link', | 				type: 'link', | ||||||
| 				text: this.$t('settings'), | 				text: this.$t('settings'), | ||||||
| 				to: '/instance/settings', | 				to: '/instance/settings', | ||||||
|  | @ -230,7 +232,7 @@ export default defineComponent({ | ||||||
| 		addAcount() { | 		addAcount() { | ||||||
| 			os.popup(import('./signin-dialog.vue'), {}, { | 			os.popup(import('./signin-dialog.vue'), {}, { | ||||||
| 				done: res => { | 				done: res => { | ||||||
| 					this.$store.dispatch('addAcount', res); | 					addAccount(res.id, res.i); | ||||||
| 					os.success(); | 					os.success(); | ||||||
| 				}, | 				}, | ||||||
| 			}, 'closed'); | 			}, 'closed'); | ||||||
|  | @ -239,30 +241,20 @@ export default defineComponent({ | ||||||
| 		createAccount() { | 		createAccount() { | ||||||
| 			os.popup(import('./signup-dialog.vue'), {}, { | 			os.popup(import('./signup-dialog.vue'), {}, { | ||||||
| 				done: res => { | 				done: res => { | ||||||
| 					this.$store.dispatch('addAcount', res); | 					addAccount(res.id, res.i); | ||||||
| 					this.switchAccountWithToken(res.i); | 					this.switchAccountWithToken(res.i); | ||||||
| 				}, | 				}, | ||||||
| 			}, 'closed'); | 			}, 'closed'); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		switchAccount(account: any) { | 		switchAccount(account: any) { | ||||||
| 			const token = this.$store.state.device.accounts.find((x: any) => x.id === account.id).token; | 			const storedAccounts = getAccounts(); | ||||||
|  | 			const token = storedAccounts.find(x => x.id === account.id).token; | ||||||
| 			this.switchAccountWithToken(token); | 			this.switchAccountWithToken(token); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		switchAccountWithToken(token: string) { | 		switchAccountWithToken(token: string) { | ||||||
| 			os.waiting(); | 			login(token); | ||||||
| 
 |  | ||||||
| 			os.api('i', {}, token).then((i: any) => { |  | ||||||
| 				this.$store.dispatch('switchAccount', { |  | ||||||
| 					...i, |  | ||||||
| 					token: token |  | ||||||
| 				}).then(() => { |  | ||||||
| 					this.$nextTick(() => { |  | ||||||
| 						location.reload(); |  | ||||||
| 					}); |  | ||||||
| 				}); |  | ||||||
| 			}); |  | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -56,6 +56,7 @@ import MkInput from './ui/input.vue'; | ||||||
| import { apiUrl, host } from '@/config'; | import { apiUrl, host } from '@/config'; | ||||||
| import { byteify, hexify } from '@/scripts/2fa'; | import { byteify, hexify } from '@/scripts/2fa'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { login } from '@/account'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
|  | @ -97,7 +98,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		meta() { | 		meta() { | ||||||
| 			return this.$store.state.instance.meta; | 			return this.$instance; | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | @ -114,8 +115,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 		onLogin(res) { | 		onLogin(res) { | ||||||
| 			if (this.autoSet) { | 			if (this.autoSet) { | ||||||
| 				localStorage.setItem('i', res.i); | 				login(res.i); | ||||||
| 				location.reload(); |  | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -59,6 +59,7 @@ import MkButton from './ui/button.vue'; | ||||||
| import MkInput from './ui/input.vue'; | import MkInput from './ui/input.vue'; | ||||||
| import MkSwitch from './ui/switch.vue'; | import MkSwitch from './ui/switch.vue'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { login } from '@/account'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
|  | @ -99,7 +100,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		meta() { | 		meta() { | ||||||
| 			return this.$store.state.instance.meta; | 			return this.$instance; | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		shouldDisableSubmitting(): boolean { | 		shouldDisableSubmitting(): boolean { | ||||||
|  | @ -184,8 +185,7 @@ export default defineComponent({ | ||||||
| 					this.$emit('signup', res); | 					this.$emit('signup', res); | ||||||
| 
 | 
 | ||||||
| 					if (this.autoSet) { | 					if (this.autoSet) { | ||||||
| 						localStorage.setItem('i', res.i); | 						login(res.i); | ||||||
| 						location.reload(); |  | ||||||
| 					} | 					} | ||||||
| 				}); | 				}); | ||||||
| 			}).catch(() => { | 			}).catch(() => { | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| 		<span v-if="note.isHidden" style="opacity: 0.5">({{ $t('private') }})</span> | 		<span v-if="note.isHidden" style="opacity: 0.5">({{ $t('private') }})</span> | ||||||
| 		<span v-if="note.deletedAt" style="opacity: 0.5">({{ $t('deleted') }})</span> | 		<span v-if="note.deletedAt" style="opacity: 0.5">({{ $t('deleted') }})</span> | ||||||
| 		<MkA class="reply" v-if="note.replyId" :to="`/notes/${note.replyId}`"><Fa :icon="faReply"/></MkA> | 		<MkA class="reply" v-if="note.replyId" :to="`/notes/${note.replyId}`"><Fa :icon="faReply"/></MkA> | ||||||
| 		<Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$store.state.i" :custom-emojis="note.emojis"/> | 		<Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i" :custom-emojis="note.emojis"/> | ||||||
| 		<MkA class="rp" v-if="note.renoteId" :to="`/notes/${note.renoteId}`">RN: ...</MkA> | 		<MkA class="rp" v-if="note.renoteId" :to="`/notes/${note.renoteId}`">RN: ...</MkA> | ||||||
| 	</div> | 	</div> | ||||||
| 	<details v-if="note.files.length > 0"> | 	<details v-if="note.files.length > 0"> | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <template> | <template> | ||||||
| <XNotes :class="{ _noGap_: !$store.state.device.showGapBetweenNotesInTimeline }" ref="tl" :pagination="pagination" @before="$emit('before')" @after="e => $emit('after', e)" @queue="$emit('queue', $event)"/> | <XNotes :class="{ _noGap_: !$store.state.showGapBetweenNotesInTimeline }" ref="tl" :pagination="pagination" @before="$emit('before')" @after="e => $emit('after', e)" @queue="$emit('queue', $event)"/> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|  | @ -51,9 +51,9 @@ export default defineComponent({ | ||||||
| 			connection2: null, | 			connection2: null, | ||||||
| 			pagination: null, | 			pagination: null, | ||||||
| 			baseQuery: { | 			baseQuery: { | ||||||
| 				includeMyRenotes: this.$store.state.settings.showMyRenotes, | 				includeMyRenotes: this.$store.state.showMyRenotes, | ||||||
| 				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes, | 				includeRenotedMyNotes: this.$store.state.showRenotedMyNotes, | ||||||
| 				includeLocalRenotes: this.$store.state.settings.showLocalRenotes | 				includeLocalRenotes: this.$store.state.showLocalRenotes | ||||||
| 			}, | 			}, | ||||||
| 			query: {}, | 			query: {}, | ||||||
| 		}; | 		}; | ||||||
|  | @ -66,7 +66,7 @@ export default defineComponent({ | ||||||
| 			this.$emit('note'); | 			this.$emit('note'); | ||||||
| 
 | 
 | ||||||
| 			if (this.sound) { | 			if (this.sound) { | ||||||
| 				sound.play(note.userId === this.$store.state.i.id ? 'noteMy' : 'note'); | 				sound.play(note.userId === this.$i.id ? 'noteMy' : 'note'); | ||||||
| 			} | 			} | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <template> | <template> | ||||||
| <transition :name="$store.state.device.animation ? 'fade' : ''" appear> | <transition :name="$store.state.animation ? 'fade' : ''" appear> | ||||||
| 	<div class="nvlagfpb" @contextmenu.prevent.stop="() => {}"> | 	<div class="nvlagfpb" @contextmenu.prevent.stop="() => {}"> | ||||||
| 		<MkMenu :items="items" @close="$emit('closed')" class="_popup _shadow" :align="'left'"/> | 		<MkMenu :items="items" @close="$emit('closed')" class="_popup _shadow" :align="'left'"/> | ||||||
| 	</div> | 	</div> | ||||||
|  |  | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| <template> | <template> | ||||||
| <div class="mk-modal" v-hotkey.global="keymap" :style="{ pointerEvents: showing ? 'auto' : 'none', '--transformOrigin': transformOrigin }"> | <div class="mk-modal" v-hotkey.global="keymap" :style="{ pointerEvents: showing ? 'auto' : 'none', '--transformOrigin': transformOrigin }"> | ||||||
| 	<transition :name="$store.state.device.animation ? 'modal-bg' : ''" appear> | 	<transition :name="$store.state.animation ? 'modal-bg' : ''" appear> | ||||||
| 		<div class="bg _modalBg" v-if="showing" @click="onBgClick"></div> | 		<div class="bg _modalBg" v-if="showing" @click="onBgClick"></div> | ||||||
| 	</transition> | 	</transition> | ||||||
| 	<div class="content" :class="{ popup, fixed, top: position === 'top' }" @click.self="onBgClick" ref="content"> | 	<div class="content" :class="{ popup, fixed, top: position === 'top' }" @click.self="onBgClick" ref="content"> | ||||||
| 		<transition :name="$store.state.device.animation ? popup ? 'modal-popup-content' : 'modal-content' : ''" appear @after-leave="$emit('closed')" @after-enter="childRendered"> | 		<transition :name="$store.state.animation ? popup ? 'modal-popup-content' : 'modal-content' : ''" appear @after-leave="$emit('closed')" @after-enter="childRendered"> | ||||||
| 			<slot v-if="showing"></slot> | 			<slot v-if="showing"></slot> | ||||||
| 		</transition> | 		</transition> | ||||||
| 	</div> | 	</div> | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| 		<slot name="empty"></slot> | 		<slot name="empty"></slot> | ||||||
| 	</div> | 	</div> | ||||||
| 	<div class="more" v-show="more" key="_more_"> | 	<div class="more" v-show="more" key="_more_"> | ||||||
| 		<MkButton class="button" v-appear="$store.state.device.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary> | 		<MkButton class="button" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary> | ||||||
| 			<template v-if="!moreFetching">{{ $t('loadMore') }}</template> | 			<template v-if="!moreFetching">{{ $t('loadMore') }}</template> | ||||||
| 			<template v-if="moreFetching"><MkLoading inline/></template> | 			<template v-if="moreFetching"><MkLoading inline/></template> | ||||||
| 		</MkButton> | 		</MkButton> | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <template> | <template> | ||||||
| <transition :name="$store.state.device.animation ? 'window' : ''" appear @after-leave="$emit('closed')"> | <transition :name="$store.state.animation ? 'window' : ''" appear @after-leave="$emit('closed')"> | ||||||
| 	<div class="ebkgocck" v-if="showing"> | 	<div class="ebkgocck" v-if="showing"> | ||||||
| 		<div class="body _popup _shadow _narrow_" @mousedown="onBodyMousedown" @keydown="onKeydown"> | 		<div class="body _popup _shadow _narrow_" @mousedown="onBodyMousedown" @keydown="onKeydown"> | ||||||
| 			<div class="header" @contextmenu.prevent.stop="onContextmenu"> | 			<div class="header" @contextmenu.prevent.stop="onContextmenu"> | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| 	<iframe :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" :width="player.width || '100%'" :heigth="player.height || 250" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen /> | 	<iframe :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" :width="player.width || '100%'" :heigth="player.height || 250" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen /> | ||||||
| </div> | </div> | ||||||
| <div v-else-if="tweetId && tweetExpanded" class="twitter" ref="twitter"> | <div v-else-if="tweetId && tweetExpanded" class="twitter" ref="twitter"> | ||||||
| 	<iframe ref="tweet" scrolling="no" frameborder="no" :style="{ position: 'relative', left: `${tweetLeft}px`, width: `${tweetLeft < 0 ? 'auto' : '100%'}`, height: `${tweetHeight}px` }" :src="`https://platform.twitter.com/embed/index.html?embedId=${embedId}&hideCard=false&hideThread=false&lang=en&theme=${$store.state.device.darkMode ? 'dark' : 'light'}&id=${tweetId}`"></iframe> | 	<iframe ref="tweet" scrolling="no" frameborder="no" :style="{ position: 'relative', left: `${tweetLeft}px`, width: `${tweetLeft < 0 ? 'auto' : '100%'}`, height: `${tweetHeight}px` }" :src="`https://platform.twitter.com/embed/index.html?embedId=${embedId}&hideCard=false&hideThread=false&lang=en&theme=${$store.state.darkMode ? 'dark' : 'light'}&id=${tweetId}`"></iframe> | ||||||
| </div> | </div> | ||||||
| <div v-else class="mk-url-preview" v-size="{ max: [400, 350] }"> | <div v-else class="mk-url-preview" v-size="{ max: [400, 350] }"> | ||||||
| 	<transition name="zoom" mode="out-in"> | 	<transition name="zoom" mode="out-in"> | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ | ||||||
| 	</div> | 	</div> | ||||||
| 	<div class="description"> | 	<div class="description"> | ||||||
| 		<div class="mfm" v-if="user.description"> | 		<div class="mfm" v-if="user.description"> | ||||||
| 			<Mfm :text="user.description" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/> | 			<Mfm :text="user.description" :author="user" :i="$i" :custom-emojis="user.emojis"/> | ||||||
| 		</div> | 		</div> | ||||||
| 		<span v-else style="opacity: 0.7;">{{ $t('noAccountDescription') }}</span> | 		<span v-else style="opacity: 0.7;">{{ $t('noAccountDescription') }}</span> | ||||||
| 	</div> | 	</div> | ||||||
|  | @ -23,7 +23,7 @@ | ||||||
| 			<p>{{ $t('followers') }}</p><span>{{ user.followersCount }}</span> | 			<p>{{ $t('followers') }}</p><span>{{ user.followersCount }}</span> | ||||||
| 		</div> | 		</div> | ||||||
| 	</div> | 	</div> | ||||||
| 	<MkFollowButton class="koudoku-button" v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" mini/> | 	<MkFollowButton class="koudoku-button" v-if="$i && user.id != $i.id" :user="user" mini/> | ||||||
| </div> | </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ | ||||||
| 	<div class="users"> | 	<div class="users"> | ||||||
| 		<MkUserInfo class="user" v-for="user in users" :user="user" :key="user.id"/> | 		<MkUserInfo class="user" v-for="user in users" :user="user" :key="user.id"/> | ||||||
| 	</div> | 	</div> | ||||||
| 	<button class="more" v-appear="$store.state.device.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" :class="{ fetching: moreFetching }" v-show="more" :disabled="moreFetching"> | 	<button class="more" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" :class="{ fetching: moreFetching }" v-show="more" :disabled="moreFetching"> | ||||||
| 		<template v-if="moreFetching"><Fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? $t('loading') : $t('loadMore') }} | 		<template v-if="moreFetching"><Fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? $t('loading') : $t('loadMore') }} | ||||||
| 	</button> | 	</button> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ | ||||||
| 				<p class="username"><MkAcct :user="user"/></p> | 				<p class="username"><MkAcct :user="user"/></p> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div class="description"> | 			<div class="description"> | ||||||
| 				<Mfm v-if="user.description" :text="user.description" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/> | 				<Mfm v-if="user.description" :text="user.description" :author="user" :i="$i" :custom-emojis="user.emojis"/> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div class="status"> | 			<div class="status"> | ||||||
| 				<div> | 				<div> | ||||||
|  | @ -22,7 +22,7 @@ | ||||||
| 					<p>{{ $t('followers') }}</p><span>{{ user.followersCount }}</span> | 					<p>{{ $t('followers') }}</p><span>{{ user.followersCount }}</span> | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
| 			<MkFollowButton class="koudoku-button" v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" mini/> | 			<MkFollowButton class="koudoku-button" v-if="$i && user.id != $i.id" :user="user" mini/> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div v-else> | 		<div v-else> | ||||||
| 			<MkLoading/> | 			<MkLoading/> | ||||||
|  |  | ||||||
|  | @ -79,7 +79,7 @@ export default defineComponent({ | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		this.recentUsers = await os.api('users/show', { | 		this.recentUsers = await os.api('users/show', { | ||||||
| 			userIds: this.$store.state.device.recentlyUsedUsers | 			userIds: this.$store.state.recentlyUsedUsers | ||||||
| 		}); | 		}); | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | @ -108,10 +108,10 @@ export default defineComponent({ | ||||||
| 			this.$refs.dialog.close(); | 			this.$refs.dialog.close(); | ||||||
| 
 | 
 | ||||||
| 			// 最近使ったユーザー更新 | 			// 最近使ったユーザー更新 | ||||||
| 			let recents = this.$store.state.device.recentlyUsedUsers; | 			let recents = this.$store.state.recentlyUsedUsers; | ||||||
| 			recents = recents.filter(x => x !== this.selected.id); | 			recents = recents.filter(x => x !== this.selected.id); | ||||||
| 			recents.unshift(this.selected.id); | 			recents.unshift(this.selected.id); | ||||||
| 			this.$store.commit('device/set', { key: 'recentlyUsedUsers', value: recents.splice(0, 16) }); | 			this.$store.set('recentlyUsedUsers', recents.splice(0, 16)); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		cancel() { | 		cancel() { | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ | ||||||
| 			</div> | 			</div> | ||||||
| 		</MkA> | 		</MkA> | ||||||
| 	</div> | 	</div> | ||||||
| 	<button class="more _button" v-appear="$store.state.device.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" v-show="more" :disabled="moreFetching"> | 	<button class="more _button" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" v-show="more" :disabled="moreFetching"> | ||||||
| 		<template v-if="!moreFetching">{{ $t('loadMore') }}</template> | 		<template v-if="!moreFetching">{{ $t('loadMore') }}</template> | ||||||
| 		<template v-if="moreFetching"><Fa :icon="faSpinner" pulse fixed-width/></template> | 		<template v-if="moreFetching"><Fa :icon="faSpinner" pulse fixed-width/></template> | ||||||
| 	</button> | 	</button> | ||||||
|  |  | ||||||
|  | @ -4,26 +4,54 @@ | ||||||
| 
 | 
 | ||||||
| import '@/style.scss'; | import '@/style.scss'; | ||||||
| 
 | 
 | ||||||
| import { createApp } from 'vue'; | // TODO: そのうち消す
 | ||||||
|  | if (localStorage.getItem('vuex') != null) { | ||||||
|  | 	const vuex = JSON.parse(localStorage.getItem('vuex')); | ||||||
|  | 
 | ||||||
|  | 	localStorage.setItem('accounts', JSON.stringify(vuex.device.accounts)); | ||||||
|  | 	localStorage.setItem('miux:themes', JSON.stringify(vuex.device.themes)); | ||||||
|  | 
 | ||||||
|  | 	for (const [k, v] of 	Object.entries(vuex.device.userData)) { | ||||||
|  | 		localStorage.setItem('pizzax::base::' + k, JSON.stringify({ | ||||||
|  | 			widgets: v.widgets | ||||||
|  | 		})); | ||||||
|  | 
 | ||||||
|  | 		if (v.deck) { | ||||||
|  | 			localStorage.setItem('pizzax::deck::' + k, JSON.stringify({ | ||||||
|  | 				columns: v.deck.columns, | ||||||
|  | 				layout: v.deck.layout, | ||||||
|  | 			})); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	localStorage.removeItem('vuex'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | import { createApp, watch } from 'vue'; | ||||||
| import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; | import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; | ||||||
| 
 | 
 | ||||||
| import widgets from './widgets'; | import widgets from '@/widgets'; | ||||||
| import directives from './directives'; | import directives from '@/directives'; | ||||||
| import components from '@/components'; | import components from '@/components'; | ||||||
| import { version, apiUrl, ui } from '@/config'; | import { version, ui } from '@/config'; | ||||||
| import { store } from './store'; | import { router } from '@/router'; | ||||||
| import { router } from './router'; |  | ||||||
| import { applyTheme } from '@/scripts/theme'; | import { applyTheme } from '@/scripts/theme'; | ||||||
| import { isDeviceDarkmode } from '@/scripts/is-device-darkmode'; | import { isDeviceDarkmode } from '@/scripts/is-device-darkmode'; | ||||||
| import { i18n, lang } from './i18n'; | import { i18n, lang } from '@/i18n'; | ||||||
| import { stream, isMobile, dialog } from '@/os'; | import { stream, isMobile, dialog } from '@/os'; | ||||||
| import * as sound from './scripts/sound'; | import * as sound from '@/scripts/sound'; | ||||||
|  | import { $i, refreshAccount, login, updateAccount, signout } from '@/account'; | ||||||
|  | import { defaultStore, ColdDeviceStorage } from '@/store'; | ||||||
|  | import { fetchInstance, instance } from '@/instance'; | ||||||
| 
 | 
 | ||||||
| console.info(`Misskey v${version}`); | console.info(`Misskey v${version}`); | ||||||
| 
 | 
 | ||||||
| if (_DEV_) { | if (_DEV_) { | ||||||
| 	console.warn('Development mode!!!'); | 	console.warn('Development mode!!!'); | ||||||
| 
 | 
 | ||||||
|  | 	(window as any).$i = $i; | ||||||
|  | 	(window as any).$store = defaultStore; | ||||||
|  | 
 | ||||||
| 	window.addEventListener('error', event => { | 	window.addEventListener('error', event => { | ||||||
| 		console.error(event); | 		console.error(event); | ||||||
| 		/* | 		/* | ||||||
|  | @ -81,80 +109,52 @@ html.setAttribute('lang', lang); | ||||||
| //#endregion
 | //#endregion
 | ||||||
| 
 | 
 | ||||||
| //#region Fetch user
 | //#region Fetch user
 | ||||||
| const signout = () => { | if ($i && $i.token) { | ||||||
| 	store.dispatch('logout'); | 	if (_DEV_) { | ||||||
| 	location.href = '/'; | 		console.log('account cache found. refreshing...'); | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // ユーザーをフェッチしてコールバックする
 |  | ||||||
| const fetchme = (token) => new Promise((done, fail) => { |  | ||||||
| 	// Fetch user
 |  | ||||||
| 	fetch(`${apiUrl}/i`, { |  | ||||||
| 		method: 'POST', |  | ||||||
| 		body: JSON.stringify({ |  | ||||||
| 			i: token |  | ||||||
| 		}) |  | ||||||
| 	}) |  | ||||||
| 	.then(res => { |  | ||||||
| 		// When failed to authenticate user
 |  | ||||||
| 		if (res.status !== 200 && res.status < 500) { |  | ||||||
| 			return signout(); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Parse response
 |  | ||||||
| 		res.json().then(i => { |  | ||||||
| 			i.token = token; |  | ||||||
| 			done(i); |  | ||||||
| 		}); |  | ||||||
| 	}) |  | ||||||
| 	.catch(fail); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| // キャッシュがあったとき
 |  | ||||||
| if (store.state.i != null) { |  | ||||||
| 	// TODO: i.token が null になるケースってどんな時だっけ?
 |  | ||||||
| 	if (store.state.i.token == null) { |  | ||||||
| 		signout(); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// 後から新鮮なデータをフェッチ
 | 	refreshAccount(); | ||||||
| 	fetchme(store.state.i.token).then(freshData => { |  | ||||||
| 		store.dispatch('mergeMe', freshData); |  | ||||||
| 	}); |  | ||||||
| } else { | } else { | ||||||
| 	// Get token from localStorage
 | 	if (_DEV_) { | ||||||
| 	let i = localStorage.getItem('i'); | 		console.log('no account cache found.'); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// 連携ログインの場合用にCookieを参照する
 | 	// 連携ログインの場合用にCookieを参照する
 | ||||||
| 	if (i == null || i === 'null') { | 	const i = (document.cookie.match(/igi=(\w+)/) || [null, null])[1]; | ||||||
| 		i = (document.cookie.match(/igi=(\w+)/) || [null, null])[1]; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	if (i != null && i !== 'null') { | 	if (i != null && i !== 'null') { | ||||||
|  | 		if (_DEV_) { | ||||||
|  | 			console.log('signing...'); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		try { | 		try { | ||||||
| 			document.body.innerHTML = '<div>Please wait...</div>'; | 			document.body.innerHTML = '<div>Please wait...</div>'; | ||||||
| 			const me = await fetchme(i); | 			await login(i); | ||||||
| 			await store.dispatch('login', me); |  | ||||||
| 			location.reload(); | 			location.reload(); | ||||||
| 		} catch (e) { | 		} catch (e) { | ||||||
| 			// Render the error screen
 | 			// Render the error screen
 | ||||||
| 			// TODO: ちゃんとしたコンポーネントをレンダリングする(v10とかのトラブルシューティングゲーム付きのやつみたいな)
 | 			// TODO: ちゃんとしたコンポーネントをレンダリングする(v10とかのトラブルシューティングゲーム付きのやつみたいな)
 | ||||||
| 			document.body.innerHTML = '<div id="err">Oops!</div>'; | 			document.body.innerHTML = '<div id="err">Oops!</div>'; | ||||||
| 		} | 		} | ||||||
|  | 	} else { | ||||||
|  | 		if (_DEV_) { | ||||||
|  | 			console.log('not signed in'); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| //#endregion
 | //#endregion
 | ||||||
| 
 | 
 | ||||||
| store.dispatch('instance/fetch').then(() => { | fetchInstance().then(() => { | ||||||
| 	// Init service worker
 | 	// Init service worker
 | ||||||
| 	//if (this.store.state.instance.meta.swPublickey) this.registerSw(this.store.state.instance.meta.swPublickey);
 | 	//if (this.store.state.instance.meta.swPublickey) this.registerSw(this.store.state.instance.meta.swPublickey);
 | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| stream.init(store.state.i); | stream.init($i); | ||||||
| 
 | 
 | ||||||
| const app = createApp(await ( | const app = createApp(await ( | ||||||
| 	window.location.search === '?zen' ? import('@/ui/zen.vue') : | 	window.location.search === '?zen' ? import('@/ui/zen.vue') : | ||||||
| 	!store.getters.isSignedIn         ? import('@/ui/visitor.vue') : | 	!$i                               ? import('@/ui/visitor.vue') : | ||||||
| 	ui === 'deck'                     ? import('@/ui/deck.vue') : | 	ui === 'deck'                     ? import('@/ui/deck.vue') : | ||||||
| 	ui === 'desktop'                  ? import('@/ui/desktop.vue') : | 	ui === 'desktop'                  ? import('@/ui/desktop.vue') : | ||||||
| 	import('@/ui/default.vue') | 	import('@/ui/default.vue') | ||||||
|  | @ -164,7 +164,12 @@ if (_DEV_) { | ||||||
| 	app.config.performance = true; | 	app.config.performance = true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| app.use(store); | app.config.globalProperties = { | ||||||
|  | 	$i, | ||||||
|  | 	$store: defaultStore, | ||||||
|  | 	$instance: instance, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| app.use(router); | app.use(router); | ||||||
| app.use(i18n); | app.use(i18n); | ||||||
| // eslint-disable-next-line vue/component-definition-name-casing
 | // eslint-disable-next-line vue/component-definition-name-casing
 | ||||||
|  | @ -180,46 +185,34 @@ await router.isReady(); | ||||||
| 
 | 
 | ||||||
| app.mount('body'); | app.mount('body'); | ||||||
| 
 | 
 | ||||||
| // 他のタブと永続化されたstateを同期
 | watch(defaultStore.reactiveState.darkMode, (darkMode) => { | ||||||
| window.addEventListener('storage', e => { |  | ||||||
| 	if (e.key === 'vuex') { |  | ||||||
| 		store.replaceState({ |  | ||||||
| 			...store.state, |  | ||||||
| 			...JSON.parse(e.newValue) |  | ||||||
| 		}); |  | ||||||
| 	} else if (e.key === 'i') { |  | ||||||
| 		location.reload(); |  | ||||||
| 	} |  | ||||||
| }, false); |  | ||||||
| 
 |  | ||||||
| store.watch(state => state.device.darkMode, darkMode => { |  | ||||||
| 	import('@/scripts/theme').then(({ builtinThemes }) => { | 	import('@/scripts/theme').then(({ builtinThemes }) => { | ||||||
| 		const themes = builtinThemes.concat(store.state.device.themes); | 		const themes = builtinThemes.concat(ColdDeviceStorage.get('themes')); | ||||||
| 		applyTheme(themes.find(x => x.id === (darkMode ? store.state.device.darkTheme : store.state.device.lightTheme))); | 		applyTheme(themes.find(x => x.id === (darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme')))); | ||||||
| 	}); | 	}); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| //#region Sync dark mode
 | //#region Sync dark mode
 | ||||||
| if (store.state.device.syncDeviceDarkMode) { | if (ColdDeviceStorage.get('syncDeviceDarkMode')) { | ||||||
| 	store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() }); | 	defaultStore.set('darkMode', isDeviceDarkmode()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => { | window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => { | ||||||
| 	if (store.state.device.syncDeviceDarkMode) { | 	if (ColdDeviceStorage.get('syncDeviceDarkMode')) { | ||||||
| 		store.commit('device/set', { key: 'darkMode', value: mql.matches }); | 		defaultStore.set('darkMode', mql.matches); | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
| //#endregion
 | //#endregion
 | ||||||
| 
 | 
 | ||||||
| store.watch(state => state.device.useBlurEffectForModal, v => { | watch(defaultStore.reactiveState.useBlurEffectForModal, v => { | ||||||
| 	document.documentElement.style.setProperty('--modalBgFilter', v ? 'blur(4px)' : 'none'); | 	document.documentElement.style.setProperty('--modalBgFilter', v ? 'blur(4px)' : 'none'); | ||||||
| }, { immediate: true }); | }, { immediate: true }); | ||||||
| 
 | 
 | ||||||
| let reloadDialogShowing = false; | let reloadDialogShowing = false; | ||||||
| stream.on('_disconnected_', async () => { | stream.on('_disconnected_', async () => { | ||||||
| 	if (store.state.device.serverDisconnectedBehavior === 'reload') { | 	if (defaultStore.state.serverDisconnectedBehavior === 'reload') { | ||||||
| 		location.reload(); | 		location.reload(); | ||||||
| 	} else if (store.state.device.serverDisconnectedBehavior === 'dialog') { | 	} else if (defaultStore.state.serverDisconnectedBehavior === 'dialog') { | ||||||
| 		if (reloadDialogShowing) return; | 		if (reloadDialogShowing) return; | ||||||
| 		reloadDialogShowing = true; | 		reloadDialogShowing = true; | ||||||
| 		const { canceled } = await dialog({ | 		const { canceled } = await dialog({ | ||||||
|  | @ -240,13 +233,13 @@ stream.on('emojiAdded', data => { | ||||||
| 	//store.commit('instance/set', );
 | 	//store.commit('instance/set', );
 | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| for (const plugin of store.state.deviceUser.plugins.filter(p => p.active)) { | for (const plugin of ColdDeviceStorage.get('plugins').filter(p => p.active)) { | ||||||
| 	import('./plugin').then(({ install }) => { | 	import('./plugin').then(({ install }) => { | ||||||
| 		install(plugin); | 		install(plugin); | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| if (store.getters.isSignedIn) { | if ($i) { | ||||||
| 	if ('Notification' in window) { | 	if ('Notification' in window) { | ||||||
| 		// 許可を得ていなかったらリクエスト
 | 		// 許可を得ていなかったらリクエスト
 | ||||||
| 		if (Notification.permission === 'default') { | 		if (Notification.permission === 'default') { | ||||||
|  | @ -258,103 +251,73 @@ if (store.getters.isSignedIn) { | ||||||
| 
 | 
 | ||||||
| 	// 自分の情報が更新されたとき
 | 	// 自分の情報が更新されたとき
 | ||||||
| 	main.on('meUpdated', i => { | 	main.on('meUpdated', i => { | ||||||
| 		store.dispatch('mergeMe', i); | 		updateAccount(i); | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('readAllNotifications', () => { | 	main.on('readAllNotifications', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadNotification: false }); | ||||||
| 			hasUnreadNotification: false |  | ||||||
| 		}); |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('unreadNotification', () => { | 	main.on('unreadNotification', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadNotification: true }); | ||||||
| 			hasUnreadNotification: true |  | ||||||
| 		}); |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('unreadMention', () => { | 	main.on('unreadMention', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadMentions: true }); | ||||||
| 			hasUnreadMentions: true |  | ||||||
| 		}); |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('readAllUnreadMentions', () => { | 	main.on('readAllUnreadMentions', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadMentions: false }); | ||||||
| 			hasUnreadMentions: false |  | ||||||
| 		}); |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('unreadSpecifiedNote', () => { | 	main.on('unreadSpecifiedNote', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadSpecifiedNotes: true }); | ||||||
| 			hasUnreadSpecifiedNotes: true |  | ||||||
| 		}); |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('readAllUnreadSpecifiedNotes', () => { | 	main.on('readAllUnreadSpecifiedNotes', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadSpecifiedNotes: false }); | ||||||
| 			hasUnreadSpecifiedNotes: false |  | ||||||
| 		}); |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('readAllMessagingMessages', () => { | 	main.on('readAllMessagingMessages', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadMessagingMessage: false }); | ||||||
| 			hasUnreadMessagingMessage: false |  | ||||||
| 		}); |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('unreadMessagingMessage', () => { | 	main.on('unreadMessagingMessage', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadMessagingMessage: true }); | ||||||
| 			hasUnreadMessagingMessage: true |  | ||||||
| 		}); |  | ||||||
| 
 |  | ||||||
| 		sound.play('chatBg'); | 		sound.play('chatBg'); | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('readAllAntennas', () => { | 	main.on('readAllAntennas', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadAntenna: false }); | ||||||
| 			hasUnreadAntenna: false |  | ||||||
| 		}); |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('unreadAntenna', () => { | 	main.on('unreadAntenna', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadAntenna: true }); | ||||||
| 			hasUnreadAntenna: true |  | ||||||
| 		}); |  | ||||||
| 
 |  | ||||||
| 		sound.play('antenna'); | 		sound.play('antenna'); | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('readAllAnnouncements', () => { | 	main.on('readAllAnnouncements', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadAnnouncement: false }); | ||||||
| 			hasUnreadAnnouncement: false |  | ||||||
| 		}); |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('readAllChannels', () => { | 	main.on('readAllChannels', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadChannel: false }); | ||||||
| 			hasUnreadChannel: false |  | ||||||
| 		}); |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('unreadChannel', () => { | 	main.on('unreadChannel', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadChannel: true }); | ||||||
| 			hasUnreadChannel: true |  | ||||||
| 		}); |  | ||||||
| 
 |  | ||||||
| 		sound.play('channel'); | 		sound.play('channel'); | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('readAllAnnouncements', () => { | 	main.on('readAllAnnouncements', () => { | ||||||
| 		store.dispatch('mergeMe', { | 		updateAccount({ hasUnreadAnnouncement: false }); | ||||||
| 			hasUnreadAnnouncement: false |  | ||||||
| 		}); |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	main.on('clientSettingUpdated', x => { | 	main.on('clientSettingUpdated', x => { | ||||||
| 		store.commit('settings/set', { | 		updateAccount({ | ||||||
| 			key: x.key, | 			clientData: { | ||||||
| 			value: x.value | 				[x.key]: x.value | ||||||
|  | 			} | ||||||
| 		}); | 		}); | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
|  | @ -364,4 +327,3 @@ if (store.getters.isSignedIn) { | ||||||
| 		signout(); | 		signout(); | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 |  | ||||||
|  |  | ||||||
							
								
								
									
										45
									
								
								src/client/instance.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/client/instance.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | ||||||
|  | import { computed, reactive } from 'vue'; | ||||||
|  | import { api } from './os'; | ||||||
|  | 
 | ||||||
|  | // TODO: 他のタブと永続化されたstateを同期
 | ||||||
|  | 
 | ||||||
|  | type Instance = { | ||||||
|  | 	emojis: { | ||||||
|  | 		category: string; | ||||||
|  | 	}[]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const data = localStorage.getItem('instance'); | ||||||
|  | 
 | ||||||
|  | // TODO: instanceをリアクティブにするかは再考の余地あり
 | ||||||
|  | 
 | ||||||
|  | export const instance: Instance = reactive(data ? JSON.parse(data) : { | ||||||
|  | 	// TODO: set default values
 | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export async function fetchInstance() { | ||||||
|  | 	const meta = await api('meta', { | ||||||
|  | 		detail: false | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	for (const [k, v] of Object.entries(meta)) { | ||||||
|  | 		instance[k] = v; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	localStorage.setItem('instance', JSON.stringify(instance)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const emojiCategories = computed(() => { | ||||||
|  | 	const categories = new Set(); | ||||||
|  | 	for (const emoji of instance.emojis) { | ||||||
|  | 		categories.add(emoji.category); | ||||||
|  | 	} | ||||||
|  | 	return Array.from(categories); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // このファイルに書きたくないけどここに書かないと何故かVeturが認識しない
 | ||||||
|  | declare module '@vue/runtime-core' { | ||||||
|  | 	interface ComponentCustomProperties { | ||||||
|  | 		$instance: typeof instance; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -1,12 +1,11 @@ | ||||||
| import { Component, defineAsyncComponent, markRaw, reactive, Ref, ref } from 'vue'; | import { Component, defineAsyncComponent, markRaw, reactive, Ref, ref } from 'vue'; | ||||||
| import { EventEmitter } from 'eventemitter3'; | import { EventEmitter } from 'eventemitter3'; | ||||||
| import Stream from '@/scripts/stream'; | import Stream from '@/scripts/stream'; | ||||||
| import { store } from '@/store'; |  | ||||||
| import { apiUrl, debug } from '@/config'; | import { apiUrl, debug } from '@/config'; | ||||||
| import MkPostFormDialog from '@/components/post-form-dialog.vue'; | import MkPostFormDialog from '@/components/post-form-dialog.vue'; | ||||||
| import MkWaitingDialog from '@/components/waiting-dialog.vue'; | import MkWaitingDialog from '@/components/waiting-dialog.vue'; | ||||||
| import { resolve } from '@/router'; | import { resolve } from '@/router'; | ||||||
| import { device } from './cold-storage'; | import { $i } from './account'; | ||||||
| 
 | 
 | ||||||
| const ua = navigator.userAgent.toLowerCase(); | const ua = navigator.userAgent.toLowerCase(); | ||||||
| export const isMobile = /mobile|iphone|ipad|android/.test(ua); | export const isMobile = /mobile|iphone|ipad|android/.test(ua); | ||||||
|  | @ -40,7 +39,7 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st | ||||||
| 
 | 
 | ||||||
| 	const promise = new Promise((resolve, reject) => { | 	const promise = new Promise((resolve, reject) => { | ||||||
| 		// Append a credential
 | 		// Append a credential
 | ||||||
| 		if (store.getters.isSignedIn) (data as any).i = store.state.i.token; | 		if ($i) (data as any).i = $i.token; | ||||||
| 		if (token !== undefined) (data as any).i = token; | 		if (token !== undefined) (data as any).i = token; | ||||||
| 
 | 
 | ||||||
| 		// Send request
 | 		// Send request
 | ||||||
|  | @ -368,7 +367,7 @@ export function upload(file: File, folder?: any, name?: string) { | ||||||
| 			uploads.value.push(ctx); | 			uploads.value.push(ctx); | ||||||
| 
 | 
 | ||||||
| 			const data = new FormData(); | 			const data = new FormData(); | ||||||
| 			data.append('i', store.state.i.token); | 			data.append('i', $i.token); | ||||||
| 			data.append('force', 'true'); | 			data.append('force', 'true'); | ||||||
| 			data.append('file', file); | 			data.append('file', file); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <template> | <template> | ||||||
| <transition :name="$store.state.device.animation ? 'zoom' : ''" appear> | <transition :name="$store.state.animation ? 'zoom' : ''" appear> | ||||||
| 	<div class="_section"> | 	<div class="_section"> | ||||||
| 		<div class="mjndxjch _content"> | 		<div class="mjndxjch _content"> | ||||||
| 			<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/> | 			<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/> | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ | ||||||
| 				<img src="/assets/icons/512.png" alt="" class="icon" ref="icon" @load="iconLoaded" draggable="false"/> | 				<img src="/assets/icons/512.png" alt="" class="icon" ref="icon" @load="iconLoaded" draggable="false"/> | ||||||
| 				<div class="misskey">Misskey</div> | 				<div class="misskey">Misskey</div> | ||||||
| 				<div class="version">v{{ version }}</div> | 				<div class="version">v{{ version }}</div> | ||||||
| 				<span class="emoji" v-for="emoji in easterEggEmojis" :key="emoji.id" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :custom-emojis="$store.state.instance.meta.emojis" :is-reaction="false" :normal="true" :no-style="true"/></span> | 				<span class="emoji" v-for="emoji in easterEggEmojis" :key="emoji.id" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :custom-emojis="$instance.emojis" :is-reaction="false" :normal="true" :no-style="true"/></span> | ||||||
| 			</div> | 			</div> | ||||||
| 		</section> | 		</section> | ||||||
| 		<section class="_formItem" style="text-align: center; padding: 0 16px;" @click="gravity"> | 		<section class="_formItem" style="text-align: center; padding: 0 16px;" @click="gravity"> | ||||||
|  | @ -144,7 +144,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	methods: { | 	methods: { | ||||||
| 		iconLoaded() { | 		iconLoaded() { | ||||||
| 			const emojis = this.$store.state.settings.reactions; | 			const emojis = this.$store.state.reactions; | ||||||
| 			const containerWidth = this.$refs.about.offsetWidth; | 			const containerWidth = this.$refs.about.offsetWidth; | ||||||
| 			for (let i = 0; i < 32; i++) { | 			for (let i = 0; i < 32; i++) { | ||||||
| 				this.easterEggEmojis.push({ | 				this.easterEggEmojis.push({ | ||||||
|  |  | ||||||
|  | @ -59,7 +59,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		meta() { | 		meta() { | ||||||
| 			return this.$store.state.instance.meta; | 			return this.$instance; | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -2,12 +2,12 @@ | ||||||
| <div class="_section"> | <div class="_section"> | ||||||
| 	<MkPagination :pagination="pagination" #default="{items}" class="ruryvtyk _content"> | 	<MkPagination :pagination="pagination" #default="{items}" class="ruryvtyk _content"> | ||||||
| 		<section class="_card announcement _vMargin" v-for="(announcement, i) in items" :key="announcement.id"> | 		<section class="_card announcement _vMargin" v-for="(announcement, i) in items" :key="announcement.id"> | ||||||
| 			<div class="_title"><span v-if="$store.getters.isSignedIn && !announcement.isRead">🆕 </span>{{ announcement.title }}</div> | 			<div class="_title"><span v-if="$i && !announcement.isRead">🆕 </span>{{ announcement.title }}</div> | ||||||
| 			<div class="_content"> | 			<div class="_content"> | ||||||
| 				<Mfm :text="announcement.text"/> | 				<Mfm :text="announcement.text"/> | ||||||
| 				<img v-if="announcement.imageUrl" :src="announcement.imageUrl"/> | 				<img v-if="announcement.imageUrl" :src="announcement.imageUrl"/> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div class="_footer" v-if="$store.getters.isSignedIn && !announcement.isRead"> | 			<div class="_footer" v-if="$i && !announcement.isRead"> | ||||||
| 				<MkButton @click="read(items, announcement, i)" primary><Fa :icon="faCheck"/> {{ $t('gotIt') }}</MkButton> | 				<MkButton @click="read(items, announcement, i)" primary><Fa :icon="faCheck"/> {{ $t('gotIt') }}</MkButton> | ||||||
| 			</div> | 			</div> | ||||||
| 		</section> | 		</section> | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| <template> | <template> | ||||||
| <div class="" v-if="$store.getters.isSignedIn && fetching"> | <div class="" v-if="$i && fetching"> | ||||||
| 	<MkLoading/> | 	<MkLoading/> | ||||||
| </div> | </div> | ||||||
| <div v-else-if="$store.getters.isSignedIn"> | <div v-else-if="$i"> | ||||||
| 	<XForm | 	<XForm | ||||||
| 		class="form" | 		class="form" | ||||||
| 		ref="form" | 		ref="form" | ||||||
|  | @ -33,6 +33,7 @@ import { defineComponent } from 'vue'; | ||||||
| import XForm from './auth.form.vue'; | import XForm from './auth.form.vue'; | ||||||
| import MkSignin from '@/components/signin.vue'; | import MkSignin from '@/components/signin.vue'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { login } from '@/account'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
|  | @ -52,7 +53,7 @@ export default defineComponent({ | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 	mounted() { | 	mounted() { | ||||||
| 		if (!this.$store.getters.isSignedIn) return; | 		if (!this.$i) return; | ||||||
| 
 | 
 | ||||||
| 		// Fetch session | 		// Fetch session | ||||||
| 		os.api('auth/session/show', { | 		os.api('auth/session/show', { | ||||||
|  | @ -83,8 +84,7 @@ export default defineComponent({ | ||||||
| 				location.href = `${this.session.app.callbackUrl}?token=${this.session.token}`; | 				location.href = `${this.session.app.callbackUrl}?token=${this.session.token}`; | ||||||
| 			} | 			} | ||||||
| 		}, onLogin(res) { | 		}, onLogin(res) { | ||||||
| 			localStorage.setItem('i', res.i); | 			login(res.i); | ||||||
| 			location.reload(); |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -16,11 +16,11 @@ | ||||||
| 			<div class="fade"></div> | 			<div class="fade"></div> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="description" v-if="channel.description"> | 		<div class="description" v-if="channel.description"> | ||||||
| 			<Mfm :text="channel.description" :is-note="false" :i="$store.state.i"/> | 			<Mfm :text="channel.description" :is-note="false" :i="$i"/> | ||||||
| 		</div> | 		</div> | ||||||
| 	</div> | 	</div> | ||||||
| 
 | 
 | ||||||
| 	<XPostForm :channel="channel" class="post-form _content _panel _vMargin" fixed v-if="$store.getters.isSignedIn"/> | 	<XPostForm :channel="channel" class="post-form _content _panel _vMargin" fixed v-if="$i"/> | ||||||
| 
 | 
 | ||||||
| 	<XTimeline class="_content _vMargin" src="channel" :channel="channelId" @before="before" @after="after"/> | 	<XTimeline class="_content _vMargin" src="channel" :channel="channelId" @before="before" @after="after"/> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
| <div> | <div> | ||||||
| 	<div class="_section" style="padding: 0;" v-if="$store.getters.isSignedIn"> | 	<div class="_section" style="padding: 0;" v-if="$i"> | ||||||
| 		<MkTab class="_content" v-model:value="tab"> | 		<MkTab class="_content" v-model:value="tab"> | ||||||
| 			<option value="featured"><Fa :icon="faFireAlt"/> {{ $t('_channel.featured') }}</option> | 			<option value="featured"><Fa :icon="faFireAlt"/> {{ $t('_channel.featured') }}</option> | ||||||
| 			<option value="following"><Fa :icon="faHeart"/> {{ $t('_channel.following') }}</option> | 			<option value="following"><Fa :icon="faHeart"/> {{ $t('_channel.following') }}</option> | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| <div v-if="clip" class="_section"> | <div v-if="clip" class="_section"> | ||||||
| 	<div class="okzinsic _content _panel _vMargin"> | 	<div class="okzinsic _content _panel _vMargin"> | ||||||
| 		<div class="description" v-if="clip.description"> | 		<div class="description" v-if="clip.description"> | ||||||
| 			<Mfm :text="clip.description" :is-note="false" :i="$store.state.i"/> | 			<Mfm :text="clip.description" :is-note="false" :i="$i"/> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="user"> | 		<div class="user"> | ||||||
| 			<MkAvatar :user="clip.user" class="avatar"/> <MkUserName :user="clip.user" :nowrap="false"/> | 			<MkAvatar :user="clip.user" class="avatar"/> <MkUserName :user="clip.user" :nowrap="false"/> | ||||||
|  | @ -58,7 +58,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		isOwned(): boolean { | 		isOwned(): boolean { | ||||||
| 			return this.$store.getters.isSignedIn && this.clip && (this.$store.state.i.id === this.clip.userId); | 			return this.$i && this.clip && (this.$i.id === this.clip.userId); | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -142,7 +142,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		meta() { | 		meta() { | ||||||
| 			return this.$store.state.instance.meta; | 			return this.$instance; | ||||||
| 		}, | 		}, | ||||||
| 		tagUsers(): any { | 		tagUsers(): any { | ||||||
| 			return { | 			return { | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ | ||||||
| 						<p class="acct">@{{ acct(req.follower) }}</p> | 						<p class="acct">@{{ acct(req.follower) }}</p> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="description" v-if="req.follower.description" :title="req.follower.description"> | 					<div class="description" v-if="req.follower.description" :title="req.follower.description"> | ||||||
| 						<Mfm :text="req.follower.description" :is-note="false" :author="req.follower" :i="$store.state.i" :custom-emojis="req.follower.emojis" :plain="true" :nowrap="true"/> | 						<Mfm :text="req.follower.description" :is-note="false" :author="req.follower" :i="$i" :custom-emojis="req.follower.emojis" :plain="true" :nowrap="true"/> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="actions"> | 					<div class="actions"> | ||||||
| 						<button class="_button" @click="accept(req.follower)"><Fa :icon="faCheck"/></button> | 						<button class="_button" @click="accept(req.follower)"><Fa :icon="faCheck"/></button> | ||||||
|  |  | ||||||
|  | @ -160,7 +160,7 @@ export default defineComponent({ | ||||||
| 	computed: { | 	computed: { | ||||||
| 		gridColor() { | 		gridColor() { | ||||||
| 			// TODO: var(--panel)の色が暗いか明るいかで判定する | 			// TODO: var(--panel)の色が暗いか明るいかで判定する | ||||||
| 			return this.$store.state.device.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; | 			return this.$store.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -122,7 +122,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		meta() { | 		meta() { | ||||||
| 			return this.$store.state.instance.meta; | 			return this.$instance; | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -206,7 +206,7 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		meta() { | 		meta() { | ||||||
| 			return this.$store.state.instance.meta; | 			return this.$instance; | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		isBlocked() { | 		isBlocked() { | ||||||
|  |  | ||||||
|  | @ -63,7 +63,7 @@ export default defineComponent({ | ||||||
| 		this.fetchJobs(); | 		this.fetchJobs(); | ||||||
| 
 | 
 | ||||||
| 		// TODO: var(--panel)の色が暗いか明るいかで判定する | 		// TODO: var(--panel)の色が暗いか明るいかで判定する | ||||||
| 		const gridColor = this.$store.state.device.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; | 		const gridColor = this.$store.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; | ||||||
| 
 | 
 | ||||||
| 		Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg'); | 		Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg'); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -259,6 +259,7 @@ import MkInfo from '@/components/ui/info.vue'; | ||||||
| import { url } from '@/config'; | import { url } from '@/config'; | ||||||
| import getAcct from '../../../misc/acct/render'; | import getAcct from '../../../misc/acct/render'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { fetchInstance } from '@/instance'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
|  | @ -565,7 +566,7 @@ export default defineComponent({ | ||||||
| 				summalyProxy: this.summalyProxy, | 				summalyProxy: this.summalyProxy, | ||||||
| 				useStarForReactionFallback: this.useStarForReactionFallback, | 				useStarForReactionFallback: this.useStarForReactionFallback, | ||||||
| 			}).then(() => { | 			}).then(() => { | ||||||
| 				this.$store.dispatch('instance/fetch'); | 				fetchInstance(); | ||||||
| 				if (withDialog) { | 				if (withDialog) { | ||||||
| 					os.success(); | 					os.success(); | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="_section"> | 		<div class="_section"> | ||||||
| 			<div class="_content"> | 			<div class="_content"> | ||||||
| 				<MkSwitch v-if="user.host == null && $store.state.i.isAdmin && (moderator || !user.isAdmin)" @update:value="toggleModerator" v-model:value="moderator">{{ $t('moderator') }}</MkSwitch> | 				<MkSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" @update:value="toggleModerator" v-model:value="moderator">{{ $t('moderator') }}</MkSwitch> | ||||||
| 				<MkSwitch @update:value="toggleSilence" v-model:value="silenced">{{ $t('silence') }}</MkSwitch> | 				<MkSwitch @update:value="toggleSilence" v-model:value="silenced">{{ $t('silence') }}</MkSwitch> | ||||||
| 				<MkSwitch @update:value="toggleSuspend" v-model:value="suspended">{{ $t('suspend') }}</MkSwitch> | 				<MkSwitch @update:value="toggleSuspend" v-model:value="suspended">{{ $t('suspend') }}</MkSwitch> | ||||||
| 			</div> | 			</div> | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ | ||||||
| 		<div class="history" v-if="messages.length > 0"> | 		<div class="history" v-if="messages.length > 0"> | ||||||
| 			<MkA v-for="(message, i) in messages" | 			<MkA v-for="(message, i) in messages" | ||||||
| 				class="message _panel" | 				class="message _panel" | ||||||
| 				:class="{ isMe: isMe(message), isRead: message.groupId ? message.reads.includes($store.state.i.id) : message.isRead }" | 				:class="{ isMe: isMe(message), isRead: message.groupId ? message.reads.includes($i.id) : message.isRead }" | ||||||
| 				:to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`" | 				:to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`" | ||||||
| 				:data-index="i" | 				:data-index="i" | ||||||
| 				:key="message.id" | 				:key="message.id" | ||||||
|  | @ -88,7 +88,7 @@ export default defineComponent({ | ||||||
| 		getAcct, | 		getAcct, | ||||||
| 
 | 
 | ||||||
| 		isMe(message) { | 		isMe(message) { | ||||||
| 			return message.userId == this.$store.state.i.id; | 			return message.userId == this.$i.id; | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		onMessage(message) { | 		onMessage(message) { | ||||||
|  | @ -111,7 +111,7 @@ export default defineComponent({ | ||||||
| 					if (found.recipientId) { | 					if (found.recipientId) { | ||||||
| 						found.isRead = true; | 						found.isRead = true; | ||||||
| 					} else if (found.groupId) { | 					} else if (found.groupId) { | ||||||
| 						found.reads.push(this.$store.state.i.id); | 						found.reads.push(this.$i.id); | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | @ -91,8 +91,8 @@ export default defineComponent({ | ||||||
| 					const file = items[0].getAsFile(); | 					const file = items[0].getAsFile(); | ||||||
| 					const lio = file.name.lastIndexOf('.'); | 					const lio = file.name.lastIndexOf('.'); | ||||||
| 					const ext = lio >= 0 ? file.name.slice(lio) : ''; | 					const ext = lio >= 0 ? file.name.slice(lio) : ''; | ||||||
| 					const formatted = `${formatTimeString(new Date(file.lastModified), this.$store.state.settings.pastedFileName).replace(/{{number}}/g, '1')}${ext}`; | 					const formatted = `${formatTimeString(new Date(file.lastModified), this.$store.state.pastedFileName).replace(/{{number}}/g, '1')}${ext}`; | ||||||
| 					const name = this.$store.state.settings.pasteDialog | 					const name = this.$store.state.pasteDialog | ||||||
| 						? await os.dialog({ | 						? await os.dialog({ | ||||||
| 							title: this.$t('enterFileName'), | 							title: this.$t('enterFileName'), | ||||||
| 							input: { | 							input: { | ||||||
|  | @ -163,7 +163,7 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		upload(file: File, name?: string) { | 		upload(file: File, name?: string) { | ||||||
| 			os.upload(file, this.$store.state.settings.uploadFolder, name).then(res => { | 			os.upload(file, this.$store.state.uploadFolder, name).then(res => { | ||||||
| 				this.file = res; | 				this.file = res; | ||||||
| 			}); | 			}); | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ | ||||||
| 				<img src="/assets/remove.png" alt="Delete"/> | 				<img src="/assets/remove.png" alt="Delete"/> | ||||||
| 			</button> | 			</button> | ||||||
| 			<div class="content" v-if="!message.isDeleted"> | 			<div class="content" v-if="!message.isDeleted"> | ||||||
| 				<Mfm class="text" v-if="message.text" ref="text" :text="message.text" :i="$store.state.i"/> | 				<Mfm class="text" v-if="message.text" ref="text" :text="message.text" :i="$i"/> | ||||||
| 				<div class="file" v-if="message.file"> | 				<div class="file" v-if="message.file"> | ||||||
| 					<a :href="message.file.url" rel="noopener" target="_blank" :title="message.file.name"> | 					<a :href="message.file.url" rel="noopener" target="_blank" :title="message.file.name"> | ||||||
| 						<img v-if="message.file.type.split('/')[0] == 'image'" :src="message.file.url" :alt="message.file.name"/> | 						<img v-if="message.file.type.split('/')[0] == 'image'" :src="message.file.url" :alt="message.file.name"/> | ||||||
|  | @ -56,7 +56,7 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 	computed: { | 	computed: { | ||||||
| 		isMe(): boolean { | 		isMe(): boolean { | ||||||
| 			return this.message.userId === this.$store.state.i.id; | 			return this.message.userId === this.$i.id; | ||||||
| 		}, | 		}, | ||||||
| 		urls(): string[] { | 		urls(): string[] { | ||||||
| 			if (this.message.text) { | 			if (this.message.text) { | ||||||
|  |  | ||||||
|  | @ -110,7 +110,7 @@ const Component = defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	mounted() { | 	mounted() { | ||||||
| 		this.fetch(); | 		this.fetch(); | ||||||
| 		if (this.$store.state.device.enableInfiniteScroll) { | 		if (this.$store.state.enableInfiniteScroll) { | ||||||
| 			this.$nextTick(() => this.ilObserver.observe(this.$refs.loadMore as Element)); | 			this.$nextTick(() => this.ilObserver.observe(this.$refs.loadMore as Element)); | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
|  | @ -224,7 +224,7 @@ const Component = defineComponent({ | ||||||
| 			const _isBottom = isBottom(this.$el, 64); | 			const _isBottom = isBottom(this.$el, 64); | ||||||
| 
 | 
 | ||||||
| 			this.messages.push(message); | 			this.messages.push(message); | ||||||
| 			if (message.userId != this.$store.state.i.id && !document.hidden) { | 			if (message.userId != this.$i.id && !document.hidden) { | ||||||
| 				this.connection.send('read', { | 				this.connection.send('read', { | ||||||
| 					id: message.id | 					id: message.id | ||||||
| 				}); | 				}); | ||||||
|  | @ -235,7 +235,7 @@ const Component = defineComponent({ | ||||||
| 				this.$nextTick(() => { | 				this.$nextTick(() => { | ||||||
| 					this.scrollToBottom(); | 					this.scrollToBottom(); | ||||||
| 				}); | 				}); | ||||||
| 			} else if (message.userId != this.$store.state.i.id) { | 			} else if (message.userId != this.$i.id) { | ||||||
| 				// Notify | 				// Notify | ||||||
| 				this.notifyNewMessage(); | 				this.notifyNewMessage(); | ||||||
| 			} | 			} | ||||||
|  | @ -299,7 +299,7 @@ const Component = defineComponent({ | ||||||
| 		onVisibilitychange() { | 		onVisibilitychange() { | ||||||
| 			if (document.hidden) return; | 			if (document.hidden) return; | ||||||
| 			for (const message of this.messages) { | 			for (const message of this.messages) { | ||||||
| 				if (message.userId !== this.$store.state.i.id && !message.isRead) { | 				if (message.userId !== this.$i.id && !message.isRead) { | ||||||
| 					this.connection.send('read', { | 					this.connection.send('read', { | ||||||
| 						id: message.id | 						id: message.id | ||||||
| 					}); | 					}); | ||||||
|  |  | ||||||
|  | @ -238,7 +238,7 @@ export default defineComponent({ | ||||||
| 			preview_hashtag: '#test', | 			preview_hashtag: '#test', | ||||||
| 			preview_url: `https://example.com`, | 			preview_url: `https://example.com`, | ||||||
| 			preview_link: `[${this.$t('_mfm.dummy')}](https://example.com)`, | 			preview_link: `[${this.$t('_mfm.dummy')}](https://example.com)`, | ||||||
| 			preview_emoji: `:${this.$store.state.instance.meta.emojis[0].name}:`, | 			preview_emoji: `:${this.$instance.emojis[0].name}:`, | ||||||
| 			preview_bold: `**${this.$t('_mfm.dummy')}**`, | 			preview_bold: `**${this.$t('_mfm.dummy')}**`, | ||||||
| 			preview_small: `<small>${this.$t('_mfm.dummy')}</small>`, | 			preview_small: `<small>${this.$t('_mfm.dummy')}</small>`, | ||||||
| 			preview_center: `<center>${this.$t('_mfm.dummy')}</center>`, | 			preview_center: `<center>${this.$t('_mfm.dummy')}</center>`, | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <template> | <template> | ||||||
| <div v-if="$store.getters.isSignedIn"> | <div v-if="$i"> | ||||||
| 	<div class="waiting _section" v-if="state == 'waiting'"> | 	<div class="waiting _section" v-if="state == 'waiting'"> | ||||||
| 		<div class="_content"> | 		<div class="_content"> | ||||||
| 			<MkLoading/> | 			<MkLoading/> | ||||||
|  | @ -41,6 +41,7 @@ import { defineComponent } from 'vue'; | ||||||
| import MkSignin from '@/components/signin.vue'; | import MkSignin from '@/components/signin.vue'; | ||||||
| import MkButton from '@/components/ui/button.vue'; | import MkButton from '@/components/ui/button.vue'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { login } from '@/account'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
|  | @ -88,8 +89,7 @@ export default defineComponent({ | ||||||
| 			this.state = 'denied'; | 			this.state = 'denied'; | ||||||
| 		}, | 		}, | ||||||
| 		onLogin(res) { | 		onLogin(res) { | ||||||
| 			localStorage.setItem('i', res.i); | 			login(res.i); | ||||||
| 			location.reload(); |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -141,7 +141,7 @@ export default defineComponent({ | ||||||
| 				title: this.$t('_pages.newPage'), | 				title: this.$t('_pages.newPage'), | ||||||
| 				icon: faPencilAlt, | 				icon: faPencilAlt, | ||||||
| 			}), | 			}), | ||||||
| 			author: this.$store.state.i, | 			author: this.$i, | ||||||
| 			readonly: false, | 			readonly: false, | ||||||
| 			page: null, | 			page: null, | ||||||
| 			pageId: null, | 			pageId: null, | ||||||
|  |  | ||||||
|  | @ -21,9 +21,9 @@ | ||||||
| 	<div class="_section links"> | 	<div class="_section links"> | ||||||
| 		<div class="_content"> | 		<div class="_content"> | ||||||
| 			<MkA :to="`./${page.name}/view-source`" class="link">{{ $t('_pages.viewSource') }}</MkA> | 			<MkA :to="`./${page.name}/view-source`" class="link">{{ $t('_pages.viewSource') }}</MkA> | ||||||
| 			<template v-if="$store.getters.isSignedIn && $store.state.i.id === page.userId"> | 			<template v-if="$i && $i.id === page.userId"> | ||||||
| 				<MkA :to="`/pages/edit/${page.id}`" class="link">{{ $t('_pages.editThisPage') }}</MkA> | 				<MkA :to="`/pages/edit/${page.id}`" class="link">{{ $t('_pages.editThisPage') }}</MkA> | ||||||
| 				<button v-if="$store.state.i.pinnedPageId === page.id" @click="pin(false)" class="link _textButton">{{ $t('unpin') }}</button> | 				<button v-if="$i.pinnedPageId === page.id" @click="pin(false)" class="link _textButton">{{ $t('unpin') }}</button> | ||||||
| 				<button v-else @click="pin(true)" class="link _textButton">{{ $t('pin') }}</button> | 				<button v-else @click="pin(true)" class="link _textButton">{{ $t('pin') }}</button> | ||||||
| 			</template> | 			</template> | ||||||
| 		</div> | 		</div> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
| <div> | <div> | ||||||
| 	<MkTab v-model:value="tab" v-if="$store.getters.isSignedIn"> | 	<MkTab v-model:value="tab" v-if="$i"> | ||||||
| 		<option value="featured"><Fa :icon="faFireAlt"/> {{ $t('_pages.featured') }}</option> | 		<option value="featured"><Fa :icon="faFireAlt"/> {{ $t('_pages.featured') }}</option> | ||||||
| 		<option value="my"><Fa :icon="faEdit"/> {{ $t('_pages.my') }}</option> | 		<option value="my"><Fa :icon="faEdit"/> {{ $t('_pages.my') }}</option> | ||||||
| 		<option value="liked"><Fa :icon="faHeart"/> {{ $t('_pages.liked') }}</option> | 		<option value="liked"><Fa :icon="faHeart"/> {{ $t('_pages.liked') }}</option> | ||||||
|  |  | ||||||
|  | @ -22,11 +22,11 @@ | ||||||
| 	</div> | 	</div> | ||||||
| 
 | 
 | ||||||
| 	<div class="board"> | 	<div class="board"> | ||||||
| 		<div class="labels-x" v-if="$store.state.settings.gamesReversiShowBoardLabels"> | 		<div class="labels-x" v-if="$store.state.gamesReversiShowBoardLabels"> | ||||||
| 			<span v-for="i in game.map[0].length">{{ String.fromCharCode(64 + i) }}</span> | 			<span v-for="i in game.map[0].length">{{ String.fromCharCode(64 + i) }}</span> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="flex"> | 		<div class="flex"> | ||||||
| 			<div class="labels-y" v-if="$store.state.settings.gamesReversiShowBoardLabels"> | 			<div class="labels-y" v-if="$store.state.gamesReversiShowBoardLabels"> | ||||||
| 				<div v-for="i in game.map.length">{{ i }}</div> | 				<div v-for="i in game.map.length">{{ i }}</div> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div class="cells" :style="cellsStyle"> | 			<div class="cells" :style="cellsStyle"> | ||||||
|  | @ -35,7 +35,7 @@ | ||||||
| 					@click="set(i)" | 					@click="set(i)" | ||||||
| 					:title="`${String.fromCharCode(65 + o.transformPosToXy(i)[0])}${o.transformPosToXy(i)[1] + 1}`" | 					:title="`${String.fromCharCode(65 + o.transformPosToXy(i)[0])}${o.transformPosToXy(i)[1] + 1}`" | ||||||
| 				> | 				> | ||||||
| 					<template v-if="$store.state.settings.gamesReversiUseAvatarStones || true"> | 					<template v-if="$store.state.gamesReversiUseAvatarStones || true"> | ||||||
| 						<img v-if="stone === true" :src="blackUser.avatarUrl" alt="black"> | 						<img v-if="stone === true" :src="blackUser.avatarUrl" alt="black"> | ||||||
| 						<img v-if="stone === false" :src="whiteUser.avatarUrl" alt="white"> | 						<img v-if="stone === false" :src="whiteUser.avatarUrl" alt="white"> | ||||||
| 					</template> | 					</template> | ||||||
|  | @ -45,11 +45,11 @@ | ||||||
| 					</template> | 					</template> | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div class="labels-y" v-if="$store.state.settings.gamesReversiShowBoardLabels"> | 			<div class="labels-y" v-if="$store.state.gamesReversiShowBoardLabels"> | ||||||
| 				<div v-for="i in game.map.length">{{ i }}</div> | 				<div v-for="i in game.map.length">{{ i }}</div> | ||||||
| 			</div> | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="labels-x" v-if="$store.state.settings.gamesReversiShowBoardLabels"> | 		<div class="labels-x" v-if="$store.state.gamesReversiShowBoardLabels"> | ||||||
| 			<span v-for="i in game.map[0].length">{{ String.fromCharCode(64 + i) }}</span> | 			<span v-for="i in game.map[0].length">{{ String.fromCharCode(64 + i) }}</span> | ||||||
| 		</div> | 		</div> | ||||||
| 	</div> | 	</div> | ||||||
|  | @ -126,14 +126,14 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		iAmPlayer(): boolean { | 		iAmPlayer(): boolean { | ||||||
| 			if (!this.$store.getters.isSignedIn) return false; | 			if (!this.$i) return false; | ||||||
| 			return this.game.user1Id == this.$store.state.i.id || this.game.user2Id == this.$store.state.i.id; | 			return this.game.user1Id == this.$i.id || this.game.user2Id == this.$i.id; | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		myColor(): Color { | 		myColor(): Color { | ||||||
| 			if (!this.iAmPlayer) return null; | 			if (!this.iAmPlayer) return null; | ||||||
| 			if (this.game.user1Id == this.$store.state.i.id && this.game.black == 1) return true; | 			if (this.game.user1Id == this.$i.id && this.game.black == 1) return true; | ||||||
| 			if (this.game.user2Id == this.$store.state.i.id && this.game.black == 2) return true; | 			if (this.game.user2Id == this.$i.id && this.game.black == 2) return true; | ||||||
| 			return false; | 			return false; | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
|  | @ -234,7 +234,7 @@ export default defineComponent({ | ||||||
| 		isMyTurn(): boolean { | 		isMyTurn(): boolean { | ||||||
| 			if (!this.iAmPlayer) return false; | 			if (!this.iAmPlayer) return false; | ||||||
| 			if (this.turnUser() == null) return false; | 			if (this.turnUser() == null) return false; | ||||||
| 			return this.turnUser().id == this.$store.state.i.id; | 			return this.turnUser().id == this.$i.id; | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		set(pos) { | 		set(pos) { | ||||||
|  |  | ||||||
|  | @ -169,13 +169,13 @@ export default defineComponent({ | ||||||
| 			return categories.filter((item, pos) => categories.indexOf(item) == pos); | 			return categories.filter((item, pos) => categories.indexOf(item) == pos); | ||||||
| 		}, | 		}, | ||||||
| 		isAccepted(): boolean { | 		isAccepted(): boolean { | ||||||
| 			if (this.game.user1Id == this.$store.state.i.id && this.game.user1Accepted) return true; | 			if (this.game.user1Id == this.$i.id && this.game.user1Accepted) return true; | ||||||
| 			if (this.game.user2Id == this.$store.state.i.id && this.game.user2Accepted) return true; | 			if (this.game.user2Id == this.$i.id && this.game.user2Accepted) return true; | ||||||
| 			return false; | 			return false; | ||||||
| 		}, | 		}, | ||||||
| 		isOpAccepted(): boolean { | 		isOpAccepted(): boolean { | ||||||
| 			if (this.game.user1Id != this.$store.state.i.id && this.game.user1Accepted) return true; | 			if (this.game.user1Id != this.$i.id && this.game.user1Accepted) return true; | ||||||
| 			if (this.game.user2Id != this.$store.state.i.id && this.game.user2Accepted) return true; | 			if (this.game.user2Id != this.$i.id && this.game.user2Accepted) return true; | ||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
|  | @ -186,8 +186,8 @@ export default defineComponent({ | ||||||
| 		this.connection.on('initForm', this.onInitForm); | 		this.connection.on('initForm', this.onInitForm); | ||||||
| 		this.connection.on('message', this.onMessage); | 		this.connection.on('message', this.onMessage); | ||||||
| 
 | 
 | ||||||
| 		if (this.game.user1Id != this.$store.state.i.id && this.game.form1) this.form = this.game.form1; | 		if (this.game.user1Id != this.$i.id && this.game.form1) this.form = this.game.form1; | ||||||
| 		if (this.game.user2Id != this.$store.state.i.id && this.game.form2) this.form = this.game.form2; | 		if (this.game.user2Id != this.$i.id && this.game.form2) this.form = this.game.form2; | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	beforeUnmount() { | 	beforeUnmount() { | ||||||
|  | @ -233,12 +233,12 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		onInitForm(x) { | 		onInitForm(x) { | ||||||
| 			if (x.userId == this.$store.state.i.id) return; | 			if (x.userId == this.$i.id) return; | ||||||
| 			this.form = x.form; | 			this.form = x.form; | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		onMessage(x) { | 		onMessage(x) { | ||||||
| 			if (x.userId == this.$store.state.i.id) return; | 			if (x.userId == this.$i.id) return; | ||||||
| 			this.messages.unshift(x.message); | 			this.messages.unshift(x.message); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -91,7 +91,7 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	mounted() { | 	mounted() { | ||||||
| 		if (this.$store.getters.isSignedIn) { | 		if (this.$i) { | ||||||
| 			this.connection = os.stream.useSharedConnection('gamesReversi'); | 			this.connection = os.stream.useSharedConnection('gamesReversi'); | ||||||
| 
 | 
 | ||||||
| 			this.connection.on('invited', this.onInvited); | 			this.connection.on('invited', this.onInvited); | ||||||
|  |  | ||||||
|  | @ -62,6 +62,7 @@ import MkButton from '@/components/ui/button.vue'; | ||||||
| import MkSelect from '@/components/ui/select.vue'; | import MkSelect from '@/components/ui/select.vue'; | ||||||
| import { selectFile } from '@/scripts/select-file'; | import { selectFile } from '@/scripts/select-file'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { ColdDeviceStorage } from '@/store'; | ||||||
| 
 | 
 | ||||||
| let room: Room; | let room: Room; | ||||||
| 
 | 
 | ||||||
|  | @ -107,7 +108,7 @@ export default defineComponent({ | ||||||
| 			...parseAcct(this.acct) | 			...parseAcct(this.acct) | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		this.isMyRoom = this.$store.getters.isSignedIn && (this.$store.state.i.id === this.user.id); | 		this.isMyRoom = this.$i && (this.$i.id === this.user.id); | ||||||
| 
 | 
 | ||||||
| 		const roomInfo = await os.api('room/show', { | 		const roomInfo = await os.api('room/show', { | ||||||
| 			userId: this.user.id | 			userId: this.user.id | ||||||
|  | @ -117,7 +118,7 @@ export default defineComponent({ | ||||||
| 		this.carpetColor = roomInfo.carpetColor; | 		this.carpetColor = roomInfo.carpetColor; | ||||||
| 
 | 
 | ||||||
| 		room = new Room(this.user, this.isMyRoom, roomInfo, this.$el, { | 		room = new Room(this.user, this.isMyRoom, roomInfo, this.$el, { | ||||||
| 			graphicsQuality: this.$store.state.device.roomGraphicsQuality, | 			graphicsQuality: ColdDeviceStorage.get('roomGraphicsQuality'), | ||||||
| 			onChangeSelect: obj => { | 			onChangeSelect: obj => { | ||||||
| 				this.objectSelected = obj != null; | 				this.objectSelected = obj != null; | ||||||
| 				if (obj) { | 				if (obj) { | ||||||
|  | @ -132,7 +133,7 @@ export default defineComponent({ | ||||||
| 					}); | 					}); | ||||||
| 				} | 				} | ||||||
| 			}, | 			}, | ||||||
| 			useOrthographicCamera: this.$store.state.device.roomUseOrthographicCamera | 			useOrthographicCamera: ColdDeviceStorage.get('roomUseOrthographicCamera'), | ||||||
| 		}); | 		}); | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,8 +2,8 @@ | ||||||
| <section class="_card"> | <section class="_card"> | ||||||
| 	<div class="_title"><Fa :icon="faLock"/> {{ $t('twoStepAuthentication') }}</div> | 	<div class="_title"><Fa :icon="faLock"/> {{ $t('twoStepAuthentication') }}</div> | ||||||
| 	<div class="_content"> | 	<div class="_content"> | ||||||
| 		<MkButton v-if="!data && !$store.state.i.twoFactorEnabled" @click="register">{{ $t('_2fa.registerDevice') }}</MkButton> | 		<MkButton v-if="!data && !$i.twoFactorEnabled" @click="register">{{ $t('_2fa.registerDevice') }}</MkButton> | ||||||
| 		<template v-if="$store.state.i.twoFactorEnabled"> | 		<template v-if="$i.twoFactorEnabled"> | ||||||
| 			<p>{{ $t('_2fa.alreadyRegistered') }}</p> | 			<p>{{ $t('_2fa.alreadyRegistered') }}</p> | ||||||
| 			<MkButton @click="unregister">{{ $t('unregister') }}</MkButton> | 			<MkButton @click="unregister">{{ $t('unregister') }}</MkButton> | ||||||
| 
 | 
 | ||||||
|  | @ -13,14 +13,14 @@ | ||||||
| 				<h2 class="heading">{{ $t('securityKey') }}</h2> | 				<h2 class="heading">{{ $t('securityKey') }}</h2> | ||||||
| 				<p>{{ $t('_2fa.securityKeyInfo') }}</p> | 				<p>{{ $t('_2fa.securityKeyInfo') }}</p> | ||||||
| 				<div class="key-list"> | 				<div class="key-list"> | ||||||
| 					<div class="key" v-for="key in $store.state.i.securityKeysList"> | 					<div class="key" v-for="key in $i.securityKeysList"> | ||||||
| 						<h3>{{ key.name }}</h3> | 						<h3>{{ key.name }}</h3> | ||||||
| 						<div class="last-used">{{ $t('lastUsed') }}<MkTime :time="key.lastUsed"/></div> | 						<div class="last-used">{{ $t('lastUsed') }}<MkTime :time="key.lastUsed"/></div> | ||||||
| 						<MkButton @click="unregisterKey(key)">{{ $t('unregister') }}</MkButton> | 						<MkButton @click="unregisterKey(key)">{{ $t('unregister') }}</MkButton> | ||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 
 | 
 | ||||||
| 				<MkSwitch v-model:value="usePasswordLessLogin" @update:value="updatePasswordLessLogin" v-if="$store.state.i.securityKeysList.length > 0">{{ $t('passwordLessLogin') }}</MkSwitch> | 				<MkSwitch v-model:value="usePasswordLessLogin" @update:value="updatePasswordLessLogin" v-if="$i.securityKeysList.length > 0">{{ $t('passwordLessLogin') }}</MkSwitch> | ||||||
| 
 | 
 | ||||||
| 				<MkInfo warn v-if="registration && registration.error">{{ $t('error') }} {{ registration.error }}</MkInfo> | 				<MkInfo warn v-if="registration && registration.error">{{ $t('error') }} {{ registration.error }}</MkInfo> | ||||||
| 				<MkButton v-if="!registration || registration.error" @click="addSecurityKey">{{ $t('_2fa.registerKey') }}</MkButton> | 				<MkButton v-if="!registration || registration.error" @click="addSecurityKey">{{ $t('_2fa.registerKey') }}</MkButton> | ||||||
|  | @ -42,7 +42,7 @@ | ||||||
| 				</ol> | 				</ol> | ||||||
| 			</template> | 			</template> | ||||||
| 		</template> | 		</template> | ||||||
| 		<div v-if="data && !$store.state.i.twoFactorEnabled"> | 		<div v-if="data && !$i.twoFactorEnabled"> | ||||||
| 			<ol style="margin: 0; padding: 0 0 0 1em;"> | 			<ol style="margin: 0; padding: 0 0 0 1em;"> | ||||||
| 				<li> | 				<li> | ||||||
| 					<i18n-t keypath="_2fa.step1" tag="span"> | 					<i18n-t keypath="_2fa.step1" tag="span"> | ||||||
|  | @ -96,7 +96,7 @@ export default defineComponent({ | ||||||
| 			}, | 			}, | ||||||
| 			data: null, | 			data: null, | ||||||
| 			supportsCredentials: !!navigator.credentials, | 			supportsCredentials: !!navigator.credentials, | ||||||
| 			usePasswordLessLogin: this.$store.state.i.usePasswordLessLogin, | 			usePasswordLessLogin: this.$i.usePasswordLessLogin, | ||||||
| 			registration: null, | 			registration: null, | ||||||
| 			keyName: '', | 			keyName: '', | ||||||
| 			token: null, | 			token: null, | ||||||
|  | @ -136,7 +136,7 @@ export default defineComponent({ | ||||||
| 					this.updatePasswordLessLogin(); | 					this.updatePasswordLessLogin(); | ||||||
| 				}).then(() => { | 				}).then(() => { | ||||||
| 					os.success(); | 					os.success(); | ||||||
| 					this.$store.state.i.twoFactorEnabled = false; | 					this.$i.twoFactorEnabled = false; | ||||||
| 				}); | 				}); | ||||||
| 			}); | 			}); | ||||||
| 		}, | 		}, | ||||||
|  | @ -146,7 +146,7 @@ export default defineComponent({ | ||||||
| 				token: this.token | 				token: this.token | ||||||
| 			}).then(() => { | 			}).then(() => { | ||||||
| 				os.success(); | 				os.success(); | ||||||
| 				this.$store.state.i.twoFactorEnabled = true; | 				this.$i.twoFactorEnabled = true; | ||||||
| 			}).catch(e => { | 			}).catch(e => { | ||||||
| 				os.dialog({ | 				os.dialog({ | ||||||
| 					type: 'error', | 					type: 'error', | ||||||
|  | @ -213,9 +213,9 @@ export default defineComponent({ | ||||||
| 								name: 'Misskey' | 								name: 'Misskey' | ||||||
| 							}, | 							}, | ||||||
| 							user: { | 							user: { | ||||||
| 								id: byteify(this.$store.state.i.id, 'ascii'), | 								id: byteify(this.$i.id, 'ascii'), | ||||||
| 								name: this.$store.state.i.username, | 								name: this.$i.username, | ||||||
| 								displayName: this.$store.state.i.name, | 								displayName: this.$i.name, | ||||||
| 							}, | 							}, | ||||||
| 							pubKeyCredParams: [{ alg: -7, type: 'public-key' }], | 							pubKeyCredParams: [{ alg: -7, type: 'public-key' }], | ||||||
| 							timeout: 60000, | 							timeout: 60000, | ||||||
|  |  | ||||||
|  | @ -2,13 +2,13 @@ | ||||||
| <FormBase> | <FormBase> | ||||||
| 	<FormKeyValueView> | 	<FormKeyValueView> | ||||||
| 		<template #key>ID</template> | 		<template #key>ID</template> | ||||||
| 		<template #value><span class="_monospace">{{ $store.state.i.id }}</span></template> | 		<template #value><span class="_monospace">{{ $i.id }}</span></template> | ||||||
| 	</FormKeyValueView> | 	</FormKeyValueView> | ||||||
| 
 | 
 | ||||||
| 	<FormGroup> | 	<FormGroup> | ||||||
| 		<FormKeyValueView> | 		<FormKeyValueView> | ||||||
| 			<template #key>{{ $t('registeredDate') }}</template> | 			<template #key>{{ $t('registeredDate') }}</template> | ||||||
| 			<template #value><MkTime :time="$store.state.i.createdAt" mode="detail"/></template> | 			<template #value><MkTime :time="$i.createdAt" mode="detail"/></template> | ||||||
| 		</FormKeyValueView> | 		</FormKeyValueView> | ||||||
| 	</FormGroup> | 	</FormGroup> | ||||||
| 
 | 
 | ||||||
|  | @ -104,27 +104,27 @@ | ||||||
| 		<template #label>{{ $t('other') }}</template> | 		<template #label>{{ $t('other') }}</template> | ||||||
| 		<FormKeyValueView> | 		<FormKeyValueView> | ||||||
| 			<template #key>emailVerified</template> | 			<template #key>emailVerified</template> | ||||||
| 			<template #value>{{ $store.state.i.emailVerified ? $t('yes') : $t('no') }}</template> | 			<template #value>{{ $i.emailVerified ? $t('yes') : $t('no') }}</template> | ||||||
| 		</FormKeyValueView> | 		</FormKeyValueView> | ||||||
| 		<FormKeyValueView> | 		<FormKeyValueView> | ||||||
| 			<template #key>twoFactorEnabled</template> | 			<template #key>twoFactorEnabled</template> | ||||||
| 			<template #value>{{ $store.state.i.twoFactorEnabled ? $t('yes') : $t('no') }}</template> | 			<template #value>{{ $i.twoFactorEnabled ? $t('yes') : $t('no') }}</template> | ||||||
| 		</FormKeyValueView> | 		</FormKeyValueView> | ||||||
| 		<FormKeyValueView> | 		<FormKeyValueView> | ||||||
| 			<template #key>securityKeys</template> | 			<template #key>securityKeys</template> | ||||||
| 			<template #value>{{ $store.state.i.securityKeys ? $t('yes') : $t('no') }}</template> | 			<template #value>{{ $i.securityKeys ? $t('yes') : $t('no') }}</template> | ||||||
| 		</FormKeyValueView> | 		</FormKeyValueView> | ||||||
| 		<FormKeyValueView> | 		<FormKeyValueView> | ||||||
| 			<template #key>usePasswordLessLogin</template> | 			<template #key>usePasswordLessLogin</template> | ||||||
| 			<template #value>{{ $store.state.i.usePasswordLessLogin ? $t('yes') : $t('no') }}</template> | 			<template #value>{{ $i.usePasswordLessLogin ? $t('yes') : $t('no') }}</template> | ||||||
| 		</FormKeyValueView> | 		</FormKeyValueView> | ||||||
| 		<FormKeyValueView> | 		<FormKeyValueView> | ||||||
| 			<template #key>isModerator</template> | 			<template #key>isModerator</template> | ||||||
| 			<template #value>{{ $store.state.i.isModerator ? $t('yes') : $t('no') }}</template> | 			<template #value>{{ $i.isModerator ? $t('yes') : $t('no') }}</template> | ||||||
| 		</FormKeyValueView> | 		</FormKeyValueView> | ||||||
| 		<FormKeyValueView> | 		<FormKeyValueView> | ||||||
| 			<template #key>isAdmin</template> | 			<template #key>isAdmin</template> | ||||||
| 			<template #value>{{ $store.state.i.isAdmin ? $t('yes') : $t('no') }}</template> | 			<template #value>{{ $i.isAdmin ? $t('yes') : $t('no') }}</template> | ||||||
| 		</FormKeyValueView> | 		</FormKeyValueView> | ||||||
| 	</FormGroup> | 	</FormGroup> | ||||||
| </FormBase> | </FormBase> | ||||||
|  | @ -171,7 +171,7 @@ export default defineComponent({ | ||||||
| 		this.$emit('info', this.INFO); | 		this.$emit('info', this.INFO); | ||||||
| 
 | 
 | ||||||
| 		os.api('users/stats', { | 		os.api('users/stats', { | ||||||
| 			userId: this.$store.state.i.id | 			userId: this.$i.id | ||||||
| 		}).then(stats => { | 		}).then(stats => { | ||||||
| 			this.stats = stats; | 			this.stats = stats; | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
|  | @ -5,17 +5,17 @@ | ||||||
| 		<div class="_title"><Fa :icon="faColumns"/> </div> | 		<div class="_title"><Fa :icon="faColumns"/> </div> | ||||||
| 		<div class="_content"> | 		<div class="_content"> | ||||||
| 			<div>{{ $t('defaultNavigationBehaviour') }}</div> | 			<div>{{ $t('defaultNavigationBehaviour') }}</div> | ||||||
| 			<MkSwitch v-model:value="deckNavWindow">{{ $t('openInWindow') }}</MkSwitch> | 			<MkSwitch v-model:value="navWindow">{{ $t('openInWindow') }}</MkSwitch> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="_content"> | 		<div class="_content"> | ||||||
| 			<MkSwitch v-model:value="deckAlwaysShowMainColumn"> | 			<MkSwitch v-model:value="alwaysShowMainColumn"> | ||||||
| 				{{ $t('_deck.alwaysShowMainColumn') }} | 				{{ $t('_deck.alwaysShowMainColumn') }} | ||||||
| 			</MkSwitch> | 			</MkSwitch> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="_content"> | 		<div class="_content"> | ||||||
| 			<div>{{ $t('_deck.columnAlign') }}</div> | 			<div>{{ $t('_deck.columnAlign') }}</div> | ||||||
| 			<MkRadio v-model="deckColumnAlign" value="left">{{ $t('left') }}</MkRadio> | 			<MkRadio v-model="columnAlign" value="left">{{ $t('left') }}</MkRadio> | ||||||
| 			<MkRadio v-model="deckColumnAlign" value="center">{{ $t('center') }}</MkRadio> | 			<MkRadio v-model="columnAlign" value="center">{{ $t('center') }}</MkRadio> | ||||||
| 		</div> | 		</div> | ||||||
| 	</section> | 	</section> | ||||||
| 
 | 
 | ||||||
|  | @ -38,6 +38,7 @@ import FormBase from '@/components/form/base.vue'; | ||||||
| import FormGroup from '@/components/form/group.vue'; | import FormGroup from '@/components/form/group.vue'; | ||||||
| import { clientDb, set } from '@/db'; | import { clientDb, set } from '@/db'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { deckStore } from '@/ui/deck/deck-store'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
|  | @ -67,20 +68,9 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		deckNavWindow: { | 		navWindow: deckStore.makeGetterSetter('navWindow'), | ||||||
| 			get() { return this.$store.state.device.deckNavWindow; }, | 		alwaysShowMainColumn: deckStore.makeGetterSetter('alwaysShowMainColumn'), | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'deckNavWindow', value }); } | 		columnAlign: deckStore.makeGetterSetter('columnAlign'), | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		deckAlwaysShowMainColumn: { |  | ||||||
| 			get() { return this.$store.state.device.deckAlwaysShowMainColumn; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'deckAlwaysShowMainColumn', value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		deckColumnAlign: { |  | ||||||
| 			get() { return this.$store.state.device.deckColumnAlign; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'deckColumnAlign', value }); } |  | ||||||
| 		}, |  | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	mounted() { | 	mounted() { | ||||||
|  |  | ||||||
|  | @ -28,9 +28,9 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	async created() { | 	async created() { | ||||||
| 		if (this.$store.state.settings.uploadFolder) { | 		if (this.$store.state.uploadFolder) { | ||||||
| 			this.uploadFolder = await os.api('drive/folders/show', { | 			this.uploadFolder = await os.api('drive/folders/show', { | ||||||
| 				folderId: this.$store.state.settings.uploadFolder | 				folderId: this.$store.state.uploadFolder | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
|  | @ -38,11 +38,11 @@ export default defineComponent({ | ||||||
| 	methods: { | 	methods: { | ||||||
| 		chooseUploadFolder() { | 		chooseUploadFolder() { | ||||||
| 			os.selectDriveFolder(false).then(async folder => { | 			os.selectDriveFolder(false).then(async folder => { | ||||||
| 				await this.$store.dispatch('settings/set', { key: 'uploadFolder', value: folder ? folder.id : null }); | 				this.$store.set('uploadFolder', folder ? folder.id : null); | ||||||
| 				os.success(); | 				os.success(); | ||||||
| 				if (this.$store.state.settings.uploadFolder) { | 				if (this.$store.state.uploadFolder) { | ||||||
| 					this.uploadFolder = await os.api('drive/folders/show', { | 					this.uploadFolder = await os.api('drive/folders/show', { | ||||||
| 						folderId: this.$store.state.settings.uploadFolder | 						folderId: this.$store.state.uploadFolder | ||||||
| 					}); | 					}); | ||||||
| 				} else { | 				} else { | ||||||
| 					this.uploadFolder = null; | 					this.uploadFolder = null; | ||||||
|  |  | ||||||
|  | @ -3,8 +3,8 @@ | ||||||
| 	<FormGroup> | 	<FormGroup> | ||||||
| 		<FormInput v-model:value="emailAddress" type="email"> | 		<FormInput v-model:value="emailAddress" type="email"> | ||||||
| 			{{ $t('emailAddress') }} | 			{{ $t('emailAddress') }} | ||||||
| 			<template #desc v-if="$store.state.i.email && !$store.state.i.emailVerified">{{ $t('verificationEmailSent') }}</template> | 			<template #desc v-if="$i.email && !$i.emailVerified">{{ $t('verificationEmailSent') }}</template> | ||||||
| 			<template #desc v-else-if="emailAddress === $store.state.i.email && $store.state.i.emailVerified">{{ $t('emailVerified') }}</template> | 			<template #desc v-else-if="emailAddress === $i.email && $i.emailVerified">{{ $t('emailVerified') }}</template> | ||||||
| 		</FormInput> | 		</FormInput> | ||||||
| 	</FormGroup> | 	</FormGroup> | ||||||
| 	<FormButton @click="save" primary>{{ $t('save') }}</FormButton> | 	<FormButton @click="save" primary>{{ $t('save') }}</FormButton> | ||||||
|  | @ -44,7 +44,7 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	created() { | 	created() { | ||||||
| 		this.emailAddress = this.$store.state.i.email; | 		this.emailAddress = this.$i.email; | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	mounted() { | 	mounted() { | ||||||
|  |  | ||||||
|  | @ -3,9 +3,9 @@ | ||||||
| 	<FormGroup> | 	<FormGroup> | ||||||
| 		<template #label>{{ $t('emailAddress') }}</template> | 		<template #label>{{ $t('emailAddress') }}</template> | ||||||
| 		<FormLink to="/settings/email/address"> | 		<FormLink to="/settings/email/address"> | ||||||
| 			<template v-if="$store.state.i.email && !$store.state.i.emailVerified" #icon><Fa :icon="faExclamationTriangle" style="color: var(--warn);"/></template> | 			<template v-if="$i.email && !$i.emailVerified" #icon><Fa :icon="faExclamationTriangle" style="color: var(--warn);"/></template> | ||||||
| 			<template v-else-if="$store.state.i.email && $store.state.i.emailVerified" #icon><Fa :icon="faCheck" style="color: var(--success);"/></template> | 			<template v-else-if="$i.email && $i.emailVerified" #icon><Fa :icon="faCheck" style="color: var(--success);"/></template> | ||||||
| 			{{ $store.state.i.email || $t('notSet') }} | 			{{ $i.email || $t('notSet') }} | ||||||
| 		</FormLink> | 		</FormLink> | ||||||
| 	</FormGroup> | 	</FormGroup> | ||||||
| </FormBase> | </FormBase> | ||||||
|  |  | ||||||
|  | @ -38,7 +38,7 @@ | ||||||
| 		<FormSwitch v-model:value="disableShowingAnimatedImages">{{ $t('disableShowingAnimatedImages') }}</FormSwitch> | 		<FormSwitch v-model:value="disableShowingAnimatedImages">{{ $t('disableShowingAnimatedImages') }}</FormSwitch> | ||||||
| 		<FormSwitch v-model:value="useSystemFont">{{ $t('useSystemFont') }}</FormSwitch> | 		<FormSwitch v-model:value="useSystemFont">{{ $t('useSystemFont') }}</FormSwitch> | ||||||
| 		<FormSwitch v-model:value="useOsNativeEmojis">{{ $t('useOsNativeEmojis') }} | 		<FormSwitch v-model:value="useOsNativeEmojis">{{ $t('useOsNativeEmojis') }} | ||||||
| 			<div><Mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div> | 			<div><Mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪" :key="useOsNativeEmojis"/></div> | ||||||
| 		</FormSwitch> | 		</FormSwitch> | ||||||
| 	</FormGroup> | 	</FormGroup> | ||||||
| 
 | 
 | ||||||
|  | @ -96,6 +96,8 @@ import MkLink from '@/components/link.vue'; | ||||||
| import { langs } from '@/config'; | import { langs } from '@/config'; | ||||||
| import { clientDb, set } from '@/db'; | import { clientDb, set } from '@/db'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { defaultStore } from '@/store'; | ||||||
|  | import { ColdDeviceStorage } from '@/store'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
|  | @ -126,85 +128,22 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		serverDisconnectedBehavior: { | 		serverDisconnectedBehavior: defaultStore.makeGetterSetter('serverDisconnectedBehavior'), | ||||||
| 			get() { return this.$store.state.device.serverDisconnectedBehavior; }, | 		reduceAnimation: defaultStore.makeGetterSetter('animation', v => !v, v => !v), | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'serverDisconnectedBehavior', value }); } | 		useBlurEffectForModal: defaultStore.makeGetterSetter('useBlurEffectForModal'), | ||||||
| 		}, | 		showGapBetweenNotesInTimeline: defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline'), | ||||||
| 
 | 		disableAnimatedMfm: defaultStore.makeGetterSetter('animatedMfm', v => !v, v => !v), | ||||||
| 		reduceAnimation: { | 		useOsNativeEmojis: defaultStore.makeGetterSetter('useOsNativeEmojis'), | ||||||
| 			get() { return !this.$store.state.device.animation; }, | 		disableShowingAnimatedImages: defaultStore.makeGetterSetter('disableShowingAnimatedImages'), | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'animation', value: !value }); } | 		loadRawImages: defaultStore.makeGetterSetter('loadRawImages'), | ||||||
| 		}, | 		imageNewTab: defaultStore.makeGetterSetter('imageNewTab'), | ||||||
| 
 | 		nsfw: defaultStore.makeGetterSetter('nsfw'), | ||||||
| 		useBlurEffectForModal: { | 		disablePagesScript: defaultStore.makeGetterSetter('disablePagesScript'), | ||||||
| 			get() { return this.$store.state.device.useBlurEffectForModal; }, | 		showFixedPostForm: defaultStore.makeGetterSetter('showFixedPostForm'), | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'useBlurEffectForModal', value: value }); } | 		defaultSideView: defaultStore.makeGetterSetter('defaultSideView'), | ||||||
| 		}, | 		chatOpenBehavior: ColdDeviceStorage.makeGetterSetter('chatOpenBehavior'), | ||||||
| 
 | 		instanceTicker: defaultStore.makeGetterSetter('instanceTicker'), | ||||||
| 		showGapBetweenNotesInTimeline: { | 		enableInfiniteScroll: defaultStore.makeGetterSetter('enableInfiniteScroll'), | ||||||
| 			get() { return this.$store.state.device.showGapBetweenNotesInTimeline; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'showGapBetweenNotesInTimeline', value: value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		disableAnimatedMfm: { |  | ||||||
| 			get() { return !this.$store.state.device.animatedMfm; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'animatedMfm', value: !value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		useOsNativeEmojis: { |  | ||||||
| 			get() { return this.$store.state.device.useOsNativeEmojis; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'useOsNativeEmojis', value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		imageNewTab: { |  | ||||||
| 			get() { return this.$store.state.device.imageNewTab; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'imageNewTab', value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		disablePagesScript: { |  | ||||||
| 			get() { return this.$store.state.device.disablePagesScript; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'disablePagesScript', value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		showFixedPostForm: { |  | ||||||
| 			get() { return this.$store.state.device.showFixedPostForm; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'showFixedPostForm', value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		defaultSideView: { |  | ||||||
| 			get() { return this.$store.state.device.defaultSideView; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'defaultSideView', value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		chatOpenBehavior: { |  | ||||||
| 			get() { return this.$store.state.device.chatOpenBehavior; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'chatOpenBehavior', value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		instanceTicker: { |  | ||||||
| 			get() { return this.$store.state.device.instanceTicker; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'instanceTicker', value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		loadRawImages: { |  | ||||||
| 			get() { return this.$store.state.device.loadRawImages; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'loadRawImages', value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		disableShowingAnimatedImages: { |  | ||||||
| 			get() { return this.$store.state.device.disableShowingAnimatedImages; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'disableShowingAnimatedImages', value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		nsfw: { |  | ||||||
| 			get() { return this.$store.state.device.nsfw; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'nsfw', value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		enableInfiniteScroll: { |  | ||||||
| 			get() { return this.$store.state.device.enableInfiniteScroll; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'enableInfiniteScroll', value }); } |  | ||||||
| 		}, |  | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	watch: { | 	watch: { | ||||||
|  |  | ||||||
|  | @ -41,13 +41,13 @@ | ||||||
| import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, ref, watch } from 'vue'; | import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, ref, watch } from 'vue'; | ||||||
| import { faCog, faPalette, faPlug, faUser, faListUl, faLock, faCommentSlash, faMusic, faCogs, faEllipsisH, faBan, faShareAlt, faLockOpen, faKey, faBoxes } from '@fortawesome/free-solid-svg-icons'; | import { faCog, faPalette, faPlug, faUser, faListUl, faLock, faCommentSlash, faMusic, faCogs, faEllipsisH, faBan, faShareAlt, faLockOpen, faKey, faBoxes } from '@fortawesome/free-solid-svg-icons'; | ||||||
| import { faLaugh, faBell, faEnvelope } from '@fortawesome/free-regular-svg-icons'; | import { faLaugh, faBell, faEnvelope } from '@fortawesome/free-regular-svg-icons'; | ||||||
| import { store } from '@/store'; |  | ||||||
| import { i18n } from '@/i18n'; | import { i18n } from '@/i18n'; | ||||||
| import FormLink from '@/components/form/link.vue'; | import FormLink from '@/components/form/link.vue'; | ||||||
| import FormGroup from '@/components/form/group.vue'; | import FormGroup from '@/components/form/group.vue'; | ||||||
| import FormBase from '@/components/form/base.vue'; | import FormBase from '@/components/form/base.vue'; | ||||||
| import FormButton from '@/components/form/button.vue'; | import FormButton from '@/components/form/button.vue'; | ||||||
| import { scroll } from '../../scripts/scroll'; | import { scroll } from '@/scripts/scroll'; | ||||||
|  | import { signout } from '@/account'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
|  | @ -101,7 +101,6 @@ export default defineComponent({ | ||||||
| 				case 'plugins': return defineAsyncComponent(() => import('./plugins.vue')); | 				case 'plugins': return defineAsyncComponent(() => import('./plugins.vue')); | ||||||
| 				case 'import-export': return defineAsyncComponent(() => import('./import-export.vue')); | 				case 'import-export': return defineAsyncComponent(() => import('./import-export.vue')); | ||||||
| 				case 'account-info': return defineAsyncComponent(() => import('./account-info.vue')); | 				case 'account-info': return defineAsyncComponent(() => import('./account-info.vue')); | ||||||
| 				case 'regedit': return defineAsyncComponent(() => import('./regedit.vue')); |  | ||||||
| 				case 'experimental-features': return defineAsyncComponent(() => import('./experimental-features.vue')); | 				case 'experimental-features': return defineAsyncComponent(() => import('./experimental-features.vue')); | ||||||
| 				default: return null; | 				default: return null; | ||||||
| 			} | 			} | ||||||
|  | @ -125,8 +124,7 @@ export default defineComponent({ | ||||||
| 			onInfo, | 			onInfo, | ||||||
| 			component, | 			component, | ||||||
| 			logout: () => { | 			logout: () => { | ||||||
| 				store.dispatch('logout'); | 				signout(); | ||||||
| 				location.href = '/'; |  | ||||||
| 			}, | 			}, | ||||||
| 			faPalette, faPlug, faUser, faListUl, faLock, faLaugh, faCommentSlash, faMusic, faBell, faCogs, faEllipsisH, faBan, faShareAlt, faLockOpen, faKey, faBoxes, faEnvelope, | 			faPalette, faPlug, faUser, faListUl, faLock, faLaugh, faCommentSlash, faMusic, faBell, faCogs, faEllipsisH, faBan, faShareAlt, faLockOpen, faKey, faBoxes, faEnvelope, | ||||||
| 		}; | 		}; | ||||||
|  |  | ||||||
|  | @ -57,11 +57,11 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		integrations() { | 		integrations() { | ||||||
| 			return this.$store.state.i.integrations; | 			return this.$i.integrations; | ||||||
| 		}, | 		}, | ||||||
| 		 | 		 | ||||||
| 		meta() { | 		meta() { | ||||||
| 			return this.$store.state.instance.meta; | 			return this.$instance; | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | @ -74,7 +74,7 @@ export default defineComponent({ | ||||||
| 	mounted() { | 	mounted() { | ||||||
| 		this.$emit('info', this.INFO); | 		this.$emit('info', this.INFO); | ||||||
| 
 | 
 | ||||||
| 		document.cookie = `igi=${this.$store.state.i.token}; path=/;` + | 		document.cookie = `igi=${this.$i.token}; path=/;` + | ||||||
| 			` max-age=31536000;` + | 			` max-age=31536000;` + | ||||||
| 			(document.location.protocol.startsWith('https') ? ' secure' : ''); | 			(document.location.protocol.startsWith('https') ? ' secure' : ''); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -58,7 +58,7 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		configure() { | 		configure() { | ||||||
| 			const includingTypes = notificationTypes.filter(x => !this.$store.state.i.mutingNotificationTypes.includes(x)); | 			const includingTypes = notificationTypes.filter(x => !this.$i.mutingNotificationTypes.includes(x)); | ||||||
| 			os.popup(import('@/components/notification-setting-window.vue'), { | 			os.popup(import('@/components/notification-setting-window.vue'), { | ||||||
| 				includingTypes, | 				includingTypes, | ||||||
| 				showGlobalToggle: false, | 				showGlobalToggle: false, | ||||||
|  | @ -68,7 +68,7 @@ export default defineComponent({ | ||||||
| 					await os.apiWithDialog('i/update', { | 					await os.apiWithDialog('i/update', { | ||||||
| 						mutingNotificationTypes: notificationTypes.filter(x => !value.includes(x)), | 						mutingNotificationTypes: notificationTypes.filter(x => !value.includes(x)), | ||||||
| 					}).then(i => { | 					}).then(i => { | ||||||
| 						this.$store.state.i.mutingNotificationTypes = i.mutingNotificationTypes; | 						this.$i.mutingNotificationTypes = i.mutingNotificationTypes; | ||||||
| 					}); | 					}); | ||||||
| 				} | 				} | ||||||
| 			}, 'closed'); | 			}, 'closed'); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
| <FormBase> | <FormBase> | ||||||
| 	<FormSwitch :value="$store.state.i.injectFeaturedNote" @update:value="onChangeInjectFeaturedNote"> | 	<FormSwitch :value="$i.injectFeaturedNote" @update:value="onChangeInjectFeaturedNote"> | ||||||
| 		{{ $t('showFeaturedNotesInTimeline') }} | 		{{ $t('showFeaturedNotesInTimeline') }} | ||||||
| 	</FormSwitch> | 	</FormSwitch> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ | ||||||
| 		<details> | 		<details> | ||||||
| 			<summary><Fa :icon="faFolderOpen"/> {{ $t('manage') }}</summary> | 			<summary><Fa :icon="faFolderOpen"/> {{ $t('manage') }}</summary> | ||||||
| 			<MkSelect v-model:value="selectedPluginId"> | 			<MkSelect v-model:value="selectedPluginId"> | ||||||
| 				<option v-for="x in $store.state.deviceUser.plugins" :value="x.id" :key="x.id">{{ x.name }}</option> | 				<option v-for="x in plugins" :value="x.id" :key="x.id">{{ x.name }}</option> | ||||||
| 			</MkSelect> | 			</MkSelect> | ||||||
| 			<template v-if="selectedPlugin"> | 			<template v-if="selectedPlugin"> | ||||||
| 				<div style="margin: -8px 0 8px 0;"> | 				<div style="margin: -8px 0 8px 0;"> | ||||||
|  | @ -55,6 +55,7 @@ import MkSelect from '@/components/ui/select.vue'; | ||||||
| import MkInfo from '@/components/ui/info.vue'; | import MkInfo from '@/components/ui/info.vue'; | ||||||
| import MkSwitch from '@/components/ui/switch.vue'; | import MkSwitch from '@/components/ui/switch.vue'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { ColdDeviceStorage } from '@/store'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
|  | @ -68,6 +69,7 @@ export default defineComponent({ | ||||||
| 	data() { | 	data() { | ||||||
| 		return { | 		return { | ||||||
| 			script: '', | 			script: '', | ||||||
|  | 			plugins: ColdDeviceStorage.get('plugins'), | ||||||
| 			selectedPluginId: null, | 			selectedPluginId: null, | ||||||
| 			faPlug, faSave, faTrashAlt, faFolderOpen, faDownload, faCog | 			faPlug, faSave, faTrashAlt, faFolderOpen, faDownload, faCog | ||||||
| 		} | 		} | ||||||
|  | @ -76,11 +78,22 @@ export default defineComponent({ | ||||||
| 	computed: { | 	computed: { | ||||||
| 		selectedPlugin() { | 		selectedPlugin() { | ||||||
| 			if (this.selectedPluginId == null) return null; | 			if (this.selectedPluginId == null) return null; | ||||||
| 			return this.$store.state.deviceUser.plugins.find(x => x.id === this.selectedPluginId); | 			return this.plugins.find(x => x.id === this.selectedPluginId); | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	methods: { | 	methods: { | ||||||
|  | 		installPlugin({ id, meta, ast, token }) { | ||||||
|  | 			ColdDeviceStorage.set('plugins', this.plugins.concat({ | ||||||
|  | 				...meta, | ||||||
|  | 				id, | ||||||
|  | 				active: true, | ||||||
|  | 				configData: {}, | ||||||
|  | 				token: token, | ||||||
|  | 				ast: ast | ||||||
|  | 			})); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
| 		async install() { | 		async install() { | ||||||
| 			let ast; | 			let ast; | ||||||
| 			try { | 			try { | ||||||
|  | @ -137,7 +150,7 @@ export default defineComponent({ | ||||||
| 				}, 'closed'); | 				}, 'closed'); | ||||||
| 			}); | 			}); | ||||||
| 
 | 
 | ||||||
| 			this.$store.commit('deviceUser/installPlugin', { | 			this.installPlugin({ | ||||||
| 				id: uuid(), | 				id: uuid(), | ||||||
| 				meta: { | 				meta: { | ||||||
| 					name, version, author, description, permissions, config | 					name, version, author, description, permissions, config | ||||||
|  | @ -154,7 +167,7 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		uninstall() { | 		uninstall() { | ||||||
| 			this.$store.commit('deviceUser/uninstallPlugin', this.selectedPluginId); | 			ColdDeviceStorage.set('plugins', this.plugins.filter(x => x.id !== this.selectedPluginId)); | ||||||
| 			os.success(); | 			os.success(); | ||||||
| 			this.$nextTick(() => { | 			this.$nextTick(() => { | ||||||
| 				location.reload(); | 				location.reload(); | ||||||
|  | @ -171,10 +184,9 @@ export default defineComponent({ | ||||||
| 			const { canceled, result } = await os.form(this.selectedPlugin.name, config); | 			const { canceled, result } = await os.form(this.selectedPlugin.name, config); | ||||||
| 			if (canceled) return; | 			if (canceled) return; | ||||||
| 
 | 
 | ||||||
| 			this.$store.commit('deviceUser/configPlugin', { | 			const plugins = ColdDeviceStorage.get('plugins'); | ||||||
| 				id: this.selectedPluginId, | 			plugins.find(p => p.id === this.selectedPluginId).configData = result; | ||||||
| 				config: result | 			ColdDeviceStorage.set('plugins', plugins); | ||||||
| 			}); |  | ||||||
| 
 | 
 | ||||||
| 			this.$nextTick(() => { | 			this.$nextTick(() => { | ||||||
| 				location.reload(); | 				location.reload(); | ||||||
|  | @ -182,10 +194,9 @@ export default defineComponent({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		changeActive(plugin, active) { | 		changeActive(plugin, active) { | ||||||
| 			this.$store.commit('deviceUser/changePluginActive', { | 			const plugins = ColdDeviceStorage.get('plugins'); | ||||||
| 				id: plugin.id, | 			plugins.find(p => p.id === plugin.id).active = active; | ||||||
| 				active: active | 			ColdDeviceStorage.set('plugins', plugins); | ||||||
| 			}); |  | ||||||
| 
 | 
 | ||||||
| 			this.$nextTick(() => { | 			this.$nextTick(() => { | ||||||
| 				location.reload(); | 				location.reload(); | ||||||
|  |  | ||||||
|  | @ -35,6 +35,7 @@ import FormSelect from '@/components/form/select.vue'; | ||||||
| import FormBase from '@/components/form/base.vue'; | import FormBase from '@/components/form/base.vue'; | ||||||
| import FormGroup from '@/components/form/group.vue'; | import FormGroup from '@/components/form/group.vue'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { defaultStore } from '@/store'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
|  | @ -60,27 +61,16 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		defaultNoteVisibility: { | 		defaultNoteVisibility: defaultStore.makeGetterSetter('defaultNoteVisibility'), | ||||||
| 			get() { return this.$store.state.settings.defaultNoteVisibility; }, | 		defaultNoteLocalOnly: defaultStore.makeGetterSetter('defaultNoteLocalOnly'), | ||||||
| 			set(value) { this.$store.dispatch('settings/set', { key: 'defaultNoteVisibility', value }); } | 		rememberNoteVisibility: defaultStore.makeGetterSetter('rememberNoteVisibility'), | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		defaultNoteLocalOnly: { |  | ||||||
| 			get() { return this.$store.state.settings.defaultNoteLocalOnly; }, |  | ||||||
| 			set(value) { this.$store.dispatch('settings/set', { key: 'defaultNoteLocalOnly', value }); } |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		rememberNoteVisibility: { |  | ||||||
| 			get() { return this.$store.state.settings.rememberNoteVisibility; }, |  | ||||||
| 			set(value) { this.$store.dispatch('settings/set', { key: 'rememberNoteVisibility', value }); } |  | ||||||
| 		}, |  | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	created() { | 	created() { | ||||||
| 		this.isLocked = this.$store.state.i.isLocked; | 		this.isLocked = this.$i.isLocked; | ||||||
| 		this.autoAcceptFollowed = this.$store.state.i.autoAcceptFollowed; | 		this.autoAcceptFollowed = this.$i.autoAcceptFollowed; | ||||||
| 		this.noCrawle = this.$store.state.i.noCrawle; | 		this.noCrawle = this.$i.noCrawle; | ||||||
| 		this.isExplorable = this.$store.state.i.isExplorable; | 		this.isExplorable = this.$i.isExplorable; | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	mounted() { | 	mounted() { | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| <template> | <template> | ||||||
| <FormBase> | <FormBase> | ||||||
| 	<FormGroup> | 	<FormGroup> | ||||||
| 		<div class="_formItem _formPanel llvierxe" :style="{ backgroundImage: $store.state.i.bannerUrl ? `url(${ $store.state.i.bannerUrl })` : null }"> | 		<div class="_formItem _formPanel llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }"> | ||||||
| 			<MkAvatar class="avatar" :user="$store.state.i"/> | 			<MkAvatar class="avatar" :user="$i"/> | ||||||
| 		</div> | 		</div> | ||||||
| 		<FormButton @click="changeAvatar" primary>{{ $t('_profile.changeAvatar') }}</FormButton> | 		<FormButton @click="changeAvatar" primary>{{ $t('_profile.changeAvatar') }}</FormButton> | ||||||
| 		<FormButton @click="changeBanner" primary>{{ $t('_profile.changeBanner') }}</FormButton> | 		<FormButton @click="changeBanner" primary>{{ $t('_profile.changeBanner') }}</FormButton> | ||||||
|  | @ -100,24 +100,24 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	created() { | 	created() { | ||||||
| 		this.name = this.$store.state.i.name; | 		this.name = this.$i.name; | ||||||
| 		this.description = this.$store.state.i.description; | 		this.description = this.$i.description; | ||||||
| 		this.location = this.$store.state.i.location; | 		this.location = this.$i.location; | ||||||
| 		this.birthday = this.$store.state.i.birthday; | 		this.birthday = this.$i.birthday; | ||||||
| 		this.avatarId = this.$store.state.i.avatarId; | 		this.avatarId = this.$i.avatarId; | ||||||
| 		this.bannerId = this.$store.state.i.bannerId; | 		this.bannerId = this.$i.bannerId; | ||||||
| 		this.isBot = this.$store.state.i.isBot; | 		this.isBot = this.$i.isBot; | ||||||
| 		this.isCat = this.$store.state.i.isCat; | 		this.isCat = this.$i.isCat; | ||||||
| 		this.alwaysMarkNsfw = this.$store.state.i.alwaysMarkNsfw; | 		this.alwaysMarkNsfw = this.$i.alwaysMarkNsfw; | ||||||
| 
 | 
 | ||||||
| 		this.fieldName0 = this.$store.state.i.fields[0] ? this.$store.state.i.fields[0].name : null; | 		this.fieldName0 = this.$i.fields[0] ? this.$i.fields[0].name : null; | ||||||
| 		this.fieldValue0 = this.$store.state.i.fields[0] ? this.$store.state.i.fields[0].value : null; | 		this.fieldValue0 = this.$i.fields[0] ? this.$i.fields[0].value : null; | ||||||
| 		this.fieldName1 = this.$store.state.i.fields[1] ? this.$store.state.i.fields[1].name : null; | 		this.fieldName1 = this.$i.fields[1] ? this.$i.fields[1].name : null; | ||||||
| 		this.fieldValue1 = this.$store.state.i.fields[1] ? this.$store.state.i.fields[1].value : null; | 		this.fieldValue1 = this.$i.fields[1] ? this.$i.fields[1].value : null; | ||||||
| 		this.fieldName2 = this.$store.state.i.fields[2] ? this.$store.state.i.fields[2].name : null; | 		this.fieldName2 = this.$i.fields[2] ? this.$i.fields[2].name : null; | ||||||
| 		this.fieldValue2 = this.$store.state.i.fields[2] ? this.$store.state.i.fields[2].value : null; | 		this.fieldValue2 = this.$i.fields[2] ? this.$i.fields[2].value : null; | ||||||
| 		this.fieldName3 = this.$store.state.i.fields[3] ? this.$store.state.i.fields[3].name : null; | 		this.fieldName3 = this.$i.fields[3] ? this.$i.fields[3].name : null; | ||||||
| 		this.fieldValue3 = this.$store.state.i.fields[3] ? this.$store.state.i.fields[3].value : null; | 		this.fieldValue3 = this.$i.fields[3] ? this.$i.fields[3].value : null; | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	mounted() { | 	mounted() { | ||||||
|  | @ -227,10 +227,10 @@ export default defineComponent({ | ||||||
| 				alwaysMarkNsfw: !!this.alwaysMarkNsfw, | 				alwaysMarkNsfw: !!this.alwaysMarkNsfw, | ||||||
| 			}).then(i => { | 			}).then(i => { | ||||||
| 				this.saving = false; | 				this.saving = false; | ||||||
| 				this.$store.state.i.avatarId = i.avatarId; | 				this.$i.avatarId = i.avatarId; | ||||||
| 				this.$store.state.i.avatarUrl = i.avatarUrl; | 				this.$i.avatarUrl = i.avatarUrl; | ||||||
| 				this.$store.state.i.bannerId = i.bannerId; | 				this.$i.bannerId = i.bannerId; | ||||||
| 				this.$store.state.i.bannerUrl = i.bannerUrl; | 				this.$i.bannerUrl = i.bannerUrl; | ||||||
| 
 | 
 | ||||||
| 				if (notify) { | 				if (notify) { | ||||||
| 					os.success(); | 					os.success(); | ||||||
|  |  | ||||||
|  | @ -43,8 +43,8 @@ import FormInput from '@/components/form/input.vue'; | ||||||
| import FormRadios from '@/components/form/radios.vue'; | import FormRadios from '@/components/form/radios.vue'; | ||||||
| import FormBase from '@/components/form/base.vue'; | import FormBase from '@/components/form/base.vue'; | ||||||
| import FormButton from '@/components/form/button.vue'; | import FormButton from '@/components/form/button.vue'; | ||||||
| import { defaultSettings } from '@/store'; |  | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
|  | import { defaultStore } from '@/store'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
| 	components: { | 	components: { | ||||||
|  | @ -67,24 +67,14 @@ export default defineComponent({ | ||||||
| 					handler: this.preview | 					handler: this.preview | ||||||
| 				} | 				} | ||||||
| 			}, | 			}, | ||||||
| 			reactions: JSON.parse(JSON.stringify(this.$store.state.settings.reactions)), | 			reactions: JSON.parse(JSON.stringify(this.$store.state.reactions)), | ||||||
| 			faLaugh, faSave, faEye, faUndo, faPlus | 			faLaugh, faSave, faEye, faUndo, faPlus | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	computed: { | 	computed: { | ||||||
| 		useFullReactionPicker: { | 		reactionPickerWidth: defaultStore.makeGetterSetter('reactionPickerWidth'), | ||||||
| 			get() { return this.$store.state.device.useFullReactionPicker; }, | 		reactionPickerHeight: defaultStore.makeGetterSetter('reactionPickerHeight'), | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'useFullReactionPicker', value: value }); } |  | ||||||
| 		}, |  | ||||||
| 		reactionPickerWidth: { |  | ||||||
| 			get() { return this.$store.state.device.reactionPickerWidth; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'reactionPickerWidth', value: value }); } |  | ||||||
| 		}, |  | ||||||
| 		reactionPickerHeight: { |  | ||||||
| 			get() { return this.$store.state.device.reactionPickerHeight; }, |  | ||||||
| 			set(value) { this.$store.commit('device/set', { key: 'reactionPickerHeight', value: value }); } |  | ||||||
| 		}, |  | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	watch: { | 	watch: { | ||||||
|  | @ -102,7 +92,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	methods: { | 	methods: { | ||||||
| 		save() { | 		save() { | ||||||
| 			this.$store.dispatch('settings/set', { key: 'reactions', value: this.reactions }); | 			this.$store.set('reactions', this.reactions); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		remove(reaction, ev) { | 		remove(reaction, ev) { | ||||||
|  | @ -129,7 +119,7 @@ export default defineComponent({ | ||||||
| 			}); | 			}); | ||||||
| 			if (canceled) return; | 			if (canceled) return; | ||||||
| 
 | 
 | ||||||
| 			this.reactions = JSON.parse(JSON.stringify(defaultSettings.reactions)); | 			this.reactions = JSON.parse(JSON.stringify(this.$store.def.reactions.default)); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		chooseEmoji(ev) { | 		chooseEmoji(ev) { | ||||||
|  |  | ||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue