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-style-loader": "4.1.2", | ||||
| 		"vuedraggable": "4.0.1", | ||||
| 		"vuex": "4.0.0-rc.2", | ||||
| 		"vuex-persistedstate": "3.1.0", | ||||
| 		"web-push": "3.4.4", | ||||
| 		"webpack": "5.10.1", | ||||
| 		"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(); | ||||
| 
 | ||||
| 		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() { | ||||
|  |  | |||
|  | @ -17,8 +17,8 @@ | |||
| 	</ol> | ||||
| 	<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"> | ||||
| 			<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-else-if="!useOsNativeEmojis"><img :src="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="!$store.state.useOsNativeEmojis"><img :src="emoji.url" :alt="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="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: { | ||||
| 		showing() { | ||||
| 			if (!this.showing) { | ||||
|  | @ -151,7 +145,7 @@ export default defineComponent({ | |||
| 		this.setPosition(); | ||||
| 
 | ||||
| 		//#region Construct Emoji DB | ||||
| 		const customEmojis = this.$store.state.instance.meta.emojis; | ||||
| 		const customEmojis = this.$instance.emojis; | ||||
| 		const emojiDefinitions: EmojiDef[] = []; | ||||
| 
 | ||||
| 		for (const x of customEmojis) { | ||||
|  |  | |||
|  | @ -28,7 +28,6 @@ declare global { | |||
| 	interface Window extends CaptchaContainer { | ||||
| 	} | ||||
| } | ||||
| import * as os from '@/os'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	props: { | ||||
|  | @ -101,7 +100,7 @@ export default defineComponent({ | |||
| 			if (this.captcha.render && this.$refs.captcha instanceof Element) { | ||||
| 				this.captcha.render(this.$refs.captcha, { | ||||
| 					sitekey: this.sitekey, | ||||
| 					theme: this.$store.state.device.darkMode ? 'dark' : 'light', | ||||
| 					theme: this.$store.state.darkMode ? 'dark' : 'light', | ||||
| 					callback: this.callback, | ||||
| 					'expired-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_', | ||||
| 			name: 'list', | ||||
| 			tag: 'div', | ||||
| 			'data-direction': this.direction, | ||||
| 			'data-reversed': this.reversed ? 'true' : 'false', | ||||
| 		} : { | ||||
| 			class: 'sqadhkmv _list_', | ||||
| 		}, this.items.map((item, i) => { | ||||
| 			const el = this.$slots.default({ | ||||
| 				item: item | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ import { | |||
| 	faFilm | ||||
| 	} from '@fortawesome/free-solid-svg-icons'; | ||||
| import ImgWithBlurhash from './img-with-blurhash.vue'; | ||||
| import { ColdDeviceStorage } from '@/store'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
|  | @ -89,12 +90,12 @@ export default defineComponent({ | |||
| 	}, | ||||
| 	mounted() { | ||||
| 		const audioTag = this.$refs.volumectrl as HTMLAudioElement; | ||||
| 		if (audioTag) audioTag.volume = this.$store.state.device.mediaVolume; | ||||
| 		if (audioTag) audioTag.volume = ColdDeviceStorage.get('mediaVolume'); | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		volumechange() { | ||||
| 			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" | ||||
| 	: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"/> | ||||
| 		<p>{{ $t('avatar') }}</p> | ||||
| 	</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"/> | ||||
| 		<p>{{ $t('banner') }}</p> | ||||
| 	</div> | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ | |||
| 		<template v-if="!hover"><Fa :icon="faFolder" fixed-width/></template> | ||||
| 		{{ folder.name }} | ||||
| 	</p> | ||||
| 	<p class="upload" v-if="$store.state.settings.uploadFolder == folder.id"> | ||||
| 	<p class="upload" v-if="$store.state.uploadFolder == folder.id"> | ||||
| 		{{ $t('uploadFolder') }} | ||||
| 	</p> | ||||
| 	<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', { | ||||
| 				folderId: this.folder.id | ||||
| 			}).then(() => { | ||||
| 				if (this.$store.state.settings.uploadFolder === this.folder.id) { | ||||
| 					this.$store.dispatch('settings/set', { | ||||
| 						key: 'uploadFolder', | ||||
| 						value: null | ||||
| 					}); | ||||
| 				if (this.$store.state.uploadFolder === this.folder.id) { | ||||
| 					this.$store.set('uploadFolder', null); | ||||
| 				} | ||||
| 			}).catch(err => { | ||||
| 				switch(err.id) { | ||||
|  | @ -238,10 +235,7 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		setAsUploadFolder() { | ||||
| 			this.$store.dispatch('settings/set', { | ||||
| 				key: 'uploadFolder', | ||||
| 				value: this.folder.id | ||||
| 			}); | ||||
| 			this.$store.set('uploadFolder', this.folder.id); | ||||
| 		}, | ||||
| 
 | ||||
| 		onContextmenu(e) { | ||||
|  |  | |||
|  | @ -136,7 +136,7 @@ export default defineComponent({ | |||
| 	}, | ||||
| 
 | ||||
| 	mounted() { | ||||
| 		if (this.$store.state.device.enableInfiniteScroll && this.$refs.loadMoreFiles) { | ||||
| 		if (this.$store.state.enableInfiniteScroll && this.$refs.loadMoreFiles) { | ||||
| 			this.$nextTick(() => { | ||||
| 				this.ilFilesObserver.observe((this.$refs.loadMoreFiles as Vue).$el) | ||||
| 			}); | ||||
|  | @ -159,7 +159,7 @@ export default defineComponent({ | |||
| 	}, | ||||
| 
 | ||||
| 	activated() { | ||||
| 		if (this.$store.state.device.enableInfiniteScroll) { | ||||
| 		if (this.$store.state.enableInfiniteScroll) { | ||||
| 			this.$nextTick(() => { | ||||
| 				this.ilFilesObserver.observe((this.$refs.loadMoreFiles as Vue).$el) | ||||
| 			}); | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ | |||
| 						tabindex="0" | ||||
| 					> | ||||
| 						<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> | ||||
| 				</div> | ||||
| 				<div v-if="searchResultUnicode.length > 0"> | ||||
|  | @ -45,7 +45,7 @@ | |||
| 				<section> | ||||
| 					<header class="_acrylic"><Fa :icon="faClock" fixed-width/> {{ $t('recentUsed') }}</header> | ||||
| 					<div> | ||||
| 						<button v-for="emoji in $store.state.device.recentlyUsedEmojis" | ||||
| 						<button v-for="emoji in $store.state.recentlyUsedEmojis" | ||||
| 							class="_button" | ||||
| 							@click="chosen(emoji, $event)" | ||||
| 							:key="emoji" | ||||
|  | @ -67,7 +67,7 @@ | |||
| 						@click="chosen(emoji, $event)" | ||||
| 						: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> | ||||
| 				</div> | ||||
| 			</section> | ||||
|  | @ -100,6 +100,7 @@ import MkModal from '@/components/ui/modal.vue'; | |||
| import Particle from '@/components/particle.vue'; | ||||
| import * as os from '@/os'; | ||||
| import { isDeviceTouch } from '../scripts/is-device-touch'; | ||||
| import { emojiCategories } from '@/instance'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
|  | @ -125,12 +126,12 @@ export default defineComponent({ | |||
| 		return { | ||||
| 			emojilist: markRaw(emojilist), | ||||
| 			getStaticImageUrl, | ||||
| 			pinned: this.$store.state.settings.reactions, | ||||
| 			width: this.asReactionPicker ? this.$store.state.device.reactionPickerWidth : 3, | ||||
| 			height: this.asReactionPicker ? this.$store.state.device.reactionPickerHeight : 2, | ||||
| 			pinned: this.$store.state.reactions, | ||||
| 			width: this.asReactionPicker ? this.$store.state.reactionPickerWidth : 3, | ||||
| 			height: this.asReactionPicker ? this.$store.state.reactionPickerHeight : 2, | ||||
| 			big: this.asReactionPicker ? isDeviceTouch : false, | ||||
| 			customEmojiCategories: this.$store.getters['instance/emojiCategories'], | ||||
| 			customEmojis: this.$store.state.instance.meta.emojis, | ||||
| 			customEmojiCategories: emojiCategories, | ||||
| 			customEmojis: this.$instance.emojis, | ||||
| 			visibleCategories: {}, | ||||
| 			q: null, | ||||
| 			searchResultCustom: [], | ||||
|  | @ -346,10 +347,10 @@ export default defineComponent({ | |||
| 
 | ||||
| 			// 最近使った絵文字更新 | ||||
| 			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.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 { | ||||
| 	font-size: 80%; | ||||
| 	padding: 0 16px 8px 16px; | ||||
| 	opacity: 0.8; | ||||
| 
 | ||||
| 	&:empty { | ||||
| 		display: none; | ||||
|  | @ -21,6 +22,7 @@ | |||
| ._formCaption { | ||||
| 	font-size: 80%; | ||||
| 	padding: 8px 16px 0 16px; | ||||
| 	opacity: 0.8; | ||||
| 
 | ||||
| 	&:empty { | ||||
| 		display: none; | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ import copyToClipboard from '@/scripts/copy-to-clipboard'; | |||
| import { router } from '@/router'; | ||||
| import { ui, url } from '@/config'; | ||||
| import { popout } from '@/scripts/popout'; | ||||
| import { ColdDeviceStorage } from '@/store'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	inject: { | ||||
|  | @ -98,8 +99,8 @@ export default defineComponent({ | |||
| 
 | ||||
| 		nav() { | ||||
| 			if (this.to.startsWith('/my/messaging')) { | ||||
| 				if (this.$store.state.device.chatOpenBehavior === 'window') return this.window(); | ||||
| 				if (this.$store.state.device.chatOpenBehavior === 'popout') return this.popout(); | ||||
| 				if (ColdDeviceStorage.get('chatOpenBehavior') === 'window') return this.window(); | ||||
| 				if (ColdDeviceStorage.get('chatOpenBehavior') === 'popout') return this.popout(); | ||||
| 			} | ||||
| 
 | ||||
| 			if (this.behavior) { | ||||
|  | @ -111,12 +112,13 @@ export default defineComponent({ | |||
| 			if (this.navHook) { | ||||
| 				this.navHook(this.to); | ||||
| 			} 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); | ||||
| 				} | ||||
| 				if (this.$store.state.device.deckNavWindow && (ui === 'deck') && this.to !== '/') { | ||||
| 					return this.window(); | ||||
| 				} | ||||
| 				// TODO: a.vueからdeck-sotreを参照したくないのでなんとかする | ||||
| 				//if (deckStore.state.device.deckNavWindow && (ui === 'deck') && this.to !== '/') { | ||||
| 				//	return this.window(); | ||||
| 				//} | ||||
| 				if (ui === 'desktop') { | ||||
| 					return this.window(); | ||||
| 				} | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| <template> | ||||
| <span class="mk-acct" v-once> | ||||
| 	<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> | ||||
| </template> | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ export default defineComponent({ | |||
| 			return this.user.isCat; | ||||
| 		}, | ||||
| 		url(): string { | ||||
| 			return this.$store.state.device.disableShowingAnimatedImages | ||||
| 			return this.$store.state.disableShowingAnimatedImages | ||||
| 				? getStaticImageUrl(this.user.avatarUrl) | ||||
| 				: this.user.avatarUrl; | ||||
| 		}, | ||||
|  |  | |||
|  | @ -54,13 +54,13 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		useOsNativeEmojis(): boolean { | ||||
| 			return this.$store.state.device.useOsNativeEmojis && !this.isReaction; | ||||
| 			return this.$store.state.useOsNativeEmojis && !this.isReaction; | ||||
| 		}, | ||||
| 
 | ||||
| 		ce() { | ||||
| 			let ce = []; | ||||
| 			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; | ||||
| 		} | ||||
| 	}, | ||||
|  | @ -72,7 +72,7 @@ export default defineComponent({ | |||
| 					const customEmoji = this.ce.find(x => x.name === this.emoji.substr(1, this.emoji.length - 2)); | ||||
| 					if (customEmoji) { | ||||
| 						this.customEmoji = customEmoji; | ||||
| 						this.url = this.$store.state.device.disableShowingAnimatedImages | ||||
| 						this.url = this.$store.state.disableShowingAnimatedImages | ||||
| 							? getStaticImageUrl(customEmoji.url) | ||||
| 							: customEmoji.url; | ||||
| 					} | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
| <transition :name="$store.state.device.animation ? 'zoom' : ''" appear> | ||||
| <transition :name="$store.state.animation ? 'zoom' : ''" appear> | ||||
| 	<div class="mjndxjcg"> | ||||
| 		<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/> | ||||
| 		<p><Fa :icon="faExclamationTriangle"/> {{ $t('somethingHappened') }}</p> | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ export default defineComponent({ | |||
| 	}, | ||||
| 	methods: { | ||||
| 		search() { | ||||
| 			const engine = this.$store.state.settings.webSearchEngine || | ||||
| 			const engine = this.$store.state.webSearchEngine || | ||||
| 				'https://www.google.com/search?q={{query}}'; | ||||
| 			const url = engine.replace('{{query}}', this.query) | ||||
| 			window.open(url, '_blank'); | ||||
|  |  | |||
|  | @ -278,7 +278,7 @@ export default defineComponent({ | |||
| 			} | ||||
| 
 | ||||
| 			// 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'); | ||||
| 			this.chartInstance = markRaw(new Chart(this.$refs.chart, { | ||||
|  |  | |||
|  | @ -58,7 +58,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 	computed: { | ||||
| 		menu(): string[] { | ||||
| 			return this.$store.state.deviceUser.menu; | ||||
| 			return this.$store.state.menu; | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ | |||
| import { defineComponent } from 'vue'; | ||||
| import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; | ||||
| import * as os from '@/os'; | ||||
| import { ColdDeviceStorage } from '@/store'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	props: { | ||||
|  | @ -45,12 +46,12 @@ export default defineComponent({ | |||
| 	}, | ||||
| 	mounted() { | ||||
| 		const audioTag = this.$refs.audio as HTMLAudioElement; | ||||
| 		if (audioTag) audioTag.volume = this.$store.state.device.mediaVolume; | ||||
| 		if (audioTag) audioTag.volume = ColdDeviceStorage.get('mediaVolume'); | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		volumechange() { | ||||
| 			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: { | ||||
| 		url(): any { | ||||
| 			let url = this.$store.state.device.disableShowingAnimatedImages | ||||
| 			let url = this.$store.state.disableShowingAnimatedImages | ||||
| 				? getStaticImageUrl(this.image.thumbnailUrl) | ||||
| 				: this.image.thumbnailUrl; | ||||
| 
 | ||||
| 			if (this.$store.state.device.loadRemoteMedia) { | ||||
| 				url = null; | ||||
| 			} else if (this.raw || this.$store.state.device.loadRawImages) { | ||||
| 			if (this.raw || this.$store.state.loadRawImages) { | ||||
| 				url = this.image.url; | ||||
| 			} | ||||
| 
 | ||||
|  | @ -68,7 +66,7 @@ export default defineComponent({ | |||
| 	created() { | ||||
| 		// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする | ||||
| 		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) { | ||||
| 				this.color = extractAvgColorFromBlurhash(this.image.blurhash); | ||||
| 			} | ||||
|  | @ -79,7 +77,7 @@ export default defineComponent({ | |||
| 	}, | ||||
| 	methods: { | ||||
| 		onClick() { | ||||
| 			if (this.$store.state.device.imageNewTab) { | ||||
| 			if (this.$store.state.imageNewTab) { | ||||
| 				window.open(this.image.url, '_blank'); | ||||
| 			} else { | ||||
| 				os.popup(ImageViewer, { | ||||
|  |  | |||
|  | @ -48,7 +48,7 @@ export default defineComponent({ | |||
| 		} | ||||
| 	}, | ||||
| 	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> | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| 	<span class="me" v-if="isMe">{{ $t('you') }}</span> | ||||
| 	<span class="main"> | ||||
| 		<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> | ||||
| </MkA> | ||||
| <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)}`; | ||||
| 		}, | ||||
| 		isMe(): boolean { | ||||
| 			return this.$store.getters.isSignedIn && ( | ||||
| 				`@${this.username}@${toUnicode(this.host)}` === `@${this.$store.state.i.username}@${toUnicode(localHost)}`.toLowerCase() | ||||
| 			return this.$i && ( | ||||
| 				`@${this.username}@${toUnicode(this.host)}` === `@${this.$i.username}@${toUnicode(localHost)}`.toLowerCase() | ||||
| 			); | ||||
| 		} | ||||
| 	}, | ||||
|  |  | |||
|  | @ -82,22 +82,22 @@ export default defineComponent({ | |||
| 					let style; | ||||
| 					switch (token.node.props.name) { | ||||
| 						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; | ||||
| 						} | ||||
| 						case 'jelly': { | ||||
| 							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; | ||||
| 						} | ||||
| 						case 'twitch': { | ||||
| 							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; | ||||
| 						} | ||||
| 						case 'shake': { | ||||
| 							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; | ||||
| 						} | ||||
| 						case 'spin': { | ||||
|  | @ -110,15 +110,15 @@ export default defineComponent({ | |||
| 								token.node.props.args.y ? 'mfm-spinY' : | ||||
| 								'mfm-spin'; | ||||
| 							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; | ||||
| 						} | ||||
| 						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; | ||||
| 						} | ||||
| 						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; | ||||
| 						} | ||||
| 						case 'flip': { | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| 			<XNoteHeader class="header" :note="note" :mini="true"/> | ||||
| 			<div class="body"> | ||||
| 				<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"/> | ||||
| 				</p> | ||||
| 				<div class="content" v-show="note.cw == null || showContent"> | ||||
|  |  | |||
|  | @ -43,14 +43,14 @@ | |||
| 			<MkInstanceTicker v-if="showTicker" class="ticker" :instance="appearNote.user.instance"/> | ||||
| 			<div class="body"> | ||||
| 				<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"/> | ||||
| 				</p> | ||||
| 				<div class="content" v-show="appearNote.cw == null || showContent"> | ||||
| 					<div class="text"> | ||||
| 						<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> | ||||
| 						<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> | ||||
| 					</div> | ||||
| 					<div class="files" v-if="appearNote.files.length > 0"> | ||||
|  | @ -182,7 +182,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 	computed: { | ||||
| 		rs() { | ||||
| 			return this.$store.state.settings.reactions; | ||||
| 			return this.$store.state.reactions; | ||||
| 		}, | ||||
| 		keymap(): any { | ||||
| 			return { | ||||
|  | @ -222,11 +222,11 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		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 { | ||||
| 			return this.$store.getters.isSignedIn && (this.$store.state.i.id === this.note.userId); | ||||
| 			return this.$i && (this.$i.id === this.note.userId); | ||||
| 		}, | ||||
| 
 | ||||
| 		canRenote(): boolean { | ||||
|  | @ -262,14 +262,14 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		showTicker() { | ||||
| 			if (this.$store.state.device.instanceTicker === 'always') return true; | ||||
| 			if (this.$store.state.device.instanceTicker === 'remote' && this.appearNote.user.instance) return true; | ||||
| 			if (this.$store.state.instanceTicker === 'always') return true; | ||||
| 			if (this.$store.state.instanceTicker === 'remote' && this.appearNote.user.instance) return true; | ||||
| 			return false; | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	async created() { | ||||
| 		if (this.$store.getters.isSignedIn) { | ||||
| 		if (this.$i) { | ||||
| 			this.connection = os.stream; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -282,7 +282,7 @@ export default defineComponent({ | |||
| 			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) { | ||||
| 			os.api('notes/children', { | ||||
|  | @ -305,7 +305,7 @@ export default defineComponent({ | |||
| 	mounted() { | ||||
| 		this.capture(true); | ||||
| 
 | ||||
| 		if (this.$store.getters.isSignedIn) { | ||||
| 		if (this.$i) { | ||||
| 			this.connection.on('_connected_', this.onStreamConnected); | ||||
| 		} | ||||
| 	}, | ||||
|  | @ -313,7 +313,7 @@ export default defineComponent({ | |||
| 	beforeUnmount() { | ||||
| 		this.decapture(true); | ||||
| 
 | ||||
| 		if (this.$store.getters.isSignedIn) { | ||||
| 		if (this.$i) { | ||||
| 			this.connection.off('_connected_', this.onStreamConnected); | ||||
| 		} | ||||
| 	}, | ||||
|  | @ -340,14 +340,14 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		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 }); | ||||
| 				if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		decapture(withHandler = false) { | ||||
| 			if (this.$store.getters.isSignedIn) { | ||||
| 			if (this.$i) { | ||||
| 				this.connection.send('un', { | ||||
| 					id: this.appearNote.id | ||||
| 				}); | ||||
|  | @ -389,7 +389,7 @@ export default defineComponent({ | |||
| 						[reaction]: currentCount + 1 | ||||
| 					}; | ||||
| 
 | ||||
| 					if (body.userId === this.$store.state.i.id) { | ||||
| 					if (body.userId === this.$i.id) { | ||||
| 						n.myReaction = reaction; | ||||
| 					} | ||||
| 
 | ||||
|  | @ -414,7 +414,7 @@ export default defineComponent({ | |||
| 						[reaction]: Math.max(0, currentCount - 1) | ||||
| 					}; | ||||
| 
 | ||||
| 					if (body.userId === this.$store.state.i.id) { | ||||
| 					if (body.userId === this.$i.id) { | ||||
| 						n.myReaction = null; | ||||
| 					} | ||||
| 
 | ||||
|  | @ -434,7 +434,7 @@ export default defineComponent({ | |||
| 					choices[choice] = { | ||||
| 						...choices[choice], | ||||
| 						votes: choices[choice].votes + 1, | ||||
| 						...(body.userId === this.$store.state.i.id ? { | ||||
| 						...(body.userId === this.$i.id ? { | ||||
| 							isVoted: true | ||||
| 						} : {}) | ||||
| 					}; | ||||
|  | @ -614,7 +614,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 		getMenu() { | ||||
| 			let menu; | ||||
| 			if (this.$store.getters.isSignedIn) { | ||||
| 			if (this.$i) { | ||||
| 				const statePromise = os.api('notes/state', { | ||||
| 					noteId: this.appearNote.id | ||||
| 				}); | ||||
|  | @ -649,7 +649,7 @@ export default defineComponent({ | |||
| 					text: this.$t('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, | ||||
| 					text: this.$t('unwatch'), | ||||
| 					action: () => this.toggleWatch(false) | ||||
|  | @ -658,7 +658,7 @@ export default defineComponent({ | |||
| 					text: this.$t('watch'), | ||||
| 					action: () => this.toggleWatch(true) | ||||
| 				}) : 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, | ||||
| 					text: this.$t('unpin'), | ||||
| 					action: () => this.togglePin(false) | ||||
|  | @ -667,7 +667,7 @@ export default defineComponent({ | |||
| 					text: this.$t('pin'), | ||||
| 					action: () => this.togglePin(true) | ||||
| 				} : undefined, | ||||
| 				...(this.$store.state.i.isModerator || this.$store.state.i.isAdmin ? [ | ||||
| 				...(this.$i.isModerator || this.$i.isAdmin ? [ | ||||
| 					null, | ||||
| 					{ | ||||
| 						icon: faBullhorn, | ||||
|  | @ -676,7 +676,7 @@ export default defineComponent({ | |||
| 					}] | ||||
| 					: [] | ||||
| 				), | ||||
| 				...(this.appearNote.userId != this.$store.state.i.id ? [ | ||||
| 				...(this.appearNote.userId != this.$i.id ? [ | ||||
| 					null, | ||||
| 					{ | ||||
| 						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, | ||||
| 					this.appearNote.userId == this.$store.state.i.id ? { | ||||
| 					this.appearNote.userId == this.$i.id ? { | ||||
| 						icon: faEdit, | ||||
| 						text: this.$t('deleteAndEdit'), | ||||
| 						action: this.delEdit | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ | |||
| 	</XList> | ||||
| 
 | ||||
| 	<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"><MkLoading inline/></template> | ||||
| 		</button> | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| 		<XNotification v-else :notification="notification" :with-time="true" :full="true" class="_panel notification" :key="notification.id"/> | ||||
| 	</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"><MkLoading inline/></template> | ||||
| 	</button> | ||||
|  | @ -59,7 +59,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 	computed: { | ||||
| 		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 | ||||
| 		}, | ||||
| 		// TODO: vue/vuexのバグか仕様かは不明なものの、プロフィール更新するなどして $store.state.i が更新されると、 | ||||
| 		// TODO: vue/vuexのバグか仕様かは不明なものの、プロフィール更新するなどして $i が更新されると、 | ||||
| 		// mutingNotificationTypes に変化が無くてもこのハンドラーが呼び出され無駄なリロードが発生するのを直す | ||||
| 		'$store.state.i.mutingNotificationTypes': { | ||||
| 		'$i.mutingNotificationTypes': { | ||||
| 			handler() { | ||||
| 				if (this.includeTypes === null) { | ||||
| 					this.reload(); | ||||
|  |  | |||
|  | @ -49,9 +49,9 @@ export default defineComponent({ | |||
| 				canvas.toBlob(blob => { | ||||
| 					const data = new FormData(); | ||||
| 					data.append('file', blob); | ||||
| 					data.append('i', this.$store.state.i.token); | ||||
| 					if (this.$store.state.settings.uploadFolder) { | ||||
| 						data.append('folderId', this.$store.state.settings.uploadFolder); | ||||
| 					data.append('i', this.$i.token); | ||||
| 					if (this.$store.state.uploadFolder) { | ||||
| 						data.append('folderId', this.$store.state.uploadFolder); | ||||
| 					} | ||||
| 
 | ||||
| 					fetch(apiUrl + '/drive/files/create', { | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| <template> | ||||
| <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"/> | ||||
| </div> | ||||
| </template> | ||||
|  |  | |||
|  | @ -35,9 +35,9 @@ export default defineComponent({ | |||
| 	created() { | ||||
| 		this.hpml = new Hpml(this.page, { | ||||
| 			randomSeed: Math.random(), | ||||
| 			visitor: this.$store.state.i, | ||||
| 			visitor: this.$i, | ||||
| 			url: url, | ||||
| 			enableAiScript: !this.$store.state.device.disablePagesScript | ||||
| 			enableAiScript: !this.$store.state.disablePagesScript | ||||
| 		}); | ||||
| 	}, | ||||
| 
 | ||||
|  |  | |||
|  | @ -135,8 +135,8 @@ export default defineComponent({ | |||
| 			poll: null, | ||||
| 			useCw: false, | ||||
| 			cw: null, | ||||
| 			localOnly: this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.localOnly : this.$store.state.settings.defaultNoteLocalOnly, | ||||
| 			visibility: this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.visibility : this.$store.state.settings.defaultNoteVisibility, | ||||
| 			localOnly: this.$store.state.rememberNoteVisibility ? this.$store.state.localOnly : this.$store.state.defaultNoteLocalOnly, | ||||
| 			visibility: this.$store.state.rememberNoteVisibility ? this.$store.state.visibility : this.$store.state.defaultNoteVisibility, | ||||
| 			visibleUsers: [], | ||||
| 			autocomplete: null, | ||||
| 			draghover: false, | ||||
|  | @ -198,7 +198,7 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		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}`; | ||||
| 
 | ||||
| 				// 自分は除外 | ||||
| 				if (this.$store.state.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 == null) continue; | ||||
| 				if (this.$i.username == x.username && x.host == host) continue; | ||||
| 
 | ||||
| 				// 重複は除外 | ||||
| 				if (this.text.indexOf(`${mention} `) != -1) continue; | ||||
|  | @ -243,12 +243,12 @@ export default defineComponent({ | |||
| 			this.visibility = this.reply.visibility; | ||||
| 			if (this.reply.visibility === 'specified') { | ||||
| 				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 => { | ||||
| 					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 => { | ||||
| 						this.visibleUsers.push(user); | ||||
| 					}); | ||||
|  | @ -262,7 +262,7 @@ export default defineComponent({ | |||
| 		} | ||||
| 
 | ||||
| 		// 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.cw = this.reply.cw; | ||||
| 		} | ||||
|  | @ -376,7 +376,7 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		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); | ||||
| 			}); | ||||
| 		}, | ||||
|  | @ -399,14 +399,14 @@ export default defineComponent({ | |||
| 			}, { | ||||
| 				changeVisibility: visibility => { | ||||
| 					this.visibility = visibility; | ||||
| 					if (this.$store.state.settings.rememberNoteVisibility) { | ||||
| 						this.$store.commit('deviceUser/setVisibility', visibility); | ||||
| 					if (this.$store.state.rememberNoteVisibility) { | ||||
| 						this.$store.set('visibility', visibility); | ||||
| 					} | ||||
| 				}, | ||||
| 				changeLocalOnly: localOnly => { | ||||
| 					this.localOnly = localOnly; | ||||
| 					if (this.$store.state.settings.rememberNoteVisibility) { | ||||
| 						this.$store.commit('deviceUser/setLocalOnly', localOnly); | ||||
| 					if (this.$store.state.rememberNoteVisibility) { | ||||
| 						this.$store.set('localOnly', localOnly); | ||||
| 					} | ||||
| 				} | ||||
| 			}, 'closed'); | ||||
|  | @ -440,7 +440,7 @@ export default defineComponent({ | |||
| 					const file = item.getAsFile(); | ||||
| 					const lio = file.name.lastIndexOf('.'); | ||||
| 					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); | ||||
| 				} | ||||
| 			} | ||||
|  |  | |||
|  | @ -53,7 +53,7 @@ export default defineComponent({ | |||
| 	}, | ||||
| 	computed: { | ||||
| 		canToggle(): boolean { | ||||
| 			return !this.reaction.match(/@\w/) && this.$store.getters.isSignedIn; | ||||
| 			return !this.reaction.match(/@\w/) && this.$i; | ||||
| 		}, | ||||
| 	}, | ||||
| 	watch: { | ||||
|  |  | |||
|  | @ -25,7 +25,7 @@ export default defineComponent({ | |||
| 	}, | ||||
| 	computed: { | ||||
| 		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: '', | ||||
| 			flag: false, | ||||
| 			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"> | ||||
| 			<div> | ||||
| 				<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> | ||||
| 				<MkA class="item index" active-class="active" to="/" exact> | ||||
| 					<Fa :icon="faHome" fixed-width/><span class="text">{{ $t('timeline') }}</span> | ||||
|  | @ -25,7 +25,7 @@ | |||
| 					</component> | ||||
| 				</template> | ||||
| 				<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> | ||||
| 				</button> | ||||
| 				<button class="item _button" @click="more"> | ||||
|  | @ -49,6 +49,7 @@ import { host } from '@/config'; | |||
| import { search } from '@/scripts/search'; | ||||
| import * as os from '@/os'; | ||||
| import { sidebarDef } from '@/sidebar'; | ||||
| import { getAccounts, addAccount, login } from '@/account'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	data() { | ||||
|  | @ -67,7 +68,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 	computed: { | ||||
| 		menu(): string[] { | ||||
| 			return this.$store.state.deviceUser.menu; | ||||
| 			return this.$store.state.menu; | ||||
| 		}, | ||||
| 
 | ||||
| 		otherNavItemIndicated(): boolean { | ||||
|  | @ -84,7 +85,7 @@ export default defineComponent({ | |||
| 			this.showing = false; | ||||
| 		}, | ||||
| 
 | ||||
| 		'$store.state.device.sidebarDisplay'() { | ||||
| 		'$store.reactiveState.sidebarDisplay'() { | ||||
| 			this.calcViewState(); | ||||
| 		}, | ||||
| 
 | ||||
|  | @ -108,8 +109,8 @@ export default defineComponent({ | |||
| 
 | ||||
| 	methods: { | ||||
| 		calcViewState() { | ||||
| 			this.iconOnly = (window.innerWidth <= 1279) || (this.$store.state.device.sidebarDisplay === 'icon'); | ||||
| 			this.hidden = (window.innerWidth <= 650) || (this.$store.state.device.sidebarDisplay === 'hide'); | ||||
| 			this.iconOnly = (window.innerWidth <= 1279) || (this.$store.state.sidebarDisplay === 'icon'); | ||||
| 			this.hidden = (window.innerWidth <= 650); | ||||
| 		}, | ||||
| 
 | ||||
| 		show() { | ||||
|  | @ -133,7 +134,8 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		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 => ({ | ||||
| 				type: 'user', | ||||
|  | @ -144,8 +146,8 @@ export default defineComponent({ | |||
| 			os.modalMenu([...[{ | ||||
| 				type: 'link', | ||||
| 				text: this.$t('profile'), | ||||
| 				to: `/@${ this.$store.state.i.username }`, | ||||
| 				avatar: this.$store.state.i, | ||||
| 				to: `/@${ this.$i.username }`, | ||||
| 				avatar: this.$i, | ||||
| 			}, null, ...accountItems, { | ||||
| 				icon: faPlus, | ||||
| 				text: this.$t('addAcount'), | ||||
|  | @ -169,7 +171,7 @@ export default defineComponent({ | |||
| 				text: this.$t('dashboard'), | ||||
| 				to: '/instance', | ||||
| 				icon: faTachometerAlt, | ||||
| 			}, null, this.$store.state.i.isAdmin ? { | ||||
| 			}, null, this.$i.isAdmin ? { | ||||
| 				type: 'link', | ||||
| 				text: this.$t('settings'), | ||||
| 				to: '/instance/settings', | ||||
|  | @ -230,7 +232,7 @@ export default defineComponent({ | |||
| 		addAcount() { | ||||
| 			os.popup(import('./signin-dialog.vue'), {}, { | ||||
| 				done: res => { | ||||
| 					this.$store.dispatch('addAcount', res); | ||||
| 					addAccount(res.id, res.i); | ||||
| 					os.success(); | ||||
| 				}, | ||||
| 			}, 'closed'); | ||||
|  | @ -239,30 +241,20 @@ export default defineComponent({ | |||
| 		createAccount() { | ||||
| 			os.popup(import('./signup-dialog.vue'), {}, { | ||||
| 				done: res => { | ||||
| 					this.$store.dispatch('addAcount', res); | ||||
| 					addAccount(res.id, res.i); | ||||
| 					this.switchAccountWithToken(res.i); | ||||
| 				}, | ||||
| 			}, 'closed'); | ||||
| 		}, | ||||
| 
 | ||||
| 		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); | ||||
| 		}, | ||||
| 
 | ||||
| 		switchAccountWithToken(token: string) { | ||||
| 			os.waiting(); | ||||
| 
 | ||||
| 			os.api('i', {}, token).then((i: any) => { | ||||
| 				this.$store.dispatch('switchAccount', { | ||||
| 					...i, | ||||
| 					token: token | ||||
| 				}).then(() => { | ||||
| 					this.$nextTick(() => { | ||||
| 						location.reload(); | ||||
| 					}); | ||||
| 				}); | ||||
| 			}); | ||||
| 			login(token); | ||||
| 		}, | ||||
| 	} | ||||
| }); | ||||
|  |  | |||
|  | @ -56,6 +56,7 @@ import MkInput from './ui/input.vue'; | |||
| import { apiUrl, host } from '@/config'; | ||||
| import { byteify, hexify } from '@/scripts/2fa'; | ||||
| import * as os from '@/os'; | ||||
| import { login } from '@/account'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
|  | @ -97,7 +98,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 	computed: { | ||||
| 		meta() { | ||||
| 			return this.$store.state.instance.meta; | ||||
| 			return this.$instance; | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -114,8 +115,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 		onLogin(res) { | ||||
| 			if (this.autoSet) { | ||||
| 				localStorage.setItem('i', res.i); | ||||
| 				location.reload(); | ||||
| 				login(res.i); | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
|  |  | |||
|  | @ -59,6 +59,7 @@ import MkButton from './ui/button.vue'; | |||
| import MkInput from './ui/input.vue'; | ||||
| import MkSwitch from './ui/switch.vue'; | ||||
| import * as os from '@/os'; | ||||
| import { login } from '@/account'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
|  | @ -99,7 +100,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 	computed: { | ||||
| 		meta() { | ||||
| 			return this.$store.state.instance.meta; | ||||
| 			return this.$instance; | ||||
| 		}, | ||||
| 
 | ||||
| 		shouldDisableSubmitting(): boolean { | ||||
|  | @ -184,8 +185,7 @@ export default defineComponent({ | |||
| 					this.$emit('signup', res); | ||||
| 
 | ||||
| 					if (this.autoSet) { | ||||
| 						localStorage.setItem('i', res.i); | ||||
| 						location.reload(); | ||||
| 						login(res.i); | ||||
| 					} | ||||
| 				}); | ||||
| 			}).catch(() => { | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| 		<span v-if="note.isHidden" style="opacity: 0.5">({{ $t('private') }})</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> | ||||
| 		<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> | ||||
| 	</div> | ||||
| 	<details v-if="note.files.length > 0"> | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <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> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
|  | @ -51,9 +51,9 @@ export default defineComponent({ | |||
| 			connection2: null, | ||||
| 			pagination: null, | ||||
| 			baseQuery: { | ||||
| 				includeMyRenotes: this.$store.state.settings.showMyRenotes, | ||||
| 				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes, | ||||
| 				includeLocalRenotes: this.$store.state.settings.showLocalRenotes | ||||
| 				includeMyRenotes: this.$store.state.showMyRenotes, | ||||
| 				includeRenotedMyNotes: this.$store.state.showRenotedMyNotes, | ||||
| 				includeLocalRenotes: this.$store.state.showLocalRenotes | ||||
| 			}, | ||||
| 			query: {}, | ||||
| 		}; | ||||
|  | @ -66,7 +66,7 @@ export default defineComponent({ | |||
| 			this.$emit('note'); | ||||
| 
 | ||||
| 			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> | ||||
| <transition :name="$store.state.device.animation ? 'fade' : ''" appear> | ||||
| <transition :name="$store.state.animation ? 'fade' : ''" appear> | ||||
| 	<div class="nvlagfpb" @contextmenu.prevent.stop="() => {}"> | ||||
| 		<MkMenu :items="items" @close="$emit('closed')" class="_popup _shadow" :align="'left'"/> | ||||
| 	</div> | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| <template> | ||||
| <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> | ||||
| 	</transition> | ||||
| 	<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> | ||||
| 		</transition> | ||||
| 	</div> | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| 		<slot name="empty"></slot> | ||||
| 	</div> | ||||
| 	<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"><MkLoading inline/></template> | ||||
| 		</MkButton> | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <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="body _popup _shadow _narrow_" @mousedown="onBodyMousedown" @keydown="onKeydown"> | ||||
| 			<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 /> | ||||
| </div> | ||||
| <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 v-else class="mk-url-preview" v-size="{ max: [400, 350] }"> | ||||
| 	<transition name="zoom" mode="out-in"> | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ | |||
| 	</div> | ||||
| 	<div class="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> | ||||
| 		<span v-else style="opacity: 0.7;">{{ $t('noAccountDescription') }}</span> | ||||
| 	</div> | ||||
|  | @ -23,7 +23,7 @@ | |||
| 			<p>{{ $t('followers') }}</p><span>{{ user.followersCount }}</span> | ||||
| 		</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> | ||||
| </template> | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ | |||
| 	<div class="users"> | ||||
| 		<MkUserInfo class="user" v-for="user in users" :user="user" :key="user.id"/> | ||||
| 	</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') }} | ||||
| 	</button> | ||||
| </div> | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ | |||
| 				<p class="username"><MkAcct :user="user"/></p> | ||||
| 			</div> | ||||
| 			<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 class="status"> | ||||
| 				<div> | ||||
|  | @ -22,7 +22,7 @@ | |||
| 					<p>{{ $t('followers') }}</p><span>{{ user.followersCount }}</span> | ||||
| 				</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 v-else> | ||||
| 			<MkLoading/> | ||||
|  |  | |||
|  | @ -79,7 +79,7 @@ export default defineComponent({ | |||
| 		}); | ||||
| 
 | ||||
| 		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(); | ||||
| 
 | ||||
| 			// 最近使ったユーザー更新 | ||||
| 			let recents = this.$store.state.device.recentlyUsedUsers; | ||||
| 			let recents = this.$store.state.recentlyUsedUsers; | ||||
| 			recents = recents.filter(x => x !== 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() { | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ | |||
| 			</div> | ||||
| 		</MkA> | ||||
| 	</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"><Fa :icon="faSpinner" pulse fixed-width/></template> | ||||
| 	</button> | ||||
|  |  | |||
|  | @ -4,26 +4,54 @@ | |||
| 
 | ||||
| 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 widgets from './widgets'; | ||||
| import directives from './directives'; | ||||
| import widgets from '@/widgets'; | ||||
| import directives from '@/directives'; | ||||
| import components from '@/components'; | ||||
| import { version, apiUrl, ui } from '@/config'; | ||||
| import { store } from './store'; | ||||
| import { router } from './router'; | ||||
| import { version, ui } from '@/config'; | ||||
| import { router } from '@/router'; | ||||
| import { applyTheme } from '@/scripts/theme'; | ||||
| import { isDeviceDarkmode } from '@/scripts/is-device-darkmode'; | ||||
| import { i18n, lang } from './i18n'; | ||||
| import { i18n, lang } from '@/i18n'; | ||||
| 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}`); | ||||
| 
 | ||||
| if (_DEV_) { | ||||
| 	console.warn('Development mode!!!'); | ||||
| 
 | ||||
| 	(window as any).$i = $i; | ||||
| 	(window as any).$store = defaultStore; | ||||
| 
 | ||||
| 	window.addEventListener('error', event => { | ||||
| 		console.error(event); | ||||
| 		/* | ||||
|  | @ -81,80 +109,52 @@ html.setAttribute('lang', lang); | |||
| //#endregion
 | ||||
| 
 | ||||
| //#region Fetch user
 | ||||
| const signout = () => { | ||||
| 	store.dispatch('logout'); | ||||
| 	location.href = '/'; | ||||
| }; | ||||
| 
 | ||||
| // ユーザーをフェッチしてコールバックする
 | ||||
| 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(); | ||||
| if ($i && $i.token) { | ||||
| 	if (_DEV_) { | ||||
| 		console.log('account cache found. refreshing...'); | ||||
| 	} | ||||
| 
 | ||||
| 		// 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(); | ||||
| 	} | ||||
| 
 | ||||
| 	// 後から新鮮なデータをフェッチ
 | ||||
| 	fetchme(store.state.i.token).then(freshData => { | ||||
| 		store.dispatch('mergeMe', freshData); | ||||
| 	}); | ||||
| 	refreshAccount(); | ||||
| } else { | ||||
| 	// Get token from localStorage
 | ||||
| 	let i = localStorage.getItem('i'); | ||||
| 	if (_DEV_) { | ||||
| 		console.log('no account cache found.'); | ||||
| 	} | ||||
| 
 | ||||
| 	// 連携ログインの場合用にCookieを参照する
 | ||||
| 	if (i == null || i === 'null') { | ||||
| 		i = (document.cookie.match(/igi=(\w+)/) || [null, null])[1]; | ||||
| 	} | ||||
| 	const i = (document.cookie.match(/igi=(\w+)/) || [null, null])[1]; | ||||
| 
 | ||||
| 	if (i != null && i !== 'null') { | ||||
| 		if (_DEV_) { | ||||
| 			console.log('signing...'); | ||||
| 		} | ||||
| 
 | ||||
| 		try { | ||||
| 			document.body.innerHTML = '<div>Please wait...</div>'; | ||||
| 			const me = await fetchme(i); | ||||
| 			await store.dispatch('login', me); | ||||
| 			await login(i); | ||||
| 			location.reload(); | ||||
| 		} catch (e) { | ||||
| 			// Render the error screen
 | ||||
| 			// TODO: ちゃんとしたコンポーネントをレンダリングする(v10とかのトラブルシューティングゲーム付きのやつみたいな)
 | ||||
| 			document.body.innerHTML = '<div id="err">Oops!</div>'; | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (_DEV_) { | ||||
| 			console.log('not signed in'); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| //#endregion
 | ||||
| 
 | ||||
| store.dispatch('instance/fetch').then(() => { | ||||
| fetchInstance().then(() => { | ||||
| 	// Init service worker
 | ||||
| 	//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 ( | ||||
| 	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 === 'desktop'                  ? import('@/ui/desktop.vue') : | ||||
| 	import('@/ui/default.vue') | ||||
|  | @ -164,7 +164,12 @@ if (_DEV_) { | |||
| 	app.config.performance = true; | ||||
| } | ||||
| 
 | ||||
| app.use(store); | ||||
| app.config.globalProperties = { | ||||
| 	$i, | ||||
| 	$store: defaultStore, | ||||
| 	$instance: instance, | ||||
| }; | ||||
| 
 | ||||
| app.use(router); | ||||
| app.use(i18n); | ||||
| // eslint-disable-next-line vue/component-definition-name-casing
 | ||||
|  | @ -180,46 +185,34 @@ await router.isReady(); | |||
| 
 | ||||
| app.mount('body'); | ||||
| 
 | ||||
| // 他のタブと永続化されたstateを同期
 | ||||
| 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 => { | ||||
| watch(defaultStore.reactiveState.darkMode, (darkMode) => { | ||||
| 	import('@/scripts/theme').then(({ builtinThemes }) => { | ||||
| 		const themes = builtinThemes.concat(store.state.device.themes); | ||||
| 		applyTheme(themes.find(x => x.id === (darkMode ? store.state.device.darkTheme : store.state.device.lightTheme))); | ||||
| 		const themes = builtinThemes.concat(ColdDeviceStorage.get('themes')); | ||||
| 		applyTheme(themes.find(x => x.id === (darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme')))); | ||||
| 	}); | ||||
| }); | ||||
| 
 | ||||
| //#region Sync dark mode
 | ||||
| if (store.state.device.syncDeviceDarkMode) { | ||||
| 	store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() }); | ||||
| if (ColdDeviceStorage.get('syncDeviceDarkMode')) { | ||||
| 	defaultStore.set('darkMode', isDeviceDarkmode()); | ||||
| } | ||||
| 
 | ||||
| window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => { | ||||
| 	if (store.state.device.syncDeviceDarkMode) { | ||||
| 		store.commit('device/set', { key: 'darkMode', value: mql.matches }); | ||||
| 	if (ColdDeviceStorage.get('syncDeviceDarkMode')) { | ||||
| 		defaultStore.set('darkMode', mql.matches); | ||||
| 	} | ||||
| }); | ||||
| //#endregion
 | ||||
| 
 | ||||
| store.watch(state => state.device.useBlurEffectForModal, v => { | ||||
| watch(defaultStore.reactiveState.useBlurEffectForModal, v => { | ||||
| 	document.documentElement.style.setProperty('--modalBgFilter', v ? 'blur(4px)' : 'none'); | ||||
| }, { immediate: true }); | ||||
| 
 | ||||
| let reloadDialogShowing = false; | ||||
| stream.on('_disconnected_', async () => { | ||||
| 	if (store.state.device.serverDisconnectedBehavior === 'reload') { | ||||
| 	if (defaultStore.state.serverDisconnectedBehavior === 'reload') { | ||||
| 		location.reload(); | ||||
| 	} else if (store.state.device.serverDisconnectedBehavior === 'dialog') { | ||||
| 	} else if (defaultStore.state.serverDisconnectedBehavior === 'dialog') { | ||||
| 		if (reloadDialogShowing) return; | ||||
| 		reloadDialogShowing = true; | ||||
| 		const { canceled } = await dialog({ | ||||
|  | @ -240,13 +233,13 @@ stream.on('emojiAdded', data => { | |||
| 	//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 }) => { | ||||
| 		install(plugin); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| if (store.getters.isSignedIn) { | ||||
| if ($i) { | ||||
| 	if ('Notification' in window) { | ||||
| 		// 許可を得ていなかったらリクエスト
 | ||||
| 		if (Notification.permission === 'default') { | ||||
|  | @ -258,103 +251,73 @@ if (store.getters.isSignedIn) { | |||
| 
 | ||||
| 	// 自分の情報が更新されたとき
 | ||||
| 	main.on('meUpdated', i => { | ||||
| 		store.dispatch('mergeMe', i); | ||||
| 		updateAccount(i); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('readAllNotifications', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadNotification: false | ||||
| 		}); | ||||
| 		updateAccount({ hasUnreadNotification: false }); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('unreadNotification', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadNotification: true | ||||
| 		}); | ||||
| 		updateAccount({ hasUnreadNotification: true }); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('unreadMention', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadMentions: true | ||||
| 		}); | ||||
| 		updateAccount({ hasUnreadMentions: true }); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('readAllUnreadMentions', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadMentions: false | ||||
| 		}); | ||||
| 		updateAccount({ hasUnreadMentions: false }); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('unreadSpecifiedNote', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadSpecifiedNotes: true | ||||
| 		}); | ||||
| 		updateAccount({ hasUnreadSpecifiedNotes: true }); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('readAllUnreadSpecifiedNotes', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadSpecifiedNotes: false | ||||
| 		}); | ||||
| 		updateAccount({ hasUnreadSpecifiedNotes: false }); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('readAllMessagingMessages', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadMessagingMessage: false | ||||
| 		}); | ||||
| 		updateAccount({ hasUnreadMessagingMessage: false }); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('unreadMessagingMessage', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadMessagingMessage: true | ||||
| 		}); | ||||
| 
 | ||||
| 		updateAccount({ hasUnreadMessagingMessage: true }); | ||||
| 		sound.play('chatBg'); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('readAllAntennas', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadAntenna: false | ||||
| 		}); | ||||
| 		updateAccount({ hasUnreadAntenna: false }); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('unreadAntenna', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadAntenna: true | ||||
| 		}); | ||||
| 
 | ||||
| 		updateAccount({ hasUnreadAntenna: true }); | ||||
| 		sound.play('antenna'); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('readAllAnnouncements', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadAnnouncement: false | ||||
| 		}); | ||||
| 		updateAccount({ hasUnreadAnnouncement: false }); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('readAllChannels', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadChannel: false | ||||
| 		}); | ||||
| 		updateAccount({ hasUnreadChannel: false }); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('unreadChannel', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadChannel: true | ||||
| 		}); | ||||
| 
 | ||||
| 		updateAccount({ hasUnreadChannel: true }); | ||||
| 		sound.play('channel'); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('readAllAnnouncements', () => { | ||||
| 		store.dispatch('mergeMe', { | ||||
| 			hasUnreadAnnouncement: false | ||||
| 		}); | ||||
| 		updateAccount({ hasUnreadAnnouncement: false }); | ||||
| 	}); | ||||
| 
 | ||||
| 	main.on('clientSettingUpdated', x => { | ||||
| 		store.commit('settings/set', { | ||||
| 			key: x.key, | ||||
| 			value: x.value | ||||
| 		updateAccount({ | ||||
| 			clientData: { | ||||
| 				[x.key]: x.value | ||||
| 			} | ||||
| 		}); | ||||
| 	}); | ||||
| 
 | ||||
|  | @ -364,4 +327,3 @@ if (store.getters.isSignedIn) { | |||
| 		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 { EventEmitter } from 'eventemitter3'; | ||||
| import Stream from '@/scripts/stream'; | ||||
| import { store } from '@/store'; | ||||
| import { apiUrl, debug } from '@/config'; | ||||
| import MkPostFormDialog from '@/components/post-form-dialog.vue'; | ||||
| import MkWaitingDialog from '@/components/waiting-dialog.vue'; | ||||
| import { resolve } from '@/router'; | ||||
| import { device } from './cold-storage'; | ||||
| import { $i } from './account'; | ||||
| 
 | ||||
| const ua = navigator.userAgent.toLowerCase(); | ||||
| 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) => { | ||||
| 		// 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; | ||||
| 
 | ||||
| 		// Send request
 | ||||
|  | @ -368,7 +367,7 @@ export function upload(file: File, folder?: any, name?: string) { | |||
| 			uploads.value.push(ctx); | ||||
| 
 | ||||
| 			const data = new FormData(); | ||||
| 			data.append('i', store.state.i.token); | ||||
| 			data.append('i', $i.token); | ||||
| 			data.append('force', 'true'); | ||||
| 			data.append('file', file); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
| <transition :name="$store.state.device.animation ? 'zoom' : ''" appear> | ||||
| <transition :name="$store.state.animation ? 'zoom' : ''" appear> | ||||
| 	<div class="_section"> | ||||
| 		<div class="mjndxjch _content"> | ||||
| 			<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"/> | ||||
| 				<div class="misskey">Misskey</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> | ||||
| 		</section> | ||||
| 		<section class="_formItem" style="text-align: center; padding: 0 16px;" @click="gravity"> | ||||
|  | @ -144,7 +144,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 	methods: { | ||||
| 		iconLoaded() { | ||||
| 			const emojis = this.$store.state.settings.reactions; | ||||
| 			const emojis = this.$store.state.reactions; | ||||
| 			const containerWidth = this.$refs.about.offsetWidth; | ||||
| 			for (let i = 0; i < 32; i++) { | ||||
| 				this.easterEggEmojis.push({ | ||||
|  |  | |||
|  | @ -59,7 +59,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 	computed: { | ||||
| 		meta() { | ||||
| 			return this.$store.state.instance.meta; | ||||
| 			return this.$instance; | ||||
| 		}, | ||||
| 	}, | ||||
| }); | ||||
|  |  | |||
|  | @ -2,12 +2,12 @@ | |||
| <div class="_section"> | ||||
| 	<MkPagination :pagination="pagination" #default="{items}" class="ruryvtyk _content"> | ||||
| 		<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"> | ||||
| 				<Mfm :text="announcement.text"/> | ||||
| 				<img v-if="announcement.imageUrl" :src="announcement.imageUrl"/> | ||||
| 			</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> | ||||
| 			</div> | ||||
| 		</section> | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| <template> | ||||
| <div class="" v-if="$store.getters.isSignedIn && fetching"> | ||||
| <div class="" v-if="$i && fetching"> | ||||
| 	<MkLoading/> | ||||
| </div> | ||||
| <div v-else-if="$store.getters.isSignedIn"> | ||||
| <div v-else-if="$i"> | ||||
| 	<XForm | ||||
| 		class="form" | ||||
| 		ref="form" | ||||
|  | @ -33,6 +33,7 @@ import { defineComponent } from 'vue'; | |||
| import XForm from './auth.form.vue'; | ||||
| import MkSignin from '@/components/signin.vue'; | ||||
| import * as os from '@/os'; | ||||
| import { login } from '@/account'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
|  | @ -52,7 +53,7 @@ export default defineComponent({ | |||
| 		} | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		if (!this.$store.getters.isSignedIn) return; | ||||
| 		if (!this.$i) return; | ||||
| 
 | ||||
| 		// Fetch session | ||||
| 		os.api('auth/session/show', { | ||||
|  | @ -83,8 +84,7 @@ export default defineComponent({ | |||
| 				location.href = `${this.session.app.callbackUrl}?token=${this.session.token}`; | ||||
| 			} | ||||
| 		}, onLogin(res) { | ||||
| 			localStorage.setItem('i', res.i); | ||||
| 			location.reload(); | ||||
| 			login(res.i); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
|  |  | |||
|  | @ -16,11 +16,11 @@ | |||
| 			<div class="fade"></div> | ||||
| 		</div> | ||||
| 		<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> | ||||
| 
 | ||||
| 	<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"/> | ||||
| </div> | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| <template> | ||||
| <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"> | ||||
| 			<option value="featured"><Fa :icon="faFireAlt"/> {{ $t('_channel.featured') }}</option> | ||||
| 			<option value="following"><Fa :icon="faHeart"/> {{ $t('_channel.following') }}</option> | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| <div v-if="clip" class="_section"> | ||||
| 	<div class="okzinsic _content _panel _vMargin"> | ||||
| 		<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 class="user"> | ||||
| 			<MkAvatar :user="clip.user" class="avatar"/> <MkUserName :user="clip.user" :nowrap="false"/> | ||||
|  | @ -58,7 +58,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 	computed: { | ||||
| 		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: { | ||||
| 		meta() { | ||||
| 			return this.$store.state.instance.meta; | ||||
| 			return this.$instance; | ||||
| 		}, | ||||
| 		tagUsers(): any { | ||||
| 			return { | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ | |||
| 						<p class="acct">@{{ acct(req.follower) }}</p> | ||||
| 					</div> | ||||
| 					<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 class="actions"> | ||||
| 						<button class="_button" @click="accept(req.follower)"><Fa :icon="faCheck"/></button> | ||||
|  |  | |||
|  | @ -160,7 +160,7 @@ export default defineComponent({ | |||
| 	computed: { | ||||
| 		gridColor() { | ||||
| 			// 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: { | ||||
| 		meta() { | ||||
| 			return this.$store.state.instance.meta; | ||||
| 			return this.$instance; | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
|  |  | |||
|  | @ -206,7 +206,7 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		meta() { | ||||
| 			return this.$store.state.instance.meta; | ||||
| 			return this.$instance; | ||||
| 		}, | ||||
| 
 | ||||
| 		isBlocked() { | ||||
|  |  | |||
|  | @ -63,7 +63,7 @@ export default defineComponent({ | |||
| 		this.fetchJobs(); | ||||
| 
 | ||||
| 		// 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'); | ||||
| 
 | ||||
|  |  | |||
|  | @ -259,6 +259,7 @@ import MkInfo from '@/components/ui/info.vue'; | |||
| import { url } from '@/config'; | ||||
| import getAcct from '../../../misc/acct/render'; | ||||
| import * as os from '@/os'; | ||||
| import { fetchInstance } from '@/instance'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
|  | @ -565,7 +566,7 @@ export default defineComponent({ | |||
| 				summalyProxy: this.summalyProxy, | ||||
| 				useStarForReactionFallback: this.useStarForReactionFallback, | ||||
| 			}).then(() => { | ||||
| 				this.$store.dispatch('instance/fetch'); | ||||
| 				fetchInstance(); | ||||
| 				if (withDialog) { | ||||
| 					os.success(); | ||||
| 				} | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ | |||
| 		</div> | ||||
| 		<div class="_section"> | ||||
| 			<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="toggleSuspend" v-model:value="suspended">{{ $t('suspend') }}</MkSwitch> | ||||
| 			</div> | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| 		<div class="history" v-if="messages.length > 0"> | ||||
| 			<MkA v-for="(message, i) in messages" | ||||
| 				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)}`" | ||||
| 				:data-index="i" | ||||
| 				:key="message.id" | ||||
|  | @ -88,7 +88,7 @@ export default defineComponent({ | |||
| 		getAcct, | ||||
| 
 | ||||
| 		isMe(message) { | ||||
| 			return message.userId == this.$store.state.i.id; | ||||
| 			return message.userId == this.$i.id; | ||||
| 		}, | ||||
| 
 | ||||
| 		onMessage(message) { | ||||
|  | @ -111,7 +111,7 @@ export default defineComponent({ | |||
| 					if (found.recipientId) { | ||||
| 						found.isRead = true; | ||||
| 					} 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 lio = file.name.lastIndexOf('.'); | ||||
| 					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 name = this.$store.state.settings.pasteDialog | ||||
| 					const formatted = `${formatTimeString(new Date(file.lastModified), this.$store.state.pastedFileName).replace(/{{number}}/g, '1')}${ext}`; | ||||
| 					const name = this.$store.state.pasteDialog | ||||
| 						? await os.dialog({ | ||||
| 							title: this.$t('enterFileName'), | ||||
| 							input: { | ||||
|  | @ -163,7 +163,7 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		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; | ||||
| 			}); | ||||
| 		}, | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
| 				<img src="/assets/remove.png" alt="Delete"/> | ||||
| 			</button> | ||||
| 			<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"> | ||||
| 					<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"/> | ||||
|  | @ -56,7 +56,7 @@ export default defineComponent({ | |||
| 	}, | ||||
| 	computed: { | ||||
| 		isMe(): boolean { | ||||
| 			return this.message.userId === this.$store.state.i.id; | ||||
| 			return this.message.userId === this.$i.id; | ||||
| 		}, | ||||
| 		urls(): string[] { | ||||
| 			if (this.message.text) { | ||||
|  |  | |||
|  | @ -110,7 +110,7 @@ const Component = defineComponent({ | |||
| 
 | ||||
| 	mounted() { | ||||
| 		this.fetch(); | ||||
| 		if (this.$store.state.device.enableInfiniteScroll) { | ||||
| 		if (this.$store.state.enableInfiniteScroll) { | ||||
| 			this.$nextTick(() => this.ilObserver.observe(this.$refs.loadMore as Element)); | ||||
| 		} | ||||
| 	}, | ||||
|  | @ -224,7 +224,7 @@ const Component = defineComponent({ | |||
| 			const _isBottom = isBottom(this.$el, 64); | ||||
| 
 | ||||
| 			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', { | ||||
| 					id: message.id | ||||
| 				}); | ||||
|  | @ -235,7 +235,7 @@ const Component = defineComponent({ | |||
| 				this.$nextTick(() => { | ||||
| 					this.scrollToBottom(); | ||||
| 				}); | ||||
| 			} else if (message.userId != this.$store.state.i.id) { | ||||
| 			} else if (message.userId != this.$i.id) { | ||||
| 				// Notify | ||||
| 				this.notifyNewMessage(); | ||||
| 			} | ||||
|  | @ -299,7 +299,7 @@ const Component = defineComponent({ | |||
| 		onVisibilitychange() { | ||||
| 			if (document.hidden) return; | ||||
| 			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', { | ||||
| 						id: message.id | ||||
| 					}); | ||||
|  |  | |||
|  | @ -238,7 +238,7 @@ export default defineComponent({ | |||
| 			preview_hashtag: '#test', | ||||
| 			preview_url: `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_small: `<small>${this.$t('_mfm.dummy')}</small>`, | ||||
| 			preview_center: `<center>${this.$t('_mfm.dummy')}</center>`, | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
| <div v-if="$store.getters.isSignedIn"> | ||||
| <div v-if="$i"> | ||||
| 	<div class="waiting _section" v-if="state == 'waiting'"> | ||||
| 		<div class="_content"> | ||||
| 			<MkLoading/> | ||||
|  | @ -41,6 +41,7 @@ import { defineComponent } from 'vue'; | |||
| import MkSignin from '@/components/signin.vue'; | ||||
| import MkButton from '@/components/ui/button.vue'; | ||||
| import * as os from '@/os'; | ||||
| import { login } from '@/account'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
|  | @ -88,8 +89,7 @@ export default defineComponent({ | |||
| 			this.state = 'denied'; | ||||
| 		}, | ||||
| 		onLogin(res) { | ||||
| 			localStorage.setItem('i', res.i); | ||||
| 			location.reload(); | ||||
| 			login(res.i); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
|  |  | |||
|  | @ -141,7 +141,7 @@ export default defineComponent({ | |||
| 				title: this.$t('_pages.newPage'), | ||||
| 				icon: faPencilAlt, | ||||
| 			}), | ||||
| 			author: this.$store.state.i, | ||||
| 			author: this.$i, | ||||
| 			readonly: false, | ||||
| 			page: null, | ||||
| 			pageId: null, | ||||
|  |  | |||
|  | @ -21,9 +21,9 @@ | |||
| 	<div class="_section links"> | ||||
| 		<div class="_content"> | ||||
| 			<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> | ||||
| 				<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> | ||||
| 			</template> | ||||
| 		</div> | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| <template> | ||||
| <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="my"><Fa :icon="faEdit"/> {{ $t('_pages.my') }}</option> | ||||
| 		<option value="liked"><Fa :icon="faHeart"/> {{ $t('_pages.liked') }}</option> | ||||
|  |  | |||
|  | @ -22,11 +22,11 @@ | |||
| 	</div> | ||||
| 
 | ||||
| 	<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> | ||||
| 		</div> | ||||
| 		<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> | ||||
| 			<div class="cells" :style="cellsStyle"> | ||||
|  | @ -35,7 +35,7 @@ | |||
| 					@click="set(i)" | ||||
| 					: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 === false" :src="whiteUser.avatarUrl" alt="white"> | ||||
| 					</template> | ||||
|  | @ -45,11 +45,11 @@ | |||
| 					</template> | ||||
| 				</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> | ||||
| 		</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> | ||||
| 		</div> | ||||
| 	</div> | ||||
|  | @ -126,14 +126,14 @@ export default defineComponent({ | |||
| 
 | ||||
| 	computed: { | ||||
| 		iAmPlayer(): boolean { | ||||
| 			if (!this.$store.getters.isSignedIn) return false; | ||||
| 			return this.game.user1Id == this.$store.state.i.id || this.game.user2Id == this.$store.state.i.id; | ||||
| 			if (!this.$i) return false; | ||||
| 			return this.game.user1Id == this.$i.id || this.game.user2Id == this.$i.id; | ||||
| 		}, | ||||
| 
 | ||||
| 		myColor(): Color { | ||||
| 			if (!this.iAmPlayer) return null; | ||||
| 			if (this.game.user1Id == this.$store.state.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.user1Id == this.$i.id && this.game.black == 1) return true; | ||||
| 			if (this.game.user2Id == this.$i.id && this.game.black == 2) return true; | ||||
| 			return false; | ||||
| 		}, | ||||
| 
 | ||||
|  | @ -234,7 +234,7 @@ export default defineComponent({ | |||
| 		isMyTurn(): boolean { | ||||
| 			if (!this.iAmPlayer) 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) { | ||||
|  |  | |||
|  | @ -169,13 +169,13 @@ export default defineComponent({ | |||
| 			return categories.filter((item, pos) => categories.indexOf(item) == pos); | ||||
| 		}, | ||||
| 		isAccepted(): boolean { | ||||
| 			if (this.game.user1Id == this.$store.state.i.id && this.game.user1Accepted) return true; | ||||
| 			if (this.game.user2Id == this.$store.state.i.id && this.game.user2Accepted) return true; | ||||
| 			if (this.game.user1Id == this.$i.id && this.game.user1Accepted) return true; | ||||
| 			if (this.game.user2Id == this.$i.id && this.game.user2Accepted) return true; | ||||
| 			return false; | ||||
| 		}, | ||||
| 		isOpAccepted(): boolean { | ||||
| 			if (this.game.user1Id != this.$store.state.i.id && this.game.user1Accepted) return true; | ||||
| 			if (this.game.user2Id != this.$store.state.i.id && this.game.user2Accepted) return true; | ||||
| 			if (this.game.user1Id != this.$i.id && this.game.user1Accepted) return true; | ||||
| 			if (this.game.user2Id != this.$i.id && this.game.user2Accepted) return true; | ||||
| 			return false; | ||||
| 		} | ||||
| 	}, | ||||
|  | @ -186,8 +186,8 @@ export default defineComponent({ | |||
| 		this.connection.on('initForm', this.onInitForm); | ||||
| 		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.user2Id != this.$store.state.i.id && this.game.form2) this.form = this.game.form2; | ||||
| 		if (this.game.user1Id != this.$i.id && this.game.form1) this.form = this.game.form1; | ||||
| 		if (this.game.user2Id != this.$i.id && this.game.form2) this.form = this.game.form2; | ||||
| 	}, | ||||
| 
 | ||||
| 	beforeUnmount() { | ||||
|  | @ -233,12 +233,12 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		onInitForm(x) { | ||||
| 			if (x.userId == this.$store.state.i.id) return; | ||||
| 			if (x.userId == this.$i.id) return; | ||||
| 			this.form = x.form; | ||||
| 		}, | ||||
| 
 | ||||
| 		onMessage(x) { | ||||
| 			if (x.userId == this.$store.state.i.id) return; | ||||
| 			if (x.userId == this.$i.id) return; | ||||
| 			this.messages.unshift(x.message); | ||||
| 		}, | ||||
| 
 | ||||
|  |  | |||
|  | @ -91,7 +91,7 @@ export default defineComponent({ | |||
| 	}, | ||||
| 
 | ||||
| 	mounted() { | ||||
| 		if (this.$store.getters.isSignedIn) { | ||||
| 		if (this.$i) { | ||||
| 			this.connection = os.stream.useSharedConnection('gamesReversi'); | ||||
| 
 | ||||
| 			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 { selectFile } from '@/scripts/select-file'; | ||||
| import * as os from '@/os'; | ||||
| import { ColdDeviceStorage } from '@/store'; | ||||
| 
 | ||||
| let room: Room; | ||||
| 
 | ||||
|  | @ -107,7 +108,7 @@ export default defineComponent({ | |||
| 			...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', { | ||||
| 			userId: this.user.id | ||||
|  | @ -117,7 +118,7 @@ export default defineComponent({ | |||
| 		this.carpetColor = roomInfo.carpetColor; | ||||
| 
 | ||||
| 		room = new Room(this.user, this.isMyRoom, roomInfo, this.$el, { | ||||
| 			graphicsQuality: this.$store.state.device.roomGraphicsQuality, | ||||
| 			graphicsQuality: ColdDeviceStorage.get('roomGraphicsQuality'), | ||||
| 			onChangeSelect: obj => { | ||||
| 				this.objectSelected = obj != null; | ||||
| 				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"> | ||||
| 	<div class="_title"><Fa :icon="faLock"/> {{ $t('twoStepAuthentication') }}</div> | ||||
| 	<div class="_content"> | ||||
| 		<MkButton v-if="!data && !$store.state.i.twoFactorEnabled" @click="register">{{ $t('_2fa.registerDevice') }}</MkButton> | ||||
| 		<template v-if="$store.state.i.twoFactorEnabled"> | ||||
| 		<MkButton v-if="!data && !$i.twoFactorEnabled" @click="register">{{ $t('_2fa.registerDevice') }}</MkButton> | ||||
| 		<template v-if="$i.twoFactorEnabled"> | ||||
| 			<p>{{ $t('_2fa.alreadyRegistered') }}</p> | ||||
| 			<MkButton @click="unregister">{{ $t('unregister') }}</MkButton> | ||||
| 
 | ||||
|  | @ -13,14 +13,14 @@ | |||
| 				<h2 class="heading">{{ $t('securityKey') }}</h2> | ||||
| 				<p>{{ $t('_2fa.securityKeyInfo') }}</p> | ||||
| 				<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> | ||||
| 						<div class="last-used">{{ $t('lastUsed') }}<MkTime :time="key.lastUsed"/></div> | ||||
| 						<MkButton @click="unregisterKey(key)">{{ $t('unregister') }}</MkButton> | ||||
| 					</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> | ||||
| 				<MkButton v-if="!registration || registration.error" @click="addSecurityKey">{{ $t('_2fa.registerKey') }}</MkButton> | ||||
|  | @ -42,7 +42,7 @@ | |||
| 				</ol> | ||||
| 			</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;"> | ||||
| 				<li> | ||||
| 					<i18n-t keypath="_2fa.step1" tag="span"> | ||||
|  | @ -96,7 +96,7 @@ export default defineComponent({ | |||
| 			}, | ||||
| 			data: null, | ||||
| 			supportsCredentials: !!navigator.credentials, | ||||
| 			usePasswordLessLogin: this.$store.state.i.usePasswordLessLogin, | ||||
| 			usePasswordLessLogin: this.$i.usePasswordLessLogin, | ||||
| 			registration: null, | ||||
| 			keyName: '', | ||||
| 			token: null, | ||||
|  | @ -136,7 +136,7 @@ export default defineComponent({ | |||
| 					this.updatePasswordLessLogin(); | ||||
| 				}).then(() => { | ||||
| 					os.success(); | ||||
| 					this.$store.state.i.twoFactorEnabled = false; | ||||
| 					this.$i.twoFactorEnabled = false; | ||||
| 				}); | ||||
| 			}); | ||||
| 		}, | ||||
|  | @ -146,7 +146,7 @@ export default defineComponent({ | |||
| 				token: this.token | ||||
| 			}).then(() => { | ||||
| 				os.success(); | ||||
| 				this.$store.state.i.twoFactorEnabled = true; | ||||
| 				this.$i.twoFactorEnabled = true; | ||||
| 			}).catch(e => { | ||||
| 				os.dialog({ | ||||
| 					type: 'error', | ||||
|  | @ -213,9 +213,9 @@ export default defineComponent({ | |||
| 								name: 'Misskey' | ||||
| 							}, | ||||
| 							user: { | ||||
| 								id: byteify(this.$store.state.i.id, 'ascii'), | ||||
| 								name: this.$store.state.i.username, | ||||
| 								displayName: this.$store.state.i.name, | ||||
| 								id: byteify(this.$i.id, 'ascii'), | ||||
| 								name: this.$i.username, | ||||
| 								displayName: this.$i.name, | ||||
| 							}, | ||||
| 							pubKeyCredParams: [{ alg: -7, type: 'public-key' }], | ||||
| 							timeout: 60000, | ||||
|  |  | |||
|  | @ -2,13 +2,13 @@ | |||
| <FormBase> | ||||
| 	<FormKeyValueView> | ||||
| 		<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> | ||||
| 
 | ||||
| 	<FormGroup> | ||||
| 		<FormKeyValueView> | ||||
| 			<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> | ||||
| 	</FormGroup> | ||||
| 
 | ||||
|  | @ -104,27 +104,27 @@ | |||
| 		<template #label>{{ $t('other') }}</template> | ||||
| 		<FormKeyValueView> | ||||
| 			<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> | ||||
| 			<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> | ||||
| 			<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> | ||||
| 			<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> | ||||
| 			<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> | ||||
| 			<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> | ||||
| 	</FormGroup> | ||||
| </FormBase> | ||||
|  | @ -171,7 +171,7 @@ export default defineComponent({ | |||
| 		this.$emit('info', this.INFO); | ||||
| 
 | ||||
| 		os.api('users/stats', { | ||||
| 			userId: this.$store.state.i.id | ||||
| 			userId: this.$i.id | ||||
| 		}).then(stats => { | ||||
| 			this.stats = stats; | ||||
| 		}); | ||||
|  |  | |||
|  | @ -5,17 +5,17 @@ | |||
| 		<div class="_title"><Fa :icon="faColumns"/> </div> | ||||
| 		<div class="_content"> | ||||
| 			<div>{{ $t('defaultNavigationBehaviour') }}</div> | ||||
| 			<MkSwitch v-model:value="deckNavWindow">{{ $t('openInWindow') }}</MkSwitch> | ||||
| 			<MkSwitch v-model:value="navWindow">{{ $t('openInWindow') }}</MkSwitch> | ||||
| 		</div> | ||||
| 		<div class="_content"> | ||||
| 			<MkSwitch v-model:value="deckAlwaysShowMainColumn"> | ||||
| 			<MkSwitch v-model:value="alwaysShowMainColumn"> | ||||
| 				{{ $t('_deck.alwaysShowMainColumn') }} | ||||
| 			</MkSwitch> | ||||
| 		</div> | ||||
| 		<div class="_content"> | ||||
| 			<div>{{ $t('_deck.columnAlign') }}</div> | ||||
| 			<MkRadio v-model="deckColumnAlign" value="left">{{ $t('left') }}</MkRadio> | ||||
| 			<MkRadio v-model="deckColumnAlign" value="center">{{ $t('center') }}</MkRadio> | ||||
| 			<MkRadio v-model="columnAlign" value="left">{{ $t('left') }}</MkRadio> | ||||
| 			<MkRadio v-model="columnAlign" value="center">{{ $t('center') }}</MkRadio> | ||||
| 		</div> | ||||
| 	</section> | ||||
| 
 | ||||
|  | @ -38,6 +38,7 @@ import FormBase from '@/components/form/base.vue'; | |||
| import FormGroup from '@/components/form/group.vue'; | ||||
| import { clientDb, set } from '@/db'; | ||||
| import * as os from '@/os'; | ||||
| import { deckStore } from '@/ui/deck/deck-store'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
|  | @ -67,20 +68,9 @@ export default defineComponent({ | |||
| 	}, | ||||
| 
 | ||||
| 	computed: { | ||||
| 		deckNavWindow: { | ||||
| 			get() { return this.$store.state.device.deckNavWindow; }, | ||||
| 			set(value) { this.$store.commit('device/set', { key: 'deckNavWindow', value }); } | ||||
| 		}, | ||||
| 
 | ||||
| 		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 }); } | ||||
| 		}, | ||||
| 		navWindow: deckStore.makeGetterSetter('navWindow'), | ||||
| 		alwaysShowMainColumn: deckStore.makeGetterSetter('alwaysShowMainColumn'), | ||||
| 		columnAlign: deckStore.makeGetterSetter('columnAlign'), | ||||
| 	}, | ||||
| 
 | ||||
| 	mounted() { | ||||
|  |  | |||
|  | @ -28,9 +28,9 @@ export default defineComponent({ | |||
| 	}, | ||||
| 
 | ||||
| 	async created() { | ||||
| 		if (this.$store.state.settings.uploadFolder) { | ||||
| 		if (this.$store.state.uploadFolder) { | ||||
| 			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: { | ||||
| 		chooseUploadFolder() { | ||||
| 			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(); | ||||
| 				if (this.$store.state.settings.uploadFolder) { | ||||
| 				if (this.$store.state.uploadFolder) { | ||||
| 					this.uploadFolder = await os.api('drive/folders/show', { | ||||
| 						folderId: this.$store.state.settings.uploadFolder | ||||
| 						folderId: this.$store.state.uploadFolder | ||||
| 					}); | ||||
| 				} else { | ||||
| 					this.uploadFolder = null; | ||||
|  |  | |||
|  | @ -3,8 +3,8 @@ | |||
| 	<FormGroup> | ||||
| 		<FormInput v-model:value="emailAddress" type="email"> | ||||
| 			{{ $t('emailAddress') }} | ||||
| 			<template #desc v-if="$store.state.i.email && !$store.state.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-if="$i.email && !$i.emailVerified">{{ $t('verificationEmailSent') }}</template> | ||||
| 			<template #desc v-else-if="emailAddress === $i.email && $i.emailVerified">{{ $t('emailVerified') }}</template> | ||||
| 		</FormInput> | ||||
| 	</FormGroup> | ||||
| 	<FormButton @click="save" primary>{{ $t('save') }}</FormButton> | ||||
|  | @ -44,7 +44,7 @@ export default defineComponent({ | |||
| 	}, | ||||
| 
 | ||||
| 	created() { | ||||
| 		this.emailAddress = this.$store.state.i.email; | ||||
| 		this.emailAddress = this.$i.email; | ||||
| 	}, | ||||
| 
 | ||||
| 	mounted() { | ||||
|  |  | |||
|  | @ -3,9 +3,9 @@ | |||
| 	<FormGroup> | ||||
| 		<template #label>{{ $t('emailAddress') }}</template> | ||||
| 		<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-else-if="$store.state.i.email && $store.state.i.emailVerified" #icon><Fa :icon="faCheck" style="color: var(--success);"/></template> | ||||
| 			{{ $store.state.i.email || $t('notSet') }} | ||||
| 			<template v-if="$i.email && !$i.emailVerified" #icon><Fa :icon="faExclamationTriangle" style="color: var(--warn);"/></template> | ||||
| 			<template v-else-if="$i.email && $i.emailVerified" #icon><Fa :icon="faCheck" style="color: var(--success);"/></template> | ||||
| 			{{ $i.email || $t('notSet') }} | ||||
| 		</FormLink> | ||||
| 	</FormGroup> | ||||
| </FormBase> | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ | |||
| 		<FormSwitch v-model:value="disableShowingAnimatedImages">{{ $t('disableShowingAnimatedImages') }}</FormSwitch> | ||||
| 		<FormSwitch v-model:value="useSystemFont">{{ $t('useSystemFont') }}</FormSwitch> | ||||
| 		<FormSwitch v-model:value="useOsNativeEmojis">{{ $t('useOsNativeEmojis') }} | ||||
| 			<div><Mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div> | ||||
| 			<div><Mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪" :key="useOsNativeEmojis"/></div> | ||||
| 		</FormSwitch> | ||||
| 	</FormGroup> | ||||
| 
 | ||||
|  | @ -96,6 +96,8 @@ import MkLink from '@/components/link.vue'; | |||
| import { langs } from '@/config'; | ||||
| import { clientDb, set } from '@/db'; | ||||
| import * as os from '@/os'; | ||||
| import { defaultStore } from '@/store'; | ||||
| import { ColdDeviceStorage } from '@/store'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
|  | @ -126,85 +128,22 @@ export default defineComponent({ | |||
| 	}, | ||||
| 
 | ||||
| 	computed: { | ||||
| 		serverDisconnectedBehavior: { | ||||
| 			get() { return this.$store.state.device.serverDisconnectedBehavior; }, | ||||
| 			set(value) { this.$store.commit('device/set', { key: 'serverDisconnectedBehavior', value }); } | ||||
| 		}, | ||||
| 
 | ||||
| 		reduceAnimation: { | ||||
| 			get() { return !this.$store.state.device.animation; }, | ||||
| 			set(value) { this.$store.commit('device/set', { key: 'animation', value: !value }); } | ||||
| 		}, | ||||
| 
 | ||||
| 		useBlurEffectForModal: { | ||||
| 			get() { return this.$store.state.device.useBlurEffectForModal; }, | ||||
| 			set(value) { this.$store.commit('device/set', { key: 'useBlurEffectForModal', value: value }); } | ||||
| 		}, | ||||
| 
 | ||||
| 		showGapBetweenNotesInTimeline: { | ||||
| 			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 }); } | ||||
| 		}, | ||||
| 		serverDisconnectedBehavior: defaultStore.makeGetterSetter('serverDisconnectedBehavior'), | ||||
| 		reduceAnimation: defaultStore.makeGetterSetter('animation', v => !v, v => !v), | ||||
| 		useBlurEffectForModal: defaultStore.makeGetterSetter('useBlurEffectForModal'), | ||||
| 		showGapBetweenNotesInTimeline: defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline'), | ||||
| 		disableAnimatedMfm: defaultStore.makeGetterSetter('animatedMfm', v => !v, v => !v), | ||||
| 		useOsNativeEmojis: defaultStore.makeGetterSetter('useOsNativeEmojis'), | ||||
| 		disableShowingAnimatedImages: defaultStore.makeGetterSetter('disableShowingAnimatedImages'), | ||||
| 		loadRawImages: defaultStore.makeGetterSetter('loadRawImages'), | ||||
| 		imageNewTab: defaultStore.makeGetterSetter('imageNewTab'), | ||||
| 		nsfw: defaultStore.makeGetterSetter('nsfw'), | ||||
| 		disablePagesScript: defaultStore.makeGetterSetter('disablePagesScript'), | ||||
| 		showFixedPostForm: defaultStore.makeGetterSetter('showFixedPostForm'), | ||||
| 		defaultSideView: defaultStore.makeGetterSetter('defaultSideView'), | ||||
| 		chatOpenBehavior: ColdDeviceStorage.makeGetterSetter('chatOpenBehavior'), | ||||
| 		instanceTicker: defaultStore.makeGetterSetter('instanceTicker'), | ||||
| 		enableInfiniteScroll: defaultStore.makeGetterSetter('enableInfiniteScroll'), | ||||
| 	}, | ||||
| 
 | ||||
| 	watch: { | ||||
|  |  | |||
|  | @ -41,13 +41,13 @@ | |||
| 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 { faLaugh, faBell, faEnvelope } from '@fortawesome/free-regular-svg-icons'; | ||||
| import { store } from '@/store'; | ||||
| import { i18n } from '@/i18n'; | ||||
| import FormLink from '@/components/form/link.vue'; | ||||
| import FormGroup from '@/components/form/group.vue'; | ||||
| import FormBase from '@/components/form/base.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({ | ||||
| 	components: { | ||||
|  | @ -101,7 +101,6 @@ export default defineComponent({ | |||
| 				case 'plugins': return defineAsyncComponent(() => import('./plugins.vue')); | ||||
| 				case 'import-export': return defineAsyncComponent(() => import('./import-export.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')); | ||||
| 				default: return null; | ||||
| 			} | ||||
|  | @ -125,8 +124,7 @@ export default defineComponent({ | |||
| 			onInfo, | ||||
| 			component, | ||||
| 			logout: () => { | ||||
| 				store.dispatch('logout'); | ||||
| 				location.href = '/'; | ||||
| 				signout(); | ||||
| 			}, | ||||
| 			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: { | ||||
| 		integrations() { | ||||
| 			return this.$store.state.i.integrations; | ||||
| 			return this.$i.integrations; | ||||
| 		}, | ||||
| 		 | ||||
| 		meta() { | ||||
| 			return this.$store.state.instance.meta; | ||||
| 			return this.$instance; | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -74,7 +74,7 @@ export default defineComponent({ | |||
| 	mounted() { | ||||
| 		this.$emit('info', this.INFO); | ||||
| 
 | ||||
| 		document.cookie = `igi=${this.$store.state.i.token}; path=/;` + | ||||
| 		document.cookie = `igi=${this.$i.token}; path=/;` + | ||||
| 			` max-age=31536000;` + | ||||
| 			(document.location.protocol.startsWith('https') ? ' secure' : ''); | ||||
| 
 | ||||
|  |  | |||
|  | @ -58,7 +58,7 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		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'), { | ||||
| 				includingTypes, | ||||
| 				showGlobalToggle: false, | ||||
|  | @ -68,7 +68,7 @@ export default defineComponent({ | |||
| 					await os.apiWithDialog('i/update', { | ||||
| 						mutingNotificationTypes: notificationTypes.filter(x => !value.includes(x)), | ||||
| 					}).then(i => { | ||||
| 						this.$store.state.i.mutingNotificationTypes = i.mutingNotificationTypes; | ||||
| 						this.$i.mutingNotificationTypes = i.mutingNotificationTypes; | ||||
| 					}); | ||||
| 				} | ||||
| 			}, 'closed'); | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| <template> | ||||
| <FormBase> | ||||
| 	<FormSwitch :value="$store.state.i.injectFeaturedNote" @update:value="onChangeInjectFeaturedNote"> | ||||
| 	<FormSwitch :value="$i.injectFeaturedNote" @update:value="onChangeInjectFeaturedNote"> | ||||
| 		{{ $t('showFeaturedNotesInTimeline') }} | ||||
| 	</FormSwitch> | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ | |||
| 		<details> | ||||
| 			<summary><Fa :icon="faFolderOpen"/> {{ $t('manage') }}</summary> | ||||
| 			<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> | ||||
| 			<template v-if="selectedPlugin"> | ||||
| 				<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 MkSwitch from '@/components/ui/switch.vue'; | ||||
| import * as os from '@/os'; | ||||
| import { ColdDeviceStorage } from '@/store'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
|  | @ -68,6 +69,7 @@ export default defineComponent({ | |||
| 	data() { | ||||
| 		return { | ||||
| 			script: '', | ||||
| 			plugins: ColdDeviceStorage.get('plugins'), | ||||
| 			selectedPluginId: null, | ||||
| 			faPlug, faSave, faTrashAlt, faFolderOpen, faDownload, faCog | ||||
| 		} | ||||
|  | @ -76,11 +78,22 @@ export default defineComponent({ | |||
| 	computed: { | ||||
| 		selectedPlugin() { | ||||
| 			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: { | ||||
| 		installPlugin({ id, meta, ast, token }) { | ||||
| 			ColdDeviceStorage.set('plugins', this.plugins.concat({ | ||||
| 				...meta, | ||||
| 				id, | ||||
| 				active: true, | ||||
| 				configData: {}, | ||||
| 				token: token, | ||||
| 				ast: ast | ||||
| 			})); | ||||
| 		}, | ||||
| 
 | ||||
| 		async install() { | ||||
| 			let ast; | ||||
| 			try { | ||||
|  | @ -137,7 +150,7 @@ export default defineComponent({ | |||
| 				}, 'closed'); | ||||
| 			}); | ||||
| 
 | ||||
| 			this.$store.commit('deviceUser/installPlugin', { | ||||
| 			this.installPlugin({ | ||||
| 				id: uuid(), | ||||
| 				meta: { | ||||
| 					name, version, author, description, permissions, config | ||||
|  | @ -154,7 +167,7 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		uninstall() { | ||||
| 			this.$store.commit('deviceUser/uninstallPlugin', this.selectedPluginId); | ||||
| 			ColdDeviceStorage.set('plugins', this.plugins.filter(x => x.id !== this.selectedPluginId)); | ||||
| 			os.success(); | ||||
| 			this.$nextTick(() => { | ||||
| 				location.reload(); | ||||
|  | @ -171,10 +184,9 @@ export default defineComponent({ | |||
| 			const { canceled, result } = await os.form(this.selectedPlugin.name, config); | ||||
| 			if (canceled) return; | ||||
| 
 | ||||
| 			this.$store.commit('deviceUser/configPlugin', { | ||||
| 				id: this.selectedPluginId, | ||||
| 				config: result | ||||
| 			}); | ||||
| 			const plugins = ColdDeviceStorage.get('plugins'); | ||||
| 			plugins.find(p => p.id === this.selectedPluginId).configData = result; | ||||
| 			ColdDeviceStorage.set('plugins', plugins); | ||||
| 
 | ||||
| 			this.$nextTick(() => { | ||||
| 				location.reload(); | ||||
|  | @ -182,10 +194,9 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		changeActive(plugin, active) { | ||||
| 			this.$store.commit('deviceUser/changePluginActive', { | ||||
| 				id: plugin.id, | ||||
| 				active: active | ||||
| 			}); | ||||
| 			const plugins = ColdDeviceStorage.get('plugins'); | ||||
| 			plugins.find(p => p.id === plugin.id).active = active; | ||||
| 			ColdDeviceStorage.set('plugins', plugins); | ||||
| 
 | ||||
| 			this.$nextTick(() => { | ||||
| 				location.reload(); | ||||
|  |  | |||
|  | @ -35,6 +35,7 @@ import FormSelect from '@/components/form/select.vue'; | |||
| import FormBase from '@/components/form/base.vue'; | ||||
| import FormGroup from '@/components/form/group.vue'; | ||||
| import * as os from '@/os'; | ||||
| import { defaultStore } from '@/store'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
|  | @ -60,27 +61,16 @@ export default defineComponent({ | |||
| 	}, | ||||
| 
 | ||||
| 	computed: { | ||||
| 		defaultNoteVisibility: { | ||||
| 			get() { return this.$store.state.settings.defaultNoteVisibility; }, | ||||
| 			set(value) { this.$store.dispatch('settings/set', { key: 'defaultNoteVisibility', value }); } | ||||
| 		}, | ||||
| 
 | ||||
| 		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 }); } | ||||
| 		}, | ||||
| 		defaultNoteVisibility: defaultStore.makeGetterSetter('defaultNoteVisibility'), | ||||
| 		defaultNoteLocalOnly: defaultStore.makeGetterSetter('defaultNoteLocalOnly'), | ||||
| 		rememberNoteVisibility: defaultStore.makeGetterSetter('rememberNoteVisibility'), | ||||
| 	}, | ||||
| 
 | ||||
| 	created() { | ||||
| 		this.isLocked = this.$store.state.i.isLocked; | ||||
| 		this.autoAcceptFollowed = this.$store.state.i.autoAcceptFollowed; | ||||
| 		this.noCrawle = this.$store.state.i.noCrawle; | ||||
| 		this.isExplorable = this.$store.state.i.isExplorable; | ||||
| 		this.isLocked = this.$i.isLocked; | ||||
| 		this.autoAcceptFollowed = this.$i.autoAcceptFollowed; | ||||
| 		this.noCrawle = this.$i.noCrawle; | ||||
| 		this.isExplorable = this.$i.isExplorable; | ||||
| 	}, | ||||
| 
 | ||||
| 	mounted() { | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| <template> | ||||
| <FormBase> | ||||
| 	<FormGroup> | ||||
| 		<div class="_formItem _formPanel llvierxe" :style="{ backgroundImage: $store.state.i.bannerUrl ? `url(${ $store.state.i.bannerUrl })` : null }"> | ||||
| 			<MkAvatar class="avatar" :user="$store.state.i"/> | ||||
| 		<div class="_formItem _formPanel llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }"> | ||||
| 			<MkAvatar class="avatar" :user="$i"/> | ||||
| 		</div> | ||||
| 		<FormButton @click="changeAvatar" primary>{{ $t('_profile.changeAvatar') }}</FormButton> | ||||
| 		<FormButton @click="changeBanner" primary>{{ $t('_profile.changeBanner') }}</FormButton> | ||||
|  | @ -100,24 +100,24 @@ export default defineComponent({ | |||
| 	}, | ||||
| 
 | ||||
| 	created() { | ||||
| 		this.name = this.$store.state.i.name; | ||||
| 		this.description = this.$store.state.i.description; | ||||
| 		this.location = this.$store.state.i.location; | ||||
| 		this.birthday = this.$store.state.i.birthday; | ||||
| 		this.avatarId = this.$store.state.i.avatarId; | ||||
| 		this.bannerId = this.$store.state.i.bannerId; | ||||
| 		this.isBot = this.$store.state.i.isBot; | ||||
| 		this.isCat = this.$store.state.i.isCat; | ||||
| 		this.alwaysMarkNsfw = this.$store.state.i.alwaysMarkNsfw; | ||||
| 		this.name = this.$i.name; | ||||
| 		this.description = this.$i.description; | ||||
| 		this.location = this.$i.location; | ||||
| 		this.birthday = this.$i.birthday; | ||||
| 		this.avatarId = this.$i.avatarId; | ||||
| 		this.bannerId = this.$i.bannerId; | ||||
| 		this.isBot = this.$i.isBot; | ||||
| 		this.isCat = this.$i.isCat; | ||||
| 		this.alwaysMarkNsfw = this.$i.alwaysMarkNsfw; | ||||
| 
 | ||||
| 		this.fieldName0 = this.$store.state.i.fields[0] ? this.$store.state.i.fields[0].name : null; | ||||
| 		this.fieldValue0 = this.$store.state.i.fields[0] ? this.$store.state.i.fields[0].value : null; | ||||
| 		this.fieldName1 = this.$store.state.i.fields[1] ? this.$store.state.i.fields[1].name : null; | ||||
| 		this.fieldValue1 = this.$store.state.i.fields[1] ? this.$store.state.i.fields[1].value : null; | ||||
| 		this.fieldName2 = this.$store.state.i.fields[2] ? this.$store.state.i.fields[2].name : null; | ||||
| 		this.fieldValue2 = this.$store.state.i.fields[2] ? this.$store.state.i.fields[2].value : null; | ||||
| 		this.fieldName3 = this.$store.state.i.fields[3] ? this.$store.state.i.fields[3].name : null; | ||||
| 		this.fieldValue3 = this.$store.state.i.fields[3] ? this.$store.state.i.fields[3].value : null; | ||||
| 		this.fieldName0 = this.$i.fields[0] ? this.$i.fields[0].name : null; | ||||
| 		this.fieldValue0 = this.$i.fields[0] ? this.$i.fields[0].value : null; | ||||
| 		this.fieldName1 = this.$i.fields[1] ? this.$i.fields[1].name : null; | ||||
| 		this.fieldValue1 = this.$i.fields[1] ? this.$i.fields[1].value : null; | ||||
| 		this.fieldName2 = this.$i.fields[2] ? this.$i.fields[2].name : null; | ||||
| 		this.fieldValue2 = this.$i.fields[2] ? this.$i.fields[2].value : null; | ||||
| 		this.fieldName3 = this.$i.fields[3] ? this.$i.fields[3].name : null; | ||||
| 		this.fieldValue3 = this.$i.fields[3] ? this.$i.fields[3].value : null; | ||||
| 	}, | ||||
| 
 | ||||
| 	mounted() { | ||||
|  | @ -227,10 +227,10 @@ export default defineComponent({ | |||
| 				alwaysMarkNsfw: !!this.alwaysMarkNsfw, | ||||
| 			}).then(i => { | ||||
| 				this.saving = false; | ||||
| 				this.$store.state.i.avatarId = i.avatarId; | ||||
| 				this.$store.state.i.avatarUrl = i.avatarUrl; | ||||
| 				this.$store.state.i.bannerId = i.bannerId; | ||||
| 				this.$store.state.i.bannerUrl = i.bannerUrl; | ||||
| 				this.$i.avatarId = i.avatarId; | ||||
| 				this.$i.avatarUrl = i.avatarUrl; | ||||
| 				this.$i.bannerId = i.bannerId; | ||||
| 				this.$i.bannerUrl = i.bannerUrl; | ||||
| 
 | ||||
| 				if (notify) { | ||||
| 					os.success(); | ||||
|  |  | |||
|  | @ -43,8 +43,8 @@ import FormInput from '@/components/form/input.vue'; | |||
| import FormRadios from '@/components/form/radios.vue'; | ||||
| import FormBase from '@/components/form/base.vue'; | ||||
| import FormButton from '@/components/form/button.vue'; | ||||
| import { defaultSettings } from '@/store'; | ||||
| import * as os from '@/os'; | ||||
| import { defaultStore } from '@/store'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
|  | @ -67,24 +67,14 @@ export default defineComponent({ | |||
| 					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 | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	computed: { | ||||
| 		useFullReactionPicker: { | ||||
| 			get() { return this.$store.state.device.useFullReactionPicker; }, | ||||
| 			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 }); } | ||||
| 		}, | ||||
| 		reactionPickerWidth: defaultStore.makeGetterSetter('reactionPickerWidth'), | ||||
| 		reactionPickerHeight: defaultStore.makeGetterSetter('reactionPickerHeight'), | ||||
| 	}, | ||||
| 
 | ||||
| 	watch: { | ||||
|  | @ -102,7 +92,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 	methods: { | ||||
| 		save() { | ||||
| 			this.$store.dispatch('settings/set', { key: 'reactions', value: this.reactions }); | ||||
| 			this.$store.set('reactions', this.reactions); | ||||
| 		}, | ||||
| 
 | ||||
| 		remove(reaction, ev) { | ||||
|  | @ -129,7 +119,7 @@ export default defineComponent({ | |||
| 			}); | ||||
| 			if (canceled) return; | ||||
| 
 | ||||
| 			this.reactions = JSON.parse(JSON.stringify(defaultSettings.reactions)); | ||||
| 			this.reactions = JSON.parse(JSON.stringify(this.$store.def.reactions.default)); | ||||
| 		}, | ||||
| 
 | ||||
| 		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