Compare commits
	
		
			28 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b6dc6c6984 | ||
| 
						 | 
					517084b1fc | ||
| 
						 | 
					27763e6898 | ||
| 
						 | 
					57dde1da38 | ||
| 
						 | 
					0bb961767c | ||
| 
						 | 
					88515ce677 | ||
| 
						 | 
					00562e840c | ||
| 
						 | 
					b7927ba386 | ||
| 
						 | 
					9c363ff045 | ||
| 
						 | 
					1dbce5e3e2 | ||
| 
						 | 
					361a9ca1be | ||
| 
						 | 
					cde6514839 | ||
| 
						 | 
					507e2f727e | ||
| 
						 | 
					8028499d2b | ||
| 
						 | 
					c2c79c4a87 | ||
| 
						 | 
					d56f7f3390 | ||
| 
						 | 
					ef70d17194 | ||
| 
						 | 
					9789b9a083 | ||
| 
						 | 
					e6311fdb13 | ||
| 
						 | 
					2231c54dee | ||
| 
						 | 
					20de9a5e35 | ||
| 
						 | 
					ec3a6d7097 | ||
| 
						 | 
					9d99bf5af8 | ||
| 
						 | 
					52911cc9fd | ||
| 
						 | 
					6f71ba376d | ||
| 
						 | 
					9f439aabba | ||
| 
						 | 
					33ad60b1f3 | ||
| 
						 | 
					010d3f8281 | 
							
								
								
									
										13
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,6 +1,19 @@
 | 
			
		||||
ChangeLog
 | 
			
		||||
=========
 | 
			
		||||
 | 
			
		||||
10.90.4
 | 
			
		||||
----------
 | 
			
		||||
* url-previewでembedプレイヤー展開をオプトインにするように
 | 
			
		||||
* デザインの調整
 | 
			
		||||
* ユーザビリティの強化
 | 
			
		||||
 | 
			
		||||
10.90.3
 | 
			
		||||
----------
 | 
			
		||||
* モバイルのデッキで投稿フォームウィジェットが設置できなかった問題を修正
 | 
			
		||||
* ドキュメントの強化
 | 
			
		||||
* デザインの調整
 | 
			
		||||
* ユーザビリティの強化
 | 
			
		||||
 | 
			
		||||
10.90.2
 | 
			
		||||
----------
 | 
			
		||||
* アカウントが削除できない問題を修正
 | 
			
		||||
 
 | 
			
		||||
@@ -40,10 +40,10 @@ Stands for _**M**iss**k**ey_.
 | 
			
		||||
Stands for _**S**ervice**W**orker_.
 | 
			
		||||
 | 
			
		||||
### Nyaize
 | 
			
		||||
な を にゃ にすること
 | 
			
		||||
Convert な(na) to にゃ(nya)
 | 
			
		||||
 | 
			
		||||
#### Denyaize
 | 
			
		||||
Nyaizeを解除すること
 | 
			
		||||
Revert Nyaize
 | 
			
		||||
 | 
			
		||||
## Code style
 | 
			
		||||
### Don't use `export default`
 | 
			
		||||
@@ -59,16 +59,16 @@ export function something(foo: string): string {
 | 
			
		||||
 | 
			
		||||
## Directory structure
 | 
			
		||||
```
 | 
			
		||||
src ... ソースコード
 | 
			
		||||
	@types ... 外部ライブラリなどの型定義
 | 
			
		||||
	prelude ... Misskeyに関係ないかつ副作用なし
 | 
			
		||||
	misc ... 副作用なしのユーティリティ処理
 | 
			
		||||
	service ... 副作用ありの共通処理
 | 
			
		||||
	queue ... ジョブキューとジョブ
 | 
			
		||||
	server ... Webサーバー
 | 
			
		||||
	client ... クライアント
 | 
			
		||||
src ... Source code
 | 
			
		||||
	@types ... Type definitions
 | 
			
		||||
	prelude ... Independence utils for coding JavaScript without side effects
 | 
			
		||||
	misc ... Independence utils for Misskey without side effects
 | 
			
		||||
	service ... Common functions with side effects
 | 
			
		||||
	queue ... Job queues and Jobs
 | 
			
		||||
	server ... Web Server
 | 
			
		||||
	client ... Client
 | 
			
		||||
	mfm ... MFM
 | 
			
		||||
 | 
			
		||||
test ... テスト
 | 
			
		||||
test ... Test code
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,7 @@ common:
 | 
			
		||||
  signin: "ログイン"
 | 
			
		||||
  signup: "新規登録"
 | 
			
		||||
  signout: "ログアウト"
 | 
			
		||||
  reload-to-apply-the-setting: "この設定を反映するにはページをリロードする必要があります。今すぐリロードしますか?"
 | 
			
		||||
 | 
			
		||||
  got-it: "わかった"
 | 
			
		||||
  customization-tips:
 | 
			
		||||
@@ -257,6 +258,9 @@ common/views/pages/explore.vue:
 | 
			
		||||
  explore: "{host}を探索"
 | 
			
		||||
  users-info: "現在{users}ユーザーが登録されています"
 | 
			
		||||
 | 
			
		||||
common/views/components/url-preview.vue:
 | 
			
		||||
  enable-player: "プレイヤーを開く"
 | 
			
		||||
 | 
			
		||||
common/views/components/user-list.vue:
 | 
			
		||||
  no-users: "ユーザーがいません"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
	"name": "misskey",
 | 
			
		||||
	"author": "syuilo <i@syuilo.com>",
 | 
			
		||||
	"version": "10.90.2",
 | 
			
		||||
	"version": "10.90.4",
 | 
			
		||||
	"codename": "nighthike",
 | 
			
		||||
	"repository": {
 | 
			
		||||
		"type": "git",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
export default function(me, settings, note) {
 | 
			
		||||
	const isMyNote = note.userId == me.id;
 | 
			
		||||
	const isMyNote = me && (note.userId == me.id);
 | 
			
		||||
	const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null;
 | 
			
		||||
 | 
			
		||||
	const includesMutedWords = (text: string) =>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<span class="mk-acct">
 | 
			
		||||
<span class="mk-acct" v-once>
 | 
			
		||||
	<span class="name">@{{ user.username }}</span>
 | 
			
		||||
	<span class="host" :class="{ fade: $store.state.settings.contrastedAcct }" v-if="user.host || detail || $store.state.settings.showFullAcct">@{{ user.host || host }}</span>
 | 
			
		||||
	<fa v-if="user.isLocked == true" class="locked" icon="lock" fixed-width/>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,14 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<span class="mk-avatar" :style="style" :class="{ cat }" :title="user | acct" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick">
 | 
			
		||||
	<span class="mk-avatar" :style="style" :class="{ cat }" :title="user | acct" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick" v-once>
 | 
			
		||||
		<span class="inner" :style="icon"></span>
 | 
			
		||||
	</span>
 | 
			
		||||
	<span class="mk-avatar" :style="style" :class="{ cat }" :title="user | acct" v-else-if="disableLink && disablePreview" @click="onClick">
 | 
			
		||||
	<span class="mk-avatar" :style="style" :class="{ cat }" :title="user | acct" v-else-if="disableLink && disablePreview" @click="onClick" v-once>
 | 
			
		||||
		<span class="inner" :style="icon"></span>
 | 
			
		||||
	</span>
 | 
			
		||||
	<router-link class="mk-avatar" :style="style" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id">
 | 
			
		||||
	<router-link class="mk-avatar" :style="style" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id" v-once>
 | 
			
		||||
		<span class="inner" :style="icon"></span>
 | 
			
		||||
	</router-link>
 | 
			
		||||
	<router-link class="mk-avatar" :style="style" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && disablePreview">
 | 
			
		||||
	<router-link class="mk-avatar" :style="style" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && disablePreview" v-once>
 | 
			
		||||
		<span class="inner" :style="icon"></span>
 | 
			
		||||
	</router-link>
 | 
			
		||||
</template>
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
	> div
 | 
			
		||||
		background-color var(--text)
 | 
			
		||||
		border-radius var(--round)
 | 
			
		||||
		border-radius 6px
 | 
			
		||||
		color var(--secondary)
 | 
			
		||||
		display inline-block
 | 
			
		||||
		font-size 14px
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mfm-core v-bind="$attrs" class="havbbuyv"/>
 | 
			
		||||
<mfm-core v-bind="$attrs" class="havbbuyv" v-once/>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="ui-card" :class="{ shadow: $store.state.device.useShadow }">
 | 
			
		||||
<div class="ui-card" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
 | 
			
		||||
	<header>
 | 
			
		||||
		<slot name="title"></slot>
 | 
			
		||||
	</header>
 | 
			
		||||
@@ -25,7 +25,9 @@ export default Vue.extend({
 | 
			
		||||
	max-width 850px
 | 
			
		||||
	color var(--faceText)
 | 
			
		||||
	background var(--face)
 | 
			
		||||
	border-radius var(--round)
 | 
			
		||||
 | 
			
		||||
	&.round
 | 
			
		||||
		border-radius 6px
 | 
			
		||||
 | 
			
		||||
	&.shadow
 | 
			
		||||
		box-shadow 0 3px 1px -2px rgba(#000, 0.2), 0 2px 2px 0 rgba(#000, 0.14), 0 1px 5px 0 rgba(#000, 0.12)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div v-if="player.url" class="player" :style="`padding: ${(player.height || 0) / (player.width || 1) * 100}% 0 0`">
 | 
			
		||||
	<iframe :src="player.url" :width="player.width || '100%'" :heigth="player.height || 250" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen />
 | 
			
		||||
<div v-if="playerEnabled" class="player" :style="`padding: ${(player.height || 0) / (player.width || 1) * 100}% 0 0`">
 | 
			
		||||
	<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="tweetUrl && detail" class="twitter">
 | 
			
		||||
	<blockquote ref="tweet" class="twitter-tweet" :data-theme="$store.state.device.darkmode ? 'dark' : null">
 | 
			
		||||
@@ -9,7 +9,9 @@
 | 
			
		||||
</div>
 | 
			
		||||
<div v-else class="mk-url-preview">
 | 
			
		||||
	<a :class="{ mini: narrow, compact }" :href="url" target="_blank" :title="url" v-if="!fetching">
 | 
			
		||||
		<div class="thumbnail" v-if="thumbnail" :style="`background-image: url('${thumbnail}')`"></div>
 | 
			
		||||
		<div class="thumbnail" v-if="thumbnail" :style="`background-image: url('${thumbnail}')`">
 | 
			
		||||
			<button v-if="!playerEnabled && player.url" @click.prevent="playerEnabled = true" :title="$t('enable-player')"><fa :icon="['far', 'play-circle']"/></button>
 | 
			
		||||
		</div>
 | 
			
		||||
		<article>
 | 
			
		||||
			<header>
 | 
			
		||||
				<h1 :title="title">{{ title }}</h1>
 | 
			
		||||
@@ -26,88 +28,11 @@
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import i18n from '../../../i18n';
 | 
			
		||||
import { url as misskeyUrl } from '../../../config';
 | 
			
		||||
 | 
			
		||||
// THIS IS THE WHITELIST FOR THE EMBED PLAYER
 | 
			
		||||
const whiteList = [
 | 
			
		||||
	'afreecatv.com',
 | 
			
		||||
	'aparat.com',
 | 
			
		||||
	'applemusic.com',
 | 
			
		||||
	'amazon.com',
 | 
			
		||||
	'awa.fm',
 | 
			
		||||
	'bandcamp.com',
 | 
			
		||||
	'bbc.co.uk',
 | 
			
		||||
	'beatport.com',
 | 
			
		||||
	'bilibili.com',
 | 
			
		||||
	'boomstream.com',
 | 
			
		||||
	'breakers.tv',
 | 
			
		||||
	'cam4.com',
 | 
			
		||||
	'cavelis.net',
 | 
			
		||||
	'chaturbate.com',
 | 
			
		||||
	'cnn.com',
 | 
			
		||||
	'cybergame.tv',
 | 
			
		||||
	'dailymotion.com',
 | 
			
		||||
	'deezer.com',
 | 
			
		||||
	'djlive.pl',
 | 
			
		||||
	'e-onkyo.com',
 | 
			
		||||
	'eventials.com',
 | 
			
		||||
	'facebook.com',
 | 
			
		||||
	'fc2.com',
 | 
			
		||||
	'gameplank.tv',
 | 
			
		||||
	'goodgame.ru',
 | 
			
		||||
	'google.com',
 | 
			
		||||
	'hardtunes.com',
 | 
			
		||||
	'instagram.com',
 | 
			
		||||
	'johnnylooch.com',
 | 
			
		||||
	'kexp.org',
 | 
			
		||||
	'lahzenegar.com',
 | 
			
		||||
	'liveedu.tv',
 | 
			
		||||
	'livetube.cc',
 | 
			
		||||
	'livestream.com',
 | 
			
		||||
	'meridix.com',
 | 
			
		||||
	'mixcloud.com',
 | 
			
		||||
	'mixer.com',
 | 
			
		||||
	'mobcrush.com',
 | 
			
		||||
	'mylive.in.th',
 | 
			
		||||
	'myspace.com',
 | 
			
		||||
	'netflix.com',
 | 
			
		||||
	'newretrowave.com',
 | 
			
		||||
	'nhk.or.jp',
 | 
			
		||||
	'nicovideo.jp',
 | 
			
		||||
	'nico.ms',
 | 
			
		||||
	'noisetrade.com',
 | 
			
		||||
	'nood.tv',
 | 
			
		||||
	'npr.org',
 | 
			
		||||
	'openrec.tv',
 | 
			
		||||
	'pandora.com',
 | 
			
		||||
	'pandora.tv',
 | 
			
		||||
	'picarto.tv',
 | 
			
		||||
	'pscp.tv',
 | 
			
		||||
	'restream.io',
 | 
			
		||||
	'reverbnation.com',
 | 
			
		||||
	'sermonaudio.com',
 | 
			
		||||
	'smashcast.tv',
 | 
			
		||||
	'songkick.com',
 | 
			
		||||
	'soundcloud.com',
 | 
			
		||||
	'spinninrecords.com',
 | 
			
		||||
	'spotify.com',
 | 
			
		||||
	'stitcher.com',
 | 
			
		||||
	'stream.me',
 | 
			
		||||
	'switchboard.live',
 | 
			
		||||
	'tunein.com',
 | 
			
		||||
	'twitcasting.tv',
 | 
			
		||||
	'twitch.tv',
 | 
			
		||||
	'twitter.com',
 | 
			
		||||
	'vaughnlive.tv',
 | 
			
		||||
	'veoh.com',
 | 
			
		||||
	'vimeo.com',
 | 
			
		||||
	'watchpeoplecode.com',
 | 
			
		||||
	'web.tv',
 | 
			
		||||
	'youtube.com',
 | 
			
		||||
	'youtu.be'
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	i18n: i18n('common/views/components/url-preview.vue'),
 | 
			
		||||
	props: {
 | 
			
		||||
		url: {
 | 
			
		||||
			type: String,
 | 
			
		||||
@@ -147,7 +72,8 @@ export default Vue.extend({
 | 
			
		||||
				height: null
 | 
			
		||||
			},
 | 
			
		||||
			tweetUrl: null,
 | 
			
		||||
			misskeyUrl
 | 
			
		||||
			playerEnabled: false,
 | 
			
		||||
			misskeyUrl,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@@ -188,9 +114,7 @@ export default Vue.extend({
 | 
			
		||||
				this.icon = info.icon;
 | 
			
		||||
				this.sitename = info.sitename;
 | 
			
		||||
				this.fetching = false;
 | 
			
		||||
				if (whiteList.some(x => x == url.hostname || url.hostname.endsWith(`.${x}`))) {
 | 
			
		||||
					this.player = info.player;
 | 
			
		||||
				}
 | 
			
		||||
				this.player = info.player;
 | 
			
		||||
			})
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
@@ -230,6 +154,17 @@ export default Vue.extend({
 | 
			
		||||
			height 100%
 | 
			
		||||
			background-position center
 | 
			
		||||
			background-size cover
 | 
			
		||||
			display flex
 | 
			
		||||
			justify-content center
 | 
			
		||||
			align-items center
 | 
			
		||||
 | 
			
		||||
			> button
 | 
			
		||||
				font-size 3.5em
 | 
			
		||||
				opacity: 0.7
 | 
			
		||||
 | 
			
		||||
				&:hover
 | 
			
		||||
					font-size 4em
 | 
			
		||||
					opacity 0.9
 | 
			
		||||
 | 
			
		||||
			& + article
 | 
			
		||||
				left 100px
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="dnpfarvgbnfmyzbdquhhzyxcmstpdqzs" :class="{ naked, narrow, active, isStacked, draghover, dragging, dropready }"
 | 
			
		||||
<div class="dnpfarvgbnfmyzbdquhhzyxcmstpdqzs" :class="{ naked, narrow, active, isStacked, draghover, dragging, dropready, isMobile: $root.isMobile, shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }"
 | 
			
		||||
		@dragover.prevent.stop="onDragover"
 | 
			
		||||
		@dragleave="onDragleave"
 | 
			
		||||
		@drop.prevent.stop="onDrop"
 | 
			
		||||
@@ -322,10 +322,14 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
	height 100%
 | 
			
		||||
	background var(--face)
 | 
			
		||||
	border-radius var(--round)
 | 
			
		||||
	box-shadow var(--shadow)
 | 
			
		||||
	overflow hidden
 | 
			
		||||
 | 
			
		||||
	&.round
 | 
			
		||||
		border-radius 6px
 | 
			
		||||
 | 
			
		||||
	&.shadow
 | 
			
		||||
		box-shadow 0 3px 8px rgba(0, 0, 0, 0.2)
 | 
			
		||||
 | 
			
		||||
	&.draghover
 | 
			
		||||
		box-shadow 0 0 0 2px var(--primaryAlpha08)
 | 
			
		||||
 | 
			
		||||
@@ -366,6 +370,10 @@ export default Vue.extend({
 | 
			
		||||
			> button
 | 
			
		||||
				color var(--text)
 | 
			
		||||
 | 
			
		||||
	&.isMobile
 | 
			
		||||
		> header
 | 
			
		||||
			box-shadow none
 | 
			
		||||
 | 
			
		||||
	> header
 | 
			
		||||
		display flex
 | 
			
		||||
		z-index 2
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ import wTips from './tips.vue';
 | 
			
		||||
import wNav from './nav.vue';
 | 
			
		||||
import wHashtags from './hashtags.vue';
 | 
			
		||||
import wInstance from './instance.vue';
 | 
			
		||||
import wPostForm from './post-form.vue';
 | 
			
		||||
 | 
			
		||||
Vue.component('mkw-analog-clock', wAnalogClock);
 | 
			
		||||
Vue.component('mkw-nav', wNav);
 | 
			
		||||
@@ -29,3 +30,4 @@ Vue.component('mkw-rss', wRss);
 | 
			
		||||
Vue.component('mkw-version', wVersion);
 | 
			
		||||
Vue.component('mkw-hashtags', wHashtags);
 | 
			
		||||
Vue.component('mkw-instance', wInstance);
 | 
			
		||||
Vue.component('mkw-post-form', wPostForm);
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
					ref="text"
 | 
			
		||||
					v-autocomplete="{ model: 'text' }"
 | 
			
		||||
				></textarea>
 | 
			
		||||
				<button class="emoji" @click="emoji" ref="emoji">
 | 
			
		||||
				<button class="emoji" @click="emoji" ref="emoji" v-if="!$root.isMobile">
 | 
			
		||||
					<fa :icon="['far', 'laugh']"/>
 | 
			
		||||
				</button>
 | 
			
		||||
			</div>
 | 
			
		||||
@@ -161,7 +161,7 @@ export default define({
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		async emoji() {
 | 
			
		||||
			const Picker = await import('../components/emoji-picker-dialog.vue').then(m => m.default);
 | 
			
		||||
			const Picker = await import('../../../desktop/views/components/emoji-picker-dialog.vue').then(m => m.default);
 | 
			
		||||
			const button = this.$refs.emoji;
 | 
			
		||||
			const rect = button.getBoundingClientRect();
 | 
			
		||||
			const vm = this.$root.new(Picker, {
 | 
			
		||||
@@ -186,6 +186,9 @@ export default define({
 | 
			
		||||
				alert('Something happened');
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				this.posting = false;
 | 
			
		||||
				this.$nextTick(() => {
 | 
			
		||||
					this.$refs.text.focus();
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
		<div class="mkw-rss--body" :data-mobile="platform == 'mobile'">
 | 
			
		||||
			<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
 | 
			
		||||
			<div class="feed" v-else>
 | 
			
		||||
				<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
 | 
			
		||||
				<a v-for="item in items" :href="item.link" target="_blank" :title="item.title">{{ item.title }}</a>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</ui-container>
 | 
			
		||||
@@ -55,12 +55,18 @@ export default define({
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		setting() {
 | 
			
		||||
			const url = window.prompt('URL', this.props.url);
 | 
			
		||||
			if (url && url != '') {
 | 
			
		||||
			this.$root.dialog({
 | 
			
		||||
				title: 'URL',
 | 
			
		||||
				input: {
 | 
			
		||||
					type: 'url',
 | 
			
		||||
					default: this.props.url
 | 
			
		||||
				}
 | 
			
		||||
			}).then(({ canceled, result: url }) => {
 | 
			
		||||
				if (canceled) return;
 | 
			
		||||
				this.props.url = url;
 | 
			
		||||
				this.save();
 | 
			
		||||
				this.fetch();
 | 
			
		||||
			}
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-calendar" :data-melt="design == 4 || design == 5">
 | 
			
		||||
<div class="mk-calendar" :data-melt="design == 4 || design == 5" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
 | 
			
		||||
	<template v-if="design == 0 || design == 1">
 | 
			
		||||
		<button @click="prev" :title="$t('prev')"><fa icon="chevron-circle-left"/></button>
 | 
			
		||||
		<p class="title">{{ $t('title', { year, month }) }}</p>
 | 
			
		||||
@@ -133,10 +133,14 @@ export default Vue.extend({
 | 
			
		||||
.mk-calendar
 | 
			
		||||
	color var(--calendarDay)
 | 
			
		||||
	background var(--face)
 | 
			
		||||
	box-shadow var(--shadow)
 | 
			
		||||
	border-radius var(--round)
 | 
			
		||||
	overflow hidden
 | 
			
		||||
 | 
			
		||||
	&.round
 | 
			
		||||
		border-radius 6px
 | 
			
		||||
 | 
			
		||||
	&.shadow
 | 
			
		||||
		box-shadow 0 3px 8px rgba(0, 0, 0, 0.2)
 | 
			
		||||
 | 
			
		||||
	&[data-melt]
 | 
			
		||||
		background transparent !important
 | 
			
		||||
		border none !important
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-note-detail" :title="title" tabindex="-1">
 | 
			
		||||
<div class="mk-note-detail" :title="title" tabindex="-1" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
 | 
			
		||||
	<button
 | 
			
		||||
		class="read-more"
 | 
			
		||||
		v-if="appearNote.reply && appearNote.reply.replyId && conversation.length == 0"
 | 
			
		||||
@@ -159,8 +159,15 @@ export default Vue.extend({
 | 
			
		||||
	overflow hidden
 | 
			
		||||
	text-align left
 | 
			
		||||
	background var(--face)
 | 
			
		||||
	box-shadow var(--shadow)
 | 
			
		||||
	border-radius var(--round)
 | 
			
		||||
 | 
			
		||||
	&.round
 | 
			
		||||
		border-radius 6px
 | 
			
		||||
 | 
			
		||||
		> .read-more
 | 
			
		||||
			border-radius 6px 6px 0 0
 | 
			
		||||
 | 
			
		||||
	&.shadow
 | 
			
		||||
		box-shadow 0 3px 8px rgba(0, 0, 0, 0.2)
 | 
			
		||||
 | 
			
		||||
	> .read-more
 | 
			
		||||
		display block
 | 
			
		||||
@@ -175,7 +182,6 @@ export default Vue.extend({
 | 
			
		||||
		outline none
 | 
			
		||||
		border none
 | 
			
		||||
		border-bottom solid 1px var(--faceDivider)
 | 
			
		||||
		border-radius var(--round) var(--round) 0 0
 | 
			
		||||
 | 
			
		||||
		&:hover
 | 
			
		||||
			box-shadow 0 0 0 100px inset rgba(0, 0, 0, 0.05)
 | 
			
		||||
 
 | 
			
		||||
@@ -39,30 +39,30 @@
 | 
			
		||||
					<mk-url-preview v-for="url in urls" :url="url" :key="url" :compact="compact"/>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
			<footer v-if="appearNote.deletedAt == null">
 | 
			
		||||
			<footer v-if="appearNote.deletedAt == null" class="footer">
 | 
			
		||||
				<span class="app" v-if="appearNote.app && narrow && $store.state.settings.showVia">via <b>{{ appearNote.app.name }}</b></span>
 | 
			
		||||
				<mk-reactions-viewer :note="appearNote" ref="reactionsViewer"/>
 | 
			
		||||
				<button class="replyButton" @click="reply()" :title="$t('reply')">
 | 
			
		||||
				<button class="replyButton button" @click="reply()" :title="$t('reply')">
 | 
			
		||||
					<template v-if="appearNote.reply"><fa icon="reply-all"/></template>
 | 
			
		||||
					<template v-else><fa icon="reply"/></template>
 | 
			
		||||
					<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
 | 
			
		||||
				</button>
 | 
			
		||||
				<button v-if="['public', 'home'].includes(appearNote.visibility)" class="renoteButton" @click="renote()" :title="$t('renote')">
 | 
			
		||||
				<button v-if="['public', 'home'].includes(appearNote.visibility)" class="renoteButton button" @click="renote()" :title="$t('renote')">
 | 
			
		||||
					<fa icon="retweet"/>
 | 
			
		||||
					<p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
 | 
			
		||||
				</button>
 | 
			
		||||
				<button v-else class="inhibitedButton">
 | 
			
		||||
				<button v-else class="inhibitedButton button">
 | 
			
		||||
					<fa icon="ban"/>
 | 
			
		||||
				</button>
 | 
			
		||||
				<button v-if="!isMyNote && appearNote.myReaction == null" class="reactionButton" @click="react()" ref="reactButton" :title="$t('add-reaction')">
 | 
			
		||||
				<button v-if="!isMyNote && appearNote.myReaction == null" class="reactionButton button" @click="react()" ref="reactButton" :title="$t('add-reaction')">
 | 
			
		||||
					<fa icon="plus"/>
 | 
			
		||||
					<p class="count" v-if="Object.values(appearNote.reactionCounts).some(x => x)">{{ Object.values(appearNote.reactionCounts).reduce((a, c) => a + c, 0) }}</p>
 | 
			
		||||
				</button>
 | 
			
		||||
				<button v-if="!isMyNote && appearNote.myReaction != null" class="reactionButton reacted" @click="undoReact(appearNote)" ref="reactButton" :title="$t('undo-reaction')">
 | 
			
		||||
				<button v-if="!isMyNote && appearNote.myReaction != null" class="reactionButton reacted button" @click="undoReact(appearNote)" ref="reactButton" :title="$t('undo-reaction')">
 | 
			
		||||
					<fa icon="minus"/>
 | 
			
		||||
					<p class="count" v-if="Object.values(appearNote.reactionCounts).some(x => x)">{{ Object.values(appearNote.reactionCounts).reduce((a, c) => a + c, 0) }}</p>
 | 
			
		||||
				</button>
 | 
			
		||||
				<button @click="menu()" ref="menuButton">
 | 
			
		||||
				<button @click="menu()" ref="menuButton" class="button">
 | 
			
		||||
					<fa icon="ellipsis-h"/>
 | 
			
		||||
				</button>
 | 
			
		||||
			</footer>
 | 
			
		||||
@@ -274,7 +274,7 @@ export default Vue.extend({
 | 
			
		||||
							border dashed var(--lineWidth) var(--quoteBorder)
 | 
			
		||||
							border-radius 8px
 | 
			
		||||
 | 
			
		||||
			> footer
 | 
			
		||||
			> .footer
 | 
			
		||||
				> .app
 | 
			
		||||
					display block
 | 
			
		||||
					margin-top 0.5em
 | 
			
		||||
@@ -282,7 +282,7 @@ export default Vue.extend({
 | 
			
		||||
					color var(--noteHeaderInfo)
 | 
			
		||||
					font-size 0.8em
 | 
			
		||||
 | 
			
		||||
				> button
 | 
			
		||||
				> .button
 | 
			
		||||
					margin 0 28px 0 0
 | 
			
		||||
					padding 0 8px
 | 
			
		||||
					line-height 32px
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-notes">
 | 
			
		||||
<div class="mk-notes" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
 | 
			
		||||
	<slot name="header"></slot>
 | 
			
		||||
 | 
			
		||||
	<div class="newer-indicator" :style="{ top: $store.state.uiHeaderHeight + 'px' }" v-show="queue.length > 0"></div>
 | 
			
		||||
@@ -191,10 +191,14 @@ export default Vue.extend({
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mk-notes
 | 
			
		||||
	background var(--face)
 | 
			
		||||
	box-shadow var(--shadow)
 | 
			
		||||
	border-radius var(--round)
 | 
			
		||||
	overflow hidden
 | 
			
		||||
 | 
			
		||||
	&.round
 | 
			
		||||
		border-radius 6px
 | 
			
		||||
 | 
			
		||||
	&.shadow
 | 
			
		||||
		box-shadow 0 3px 8px rgba(0, 0, 0, 0.2)
 | 
			
		||||
 | 
			
		||||
	.transition
 | 
			
		||||
		.mk-notes-enter
 | 
			
		||||
		.mk-notes-leave-to
 | 
			
		||||
 
 | 
			
		||||
@@ -463,17 +463,26 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
		circleIcons: {
 | 
			
		||||
			get() { return this.$store.state.settings.circleIcons; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'circleIcons', value }); }
 | 
			
		||||
			set(value) {
 | 
			
		||||
				this.$store.dispatch('settings/set', { key: 'circleIcons', value });
 | 
			
		||||
				this.reload();
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		contrastedAcct: {
 | 
			
		||||
			get() { return this.$store.state.settings.contrastedAcct; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'contrastedAcct', value }); }
 | 
			
		||||
			set(value) {
 | 
			
		||||
				this.$store.dispatch('settings/set', { key: 'contrastedAcct', value });
 | 
			
		||||
				this.reload();
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showFullAcct: {
 | 
			
		||||
			get() { return this.$store.state.settings.showFullAcct; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showFullAcct', value }); }
 | 
			
		||||
			set(value) {
 | 
			
		||||
				this.$store.dispatch('settings/set', { key: 'showFullAcct', value });
 | 
			
		||||
				this.reload();
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showVia: {
 | 
			
		||||
@@ -517,6 +526,17 @@ export default Vue.extend({
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		reload() {
 | 
			
		||||
			this.$root.dialog({
 | 
			
		||||
				type: 'warning',
 | 
			
		||||
				text: this.$t('@.reload-to-apply-the-setting'),
 | 
			
		||||
				showCancelButton: true
 | 
			
		||||
			}).then(({ canceled }) => {
 | 
			
		||||
				if (!canceled) {
 | 
			
		||||
					location.reload();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		customizeHome() {
 | 
			
		||||
			location.href = '/?customize';
 | 
			
		||||
		},
 | 
			
		||||
@@ -583,9 +603,6 @@ export default Vue.extend({
 | 
			
		||||
		z-index 1
 | 
			
		||||
		font-size 15px
 | 
			
		||||
 | 
			
		||||
		&.inWindow
 | 
			
		||||
			box-shadow var(--shadowRight)
 | 
			
		||||
 | 
			
		||||
		> p
 | 
			
		||||
			display block
 | 
			
		||||
			padding 10px 16px
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="kedshtep" :class="{ naked, inNakedDeckColumn }">
 | 
			
		||||
<div class="kedshtep" :class="{ naked, inNakedDeckColumn, shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
 | 
			
		||||
	<header v-if="showHeader">
 | 
			
		||||
		<div class="title"><slot name="header"></slot></div>
 | 
			
		||||
		<slot name="func"></slot>
 | 
			
		||||
@@ -60,8 +60,12 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
	&:not(.inNakedDeckColumn)
 | 
			
		||||
		background var(--face)
 | 
			
		||||
		box-shadow var(--shadow)
 | 
			
		||||
		border-radius var(--round)
 | 
			
		||||
 | 
			
		||||
		&.round
 | 
			
		||||
			border-radius 6px
 | 
			
		||||
 | 
			
		||||
		&.shadow
 | 
			
		||||
			box-shadow 0 3px 8px rgba(0, 0, 0, 0.2)
 | 
			
		||||
 | 
			
		||||
		& + .kedshtep
 | 
			
		||||
			margin-top 16px
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="header" :class="navbar">
 | 
			
		||||
<div class="header" :class="navbar" :data-shadow="$store.state.device.useShadow">
 | 
			
		||||
	<div class="body">
 | 
			
		||||
		<div class="post">
 | 
			
		||||
			<button @click="post" :title="$t('title')"><fa icon="pencil-alt"/></button>
 | 
			
		||||
@@ -62,7 +62,7 @@
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<transition :name="`slide-${navbar}`">
 | 
			
		||||
		<div class="notifications" v-if="showNotifications" ref="notifications" :class="navbar">
 | 
			
		||||
		<div class="notifications" v-if="showNotifications" ref="notifications" :class="navbar" :data-shadow="$store.state.device.useShadow">
 | 
			
		||||
			<mk-notifications/>
 | 
			
		||||
		</div>
 | 
			
		||||
	</transition>
 | 
			
		||||
@@ -226,11 +226,15 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
	&.left
 | 
			
		||||
		left 0
 | 
			
		||||
		box-shadow var(--shadowRight)
 | 
			
		||||
 | 
			
		||||
		&[data-shadow]
 | 
			
		||||
			box-shadow 4px 0 4px rgba(0, 0, 0, 0.1)
 | 
			
		||||
 | 
			
		||||
	&.right
 | 
			
		||||
		right 0
 | 
			
		||||
		box-shadow var(--shadowLeft)
 | 
			
		||||
 | 
			
		||||
		&[data-shadow]
 | 
			
		||||
			box-shadow -4px 0 4px rgba(0, 0, 0, 0.1)
 | 
			
		||||
 | 
			
		||||
	> .body
 | 
			
		||||
		position fixed
 | 
			
		||||
@@ -302,11 +306,15 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
		&.left
 | 
			
		||||
			left $width
 | 
			
		||||
			box-shadow var(--shadowRight)
 | 
			
		||||
 | 
			
		||||
			&[data-shadow]
 | 
			
		||||
				box-shadow 4px 0 4px rgba(0, 0, 0, 0.1)
 | 
			
		||||
 | 
			
		||||
		&.right
 | 
			
		||||
			right $width
 | 
			
		||||
			box-shadow var(--shadowLeft)
 | 
			
		||||
 | 
			
		||||
			&[data-shadow]
 | 
			
		||||
				box-shadow -4px 0 4px rgba(0, 0, 0, 0.1)
 | 
			
		||||
 | 
			
		||||
	.nav
 | 
			
		||||
		> *
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
						<option value="users">{{ $t('@.widgets.users') }}</option>
 | 
			
		||||
						<option value="polls">{{ $t('@.widgets.polls') }}</option>
 | 
			
		||||
						<option value="post-form">{{ $t('@.widgets.post-form') }}</option>
 | 
			
		||||
						<option value="messaging">{{ $t('@.widgets.messaging') }}</option>
 | 
			
		||||
						<option value="messaging">{{ $t('@.messaging') }}</option>
 | 
			
		||||
						<option value="memo">{{ $t('@.widgets.memo') }}</option>
 | 
			
		||||
						<option value="hashtags">{{ $t('@.widgets.hashtags') }}</option>
 | 
			
		||||
						<option value="posts-monitor">{{ $t('@.widgets.posts-monitor') }}</option>
 | 
			
		||||
@@ -109,6 +109,10 @@ export default Vue.extend({
 | 
			
		||||
					name: 'broadcast',
 | 
			
		||||
					place: 'right',
 | 
			
		||||
					data: {}
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'hashtags',
 | 
			
		||||
					place: 'right',
 | 
			
		||||
					data: {}
 | 
			
		||||
				}];
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="pwbzawku">
 | 
			
		||||
	<mk-post-form class="form" v-if="$store.state.settings.showPostFormOnTopOfTl"/>
 | 
			
		||||
	<mk-post-form class="form" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }" v-if="$store.state.settings.showPostFormOnTopOfTl"/>
 | 
			
		||||
	<div class="main">
 | 
			
		||||
		<component :is="src == 'list' ? 'mk-user-list-timeline' : 'x-core'" ref="tl" v-bind="options">
 | 
			
		||||
			<header class="zahtxcqi">
 | 
			
		||||
@@ -193,8 +193,12 @@ export default Vue.extend({
 | 
			
		||||
.pwbzawku
 | 
			
		||||
	> .form
 | 
			
		||||
		margin-bottom 16px
 | 
			
		||||
		box-shadow var(--shadow)
 | 
			
		||||
		border-radius var(--round)
 | 
			
		||||
 | 
			
		||||
		&.round
 | 
			
		||||
			border-radius 6px
 | 
			
		||||
 | 
			
		||||
		&.shadow
 | 
			
		||||
			box-shadow 0 3px 8px rgba(0, 0, 0, 0.2)
 | 
			
		||||
 | 
			
		||||
	.zahtxcqi
 | 
			
		||||
		padding 0 8px
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,11 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="omechnps" v-if="!fetching">
 | 
			
		||||
	<div class="is-suspended" v-if="user.isSuspended"><fa icon="exclamation-triangle"/> {{ $t('@.user-suspended') }}</div>
 | 
			
		||||
	<div class="is-remote" v-if="user.host != null"><fa icon="exclamation-triangle"/> {{ $t('@.is-remote-user') }}<a :href="user.url || user.uri" target="_blank">{{ $t('@.view-on-remote') }}</a></div>
 | 
			
		||||
	<div class="is-suspended" v-if="user.isSuspended" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
 | 
			
		||||
		<fa icon="exclamation-triangle"/> {{ $t('@.user-suspended') }}
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="is-remote" v-if="user.host != null" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
 | 
			
		||||
		<fa icon="exclamation-triangle"/> {{ $t('@.is-remote-user') }}<a :href="user.url || user.uri" target="_blank">{{ $t('@.view-on-remote') }}</a>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="main">
 | 
			
		||||
		<x-header class="header" :user="user"/>
 | 
			
		||||
		<router-view :user="user"></router-view>
 | 
			
		||||
@@ -61,8 +65,12 @@ export default Vue.extend({
 | 
			
		||||
		margin-bottom 16px
 | 
			
		||||
		padding 14px 16px
 | 
			
		||||
		font-size 14px
 | 
			
		||||
		box-shadow var(--shadow)
 | 
			
		||||
		border-radius var(--round)
 | 
			
		||||
 | 
			
		||||
		&.round
 | 
			
		||||
			border-radius 6px
 | 
			
		||||
 | 
			
		||||
		&.shadow
 | 
			
		||||
			box-shadow 0 3px 8px rgba(0, 0, 0, 0.2)
 | 
			
		||||
 | 
			
		||||
		&.is-suspended
 | 
			
		||||
			color var(--suspendedInfoFg)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,84 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="vahgrswmbzfdlmomxnqftuueyvwaafth">
 | 
			
		||||
	<p class="title"><fa icon="users"/>{{ $t('title') }}</p>
 | 
			
		||||
	<p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
 | 
			
		||||
	<div v-if="!fetching && users.length > 0">
 | 
			
		||||
	<router-link v-for="user in users" :to="user | userPage" :key="user.id">
 | 
			
		||||
		<img :src="user.avatarUrl" :alt="user | userName" v-user-preview="user.id"/>
 | 
			
		||||
	</router-link>
 | 
			
		||||
	</div>
 | 
			
		||||
	<p class="empty" v-if="!fetching && users.length == 0">{{ $t('no-users') }}</p>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import i18n from '../../../../i18n';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	i18n: i18n('desktop/views/pages/user/user.followers-you-know.vue'),
 | 
			
		||||
	props: ['user'],
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			users: [],
 | 
			
		||||
			fetching: true
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$root.api('users/followers', {
 | 
			
		||||
			userId: this.user.id,
 | 
			
		||||
			iknow: true,
 | 
			
		||||
			limit: 16
 | 
			
		||||
		}).then(x => {
 | 
			
		||||
			this.users = x.users;
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.vahgrswmbzfdlmomxnqftuueyvwaafth
 | 
			
		||||
	background var(--face)
 | 
			
		||||
	box-shadow var(--shadow)
 | 
			
		||||
	border-radius var(--round)
 | 
			
		||||
 | 
			
		||||
	> .title
 | 
			
		||||
		z-index 1
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding 0 16px
 | 
			
		||||
		line-height 42px
 | 
			
		||||
		font-size 0.9em
 | 
			
		||||
		font-weight bold
 | 
			
		||||
		color var(--faceHeaderText)
 | 
			
		||||
		box-shadow 0 1px rgba(#000, 0.07)
 | 
			
		||||
 | 
			
		||||
		> i
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	> div
 | 
			
		||||
		padding 8px
 | 
			
		||||
 | 
			
		||||
		> a
 | 
			
		||||
			display inline-block
 | 
			
		||||
			margin 4px
 | 
			
		||||
 | 
			
		||||
			> img
 | 
			
		||||
				display inline-block
 | 
			
		||||
				text-align center
 | 
			
		||||
				width 48px
 | 
			
		||||
				height 48px
 | 
			
		||||
				vertical-align bottom
 | 
			
		||||
				border-radius 100%
 | 
			
		||||
 | 
			
		||||
	> .initializing
 | 
			
		||||
	> .empty
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding 16px
 | 
			
		||||
		text-align center
 | 
			
		||||
		color var(--text)
 | 
			
		||||
 | 
			
		||||
		> i
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,112 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="hozptpaliadatkehcmcayizwzwwctpbc">
 | 
			
		||||
	<p class="title"><fa icon="users"/>{{ $t('title') }}</p>
 | 
			
		||||
	<p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
 | 
			
		||||
	<template v-if="!fetching && users.length != 0">
 | 
			
		||||
		<div class="user" v-for="friend in users">
 | 
			
		||||
			<mk-avatar class="avatar" :user="friend"/>
 | 
			
		||||
			<div class="body">
 | 
			
		||||
				<router-link class="name" :to="friend | userPage" v-user-preview="friend.id"><mk-user-name :user="friend"/></router-link>
 | 
			
		||||
				<p class="username">@{{ friend | acct }}</p>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</template>
 | 
			
		||||
	<p class="empty" v-if="!fetching && users.length == 0">{{ $t('no-users') }}</p>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import i18n from '../../../../i18n';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	i18n: i18n('desktop/views/pages/user/user.friends.vue'),
 | 
			
		||||
	props: ['user'],
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			users: [],
 | 
			
		||||
			fetching: true
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$root.api('users/get_frequently_replied_users', {
 | 
			
		||||
			userId: this.user.id,
 | 
			
		||||
			limit: 4
 | 
			
		||||
		}).then(docs => {
 | 
			
		||||
			this.users = docs.map(doc => doc.user);
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.hozptpaliadatkehcmcayizwzwwctpbc
 | 
			
		||||
	background var(--face)
 | 
			
		||||
	box-shadow var(--shadow)
 | 
			
		||||
	border-radius var(--round)
 | 
			
		||||
	overflow hidden
 | 
			
		||||
 | 
			
		||||
	> .title
 | 
			
		||||
		z-index 1
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding 0 16px
 | 
			
		||||
		line-height 42px
 | 
			
		||||
		font-size 0.9em
 | 
			
		||||
		font-weight bold
 | 
			
		||||
		background var(--faceHeader)
 | 
			
		||||
		color var(--faceHeaderText)
 | 
			
		||||
		box-shadow 0 1px rgba(#000, 0.07)
 | 
			
		||||
 | 
			
		||||
		> i
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	> .initializing
 | 
			
		||||
	> .empty
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding 16px
 | 
			
		||||
		text-align center
 | 
			
		||||
		color var(--text)
 | 
			
		||||
 | 
			
		||||
		> i
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	> .user
 | 
			
		||||
		padding 16px
 | 
			
		||||
		border-bottom solid 1px var(--faceDivider)
 | 
			
		||||
 | 
			
		||||
		&:last-child
 | 
			
		||||
			border-bottom none
 | 
			
		||||
 | 
			
		||||
		&:after
 | 
			
		||||
			content ""
 | 
			
		||||
			display block
 | 
			
		||||
			clear both
 | 
			
		||||
 | 
			
		||||
		> .avatar
 | 
			
		||||
			display block
 | 
			
		||||
			float left
 | 
			
		||||
			margin 0 12px 0 0
 | 
			
		||||
			width 42px
 | 
			
		||||
			height 42px
 | 
			
		||||
			border-radius 8px
 | 
			
		||||
 | 
			
		||||
		> .body
 | 
			
		||||
			float left
 | 
			
		||||
			width calc(100% - 54px)
 | 
			
		||||
 | 
			
		||||
			> .name
 | 
			
		||||
				margin 0
 | 
			
		||||
				font-size 16px
 | 
			
		||||
				line-height 24px
 | 
			
		||||
				color var(--text)
 | 
			
		||||
 | 
			
		||||
			> .username
 | 
			
		||||
				display block
 | 
			
		||||
				margin 0
 | 
			
		||||
				font-size 15px
 | 
			
		||||
				line-height 16px
 | 
			
		||||
				color var(--text)
 | 
			
		||||
				opacity 0.7
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="header">
 | 
			
		||||
<div class="header" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
 | 
			
		||||
	<div class="banner-container" :style="style">
 | 
			
		||||
		<div class="banner" ref="banner" :style="style" @click="onBannerClick"></div>
 | 
			
		||||
		<div class="fade"></div>
 | 
			
		||||
@@ -126,10 +126,14 @@ export default Vue.extend({
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.header
 | 
			
		||||
	background var(--face)
 | 
			
		||||
	box-shadow var(--shadow)
 | 
			
		||||
	border-radius var(--round)
 | 
			
		||||
	overflow hidden
 | 
			
		||||
 | 
			
		||||
	&.round
 | 
			
		||||
		border-radius 6px
 | 
			
		||||
 | 
			
		||||
	&.shadow
 | 
			
		||||
		box-shadow 0 3px 8px rgba(0, 0, 0, 0.2)
 | 
			
		||||
 | 
			
		||||
	> .banner-container
 | 
			
		||||
		height 250px
 | 
			
		||||
		overflow hidden
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@
 | 
			
		||||
						<span class="signin" @click="signin">{{ $t('@.signin') }}</span>
 | 
			
		||||
					</p>
 | 
			
		||||
 | 
			
		||||
					<img :src="meta.mascotImageUrl" alt="" title="藍" class="char">
 | 
			
		||||
					<img v-if="meta" :src="meta.mascotImageUrl" alt="" title="藍" class="char">
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
 | 
			
		||||
@@ -344,8 +344,6 @@ export default Vue.extend({
 | 
			
		||||
		.block
 | 
			
		||||
			color var(--text)
 | 
			
		||||
			background var(--face)
 | 
			
		||||
			box-shadow var(--shadow)
 | 
			
		||||
			//border-radius 8px
 | 
			
		||||
			overflow auto
 | 
			
		||||
 | 
			
		||||
			> header
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ import wActivity from './activity.vue';
 | 
			
		||||
import wTrends from './trends.vue';
 | 
			
		||||
import wUsers from './users.vue';
 | 
			
		||||
import wPolls from './polls.vue';
 | 
			
		||||
import wPostForm from './post-form.vue';
 | 
			
		||||
import wMessaging from './messaging.vue';
 | 
			
		||||
import wProfile from './profile.vue';
 | 
			
		||||
import wCustomize from './customize.vue';
 | 
			
		||||
@@ -17,7 +16,6 @@ Vue.component('mkw-activity', wActivity);
 | 
			
		||||
Vue.component('mkw-trends', wTrends);
 | 
			
		||||
Vue.component('mkw-users', wUsers);
 | 
			
		||||
Vue.component('mkw-polls', wPolls);
 | 
			
		||||
Vue.component('mkw-post-form', wPostForm);
 | 
			
		||||
Vue.component('mkw-messaging', wMessaging);
 | 
			
		||||
Vue.component('mkw-profile', wProfile);
 | 
			
		||||
Vue.component('mkw-customize', wCustomize);
 | 
			
		||||
 
 | 
			
		||||
@@ -401,34 +401,6 @@ export default (callback: (launch: (router: VueRouter) => [Vue, MiOS], os: MiOS)
 | 
			
		||||
				console.log(`Cannot reapply theme. ${e}`);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			//#region shadow
 | 
			
		||||
			const shadow = '0 3px 8px rgba(0, 0, 0, 0.2)';
 | 
			
		||||
			const shadowRight = '4px 0 4px rgba(0, 0, 0, 0.1)';
 | 
			
		||||
			const shadowLeft = '-4px 0 4px rgba(0, 0, 0, 0.1)';
 | 
			
		||||
			if (os.store.state.device.useShadow) {
 | 
			
		||||
				document.documentElement.style.setProperty('--shadow', shadow);
 | 
			
		||||
				document.documentElement.style.setProperty('--shadowRight', shadowRight);
 | 
			
		||||
				document.documentElement.style.setProperty('--shadowLeft', shadowLeft);
 | 
			
		||||
			}
 | 
			
		||||
			os.store.watch(s => {
 | 
			
		||||
				return s.device.useShadow;
 | 
			
		||||
			}, v => {
 | 
			
		||||
				document.documentElement.style.setProperty('--shadow', v ? shadow : 'none');
 | 
			
		||||
				document.documentElement.style.setProperty('--shadowRight', v ? shadowRight : 'none');
 | 
			
		||||
				document.documentElement.style.setProperty('--shadowLeft', v ? shadowLeft : 'none');
 | 
			
		||||
			});
 | 
			
		||||
			//#endregion
 | 
			
		||||
 | 
			
		||||
			//#region rounded corners
 | 
			
		||||
			const round = '6px';
 | 
			
		||||
			if (os.store.state.device.roundedCorners) document.documentElement.style.setProperty('--round', round);
 | 
			
		||||
			os.store.watch(s => {
 | 
			
		||||
				return s.device.roundedCorners;
 | 
			
		||||
			}, v => {
 | 
			
		||||
				document.documentElement.style.setProperty('--round', v ? round : '0');
 | 
			
		||||
			});
 | 
			
		||||
			//#endregion
 | 
			
		||||
 | 
			
		||||
			//#region line width
 | 
			
		||||
			document.documentElement.style.setProperty('--lineWidth', `${os.store.state.device.lineWidth}px`);
 | 
			
		||||
			os.store.watch(s => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-note-detail" tabindex="-1" :class="{ shadow: $store.state.device.useShadow }">
 | 
			
		||||
<div class="mk-note-detail" tabindex="-1" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
 | 
			
		||||
	<button
 | 
			
		||||
		class="more"
 | 
			
		||||
		v-if="appearNote.reply && appearNote.reply.replyId && conversation.length == 0"
 | 
			
		||||
@@ -164,7 +164,9 @@ export default Vue.extend({
 | 
			
		||||
	width 100%
 | 
			
		||||
	text-align left
 | 
			
		||||
	background var(--face)
 | 
			
		||||
	border-radius 8px
 | 
			
		||||
 | 
			
		||||
	&.round
 | 
			
		||||
		border-radius 8px
 | 
			
		||||
 | 
			
		||||
	&.shadow
 | 
			
		||||
		box-shadow 0 4px 16px rgba(#000, 0.1)
 | 
			
		||||
 
 | 
			
		||||
@@ -36,26 +36,26 @@
 | 
			
		||||
				</div>
 | 
			
		||||
				<span class="app" v-if="appearNote.app && $store.state.settings.showVia">via <b>{{ appearNote.app.name }}</b></span>
 | 
			
		||||
			</div>
 | 
			
		||||
			<footer v-if="appearNote.deletedAt == null">
 | 
			
		||||
			<footer v-if="appearNote.deletedAt == null" class="footer">
 | 
			
		||||
				<mk-reactions-viewer :note="appearNote" ref="reactionsViewer"/>
 | 
			
		||||
				<button @click="reply()">
 | 
			
		||||
				<button @click="reply()" class="button">
 | 
			
		||||
					<template v-if="appearNote.reply"><fa icon="reply-all"/></template>
 | 
			
		||||
					<template v-else><fa icon="reply"/></template>
 | 
			
		||||
					<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
 | 
			
		||||
				</button>
 | 
			
		||||
				<button v-if="['public', 'home'].includes(appearNote.visibility)" @click="renote()" title="Renote">
 | 
			
		||||
				<button v-if="['public', 'home'].includes(appearNote.visibility)" @click="renote()" title="Renote" class="button">
 | 
			
		||||
					<fa icon="retweet"/><p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
 | 
			
		||||
				</button>
 | 
			
		||||
				<button v-else>
 | 
			
		||||
				<button v-else class="button">
 | 
			
		||||
					<fa icon="ban"/>
 | 
			
		||||
				</button>
 | 
			
		||||
				<button v-if="!isMyNote && appearNote.myReaction == null" class="reactionButton" @click="react()" ref="reactButton">
 | 
			
		||||
				<button v-if="!isMyNote && appearNote.myReaction == null" class="button" @click="react()" ref="reactButton">
 | 
			
		||||
					<fa icon="plus"/>
 | 
			
		||||
				</button>
 | 
			
		||||
				<button v-if="!isMyNote && appearNote.myReaction != null" class="reactionButton reacted" @click="undoReact(appearNote)" ref="reactButton">
 | 
			
		||||
				<button v-if="!isMyNote && appearNote.myReaction != null" class="button reacted" @click="undoReact(appearNote)" ref="reactButton">
 | 
			
		||||
					<fa icon="minus"/>
 | 
			
		||||
				</button>
 | 
			
		||||
				<button class="menu" @click="menu()" ref="menuButton">
 | 
			
		||||
				<button class="button" @click="menu()" ref="menuButton">
 | 
			
		||||
					<fa icon="ellipsis-h"/>
 | 
			
		||||
				</button>
 | 
			
		||||
			</footer>
 | 
			
		||||
@@ -107,20 +107,6 @@ export default Vue.extend({
 | 
			
		||||
	font-size 13px
 | 
			
		||||
	border-bottom solid var(--lineWidth) var(--faceDivider)
 | 
			
		||||
 | 
			
		||||
	&:focus
 | 
			
		||||
		z-index 1
 | 
			
		||||
 | 
			
		||||
		&:after
 | 
			
		||||
			content ""
 | 
			
		||||
			pointer-events none
 | 
			
		||||
			position absolute
 | 
			
		||||
			top 2px
 | 
			
		||||
			right 2px
 | 
			
		||||
			bottom 2px
 | 
			
		||||
			left 2px
 | 
			
		||||
			border 2px solid var(--primaryAlpha03)
 | 
			
		||||
			border-radius 4px
 | 
			
		||||
 | 
			
		||||
	&:last-of-type
 | 
			
		||||
		border-bottom none
 | 
			
		||||
 | 
			
		||||
@@ -251,8 +237,8 @@ export default Vue.extend({
 | 
			
		||||
					font-size 12px
 | 
			
		||||
					color #ccc
 | 
			
		||||
 | 
			
		||||
			> footer
 | 
			
		||||
				> button
 | 
			
		||||
			> .footer
 | 
			
		||||
				> .button
 | 
			
		||||
					margin 0
 | 
			
		||||
					padding 8px
 | 
			
		||||
					background transparent
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="ivaojijs" :class="{ shadow: $store.state.device.useShadow }">
 | 
			
		||||
<div class="ivaojijs" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
 | 
			
		||||
	<div class="empty" v-if="notes.length == 0 && !fetching && inited">{{ $t('@.no-notes') }}</div>
 | 
			
		||||
 | 
			
		||||
	<mk-error v-if="!fetching && !inited" @retry="init()"/>
 | 
			
		||||
@@ -191,7 +191,9 @@ export default Vue.extend({
 | 
			
		||||
.ivaojijs
 | 
			
		||||
	overflow hidden
 | 
			
		||||
	background var(--face)
 | 
			
		||||
	border-radius 8px
 | 
			
		||||
 | 
			
		||||
	&.round
 | 
			
		||||
		border-radius 8px
 | 
			
		||||
 | 
			
		||||
	&.shadow
 | 
			
		||||
		box-shadow 0 4px 16px rgba(#000, 0.1)
 | 
			
		||||
 
 | 
			
		||||
@@ -61,7 +61,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
		this.connection.on('notification', this.onNotification);
 | 
			
		||||
 | 
			
		||||
		const max = 10;
 | 
			
		||||
		const max = 15;
 | 
			
		||||
 | 
			
		||||
		this.$root.api('i/notifications', {
 | 
			
		||||
			limit: max + 1
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="ukygtjoj" :class="{ naked, inNakedDeckColumn, hideHeader: !showHeader, shadow: $store.state.device.useShadow }">
 | 
			
		||||
<div class="ukygtjoj" :class="{ naked, inNakedDeckColumn, hideHeader: !showHeader, shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
 | 
			
		||||
	<header v-if="showHeader">
 | 
			
		||||
		<div class="title"><slot name="header"></slot></div>
 | 
			
		||||
		<slot name="func"></slot>
 | 
			
		||||
@@ -59,7 +59,9 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
	&:not(.inNakedDeckColumn)
 | 
			
		||||
		background var(--face)
 | 
			
		||||
		border-radius 8px
 | 
			
		||||
 | 
			
		||||
		&.round
 | 
			
		||||
			border-radius 8px
 | 
			
		||||
 | 
			
		||||
		&.shadow
 | 
			
		||||
			box-shadow 0 4px 16px rgba(#000, 0.1)
 | 
			
		||||
@@ -82,7 +84,6 @@ export default Vue.extend({
 | 
			
		||||
				font-weight normal
 | 
			
		||||
				color var(--faceHeaderText)
 | 
			
		||||
				background var(--faceHeader)
 | 
			
		||||
				border-radius 8px 8px 0 0
 | 
			
		||||
 | 
			
		||||
				> [data-icon]
 | 
			
		||||
					margin-right 6px
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,7 @@
 | 
			
		||||
			<div class="notifications" v-if="showNotifications">
 | 
			
		||||
				<header>
 | 
			
		||||
					<button @click="showNotifications = false"><fa icon="times"/></button>
 | 
			
		||||
					<i v-if="hasUnreadNotification" class="circle"><fa icon="circle"/></i>
 | 
			
		||||
				</header>
 | 
			
		||||
				<mk-notifications/>
 | 
			
		||||
			</div>
 | 
			
		||||
@@ -207,7 +208,7 @@ export default Vue.extend({
 | 
			
		||||
		font-size 15px
 | 
			
		||||
 | 
			
		||||
		&.notifications
 | 
			
		||||
			width 340px
 | 
			
		||||
			width 330px
 | 
			
		||||
 | 
			
		||||
		> .notifications
 | 
			
		||||
			padding-top 42px
 | 
			
		||||
@@ -217,7 +218,7 @@ export default Vue.extend({
 | 
			
		||||
				top 0
 | 
			
		||||
				left 0
 | 
			
		||||
				z-index 1000
 | 
			
		||||
				width 340px
 | 
			
		||||
				width 330px
 | 
			
		||||
				line-height 42px
 | 
			
		||||
				background var(--secondary)
 | 
			
		||||
 | 
			
		||||
@@ -228,6 +229,13 @@ export default Vue.extend({
 | 
			
		||||
					line-height 42px
 | 
			
		||||
					color var(--text)
 | 
			
		||||
 | 
			
		||||
				> i
 | 
			
		||||
					position absolute
 | 
			
		||||
					top 0
 | 
			
		||||
					right 16px
 | 
			
		||||
					font-size 12px
 | 
			
		||||
					color var(--notificationIndicator)
 | 
			
		||||
 | 
			
		||||
		> .nav
 | 
			
		||||
 | 
			
		||||
			> .me
 | 
			
		||||
 
 | 
			
		||||
@@ -124,7 +124,7 @@ export default Vue.extend({
 | 
			
		||||
				position absolute
 | 
			
		||||
				top 0
 | 
			
		||||
				left 0
 | 
			
		||||
				color var(--primary)
 | 
			
		||||
				color var(--notificationIndicator)
 | 
			
		||||
				font-size 16px
 | 
			
		||||
 | 
			
		||||
		&.post
 | 
			
		||||
 
 | 
			
		||||
@@ -308,7 +308,10 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
		circleIcons: {
 | 
			
		||||
			get() { return this.$store.state.settings.circleIcons; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'circleIcons', value }); }
 | 
			
		||||
			set(value) {
 | 
			
		||||
				this.$store.dispatch('settings/set', { key: 'circleIcons', value });
 | 
			
		||||
				this.reload();
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		lineWidth: {
 | 
			
		||||
@@ -318,12 +321,18 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
		contrastedAcct: {
 | 
			
		||||
			get() { return this.$store.state.settings.contrastedAcct; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'contrastedAcct', value }); }
 | 
			
		||||
			set(value) {
 | 
			
		||||
				this.$store.dispatch('settings/set', { key: 'contrastedAcct', value });
 | 
			
		||||
				this.reload();
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showFullAcct: {
 | 
			
		||||
			get() { return this.$store.state.settings.showFullAcct; },
 | 
			
		||||
			set(value) { this.$store.dispatch('settings/set', { key: 'showFullAcct', value }); }
 | 
			
		||||
			set(value) {
 | 
			
		||||
				this.$store.dispatch('settings/set', { key: 'showFullAcct', value });
 | 
			
		||||
				this.reload();
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		showVia: {
 | 
			
		||||
@@ -396,6 +405,18 @@ export default Vue.extend({
 | 
			
		||||
			this.$root.signout();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		reload() {
 | 
			
		||||
			this.$root.dialog({
 | 
			
		||||
				type: 'warning',
 | 
			
		||||
				text: this.$t('@.reload-to-apply-the-setting'),
 | 
			
		||||
				showCancelButton: true
 | 
			
		||||
			}).then(({ canceled }) => {
 | 
			
		||||
				if (!canceled) {
 | 
			
		||||
					location.reload();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		checkForUpdate() {
 | 
			
		||||
			this.checkingForUpdate = true;
 | 
			
		||||
			checkForUpdate(this.$root, true, true).then(newer => {
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,14 @@ export const meta = {
 | 
			
		||||
				'-attachedRemoteUsers',
 | 
			
		||||
			]),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	res: {
 | 
			
		||||
		type: 'array',
 | 
			
		||||
		items: {
 | 
			
		||||
			type: 'Hashtag'
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const sort: any = {
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,14 @@ export const meta = {
 | 
			
		||||
				'ja-JP': 'オフセット'
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	res: {
 | 
			
		||||
		type: 'array',
 | 
			
		||||
		items: {
 | 
			
		||||
			type: 'string'
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default define(meta, async (ps) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,14 @@ export const meta = {
 | 
			
		||||
			]),
 | 
			
		||||
			default: 'local'
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	res: {
 | 
			
		||||
		type: 'array',
 | 
			
		||||
		items: {
 | 
			
		||||
			type: 'User'
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const sort: any = {
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,13 @@ export const meta = {
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	res: {
 | 
			
		||||
		type: 'array',
 | 
			
		||||
		items: {
 | 
			
		||||
			type: 'Reaction'
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	errors: {
 | 
			
		||||
		noSuchNote: {
 | 
			
		||||
			message: 'No such note.',
 | 
			
		||||
 
 | 
			
		||||
@@ -312,7 +312,7 @@ export const schemas = {
 | 
			
		||||
				example: 'xxxxxxxxxxxxxxxxxxxxxxxx',
 | 
			
		||||
			},
 | 
			
		||||
			parent: {
 | 
			
		||||
				type: 'DriveFolder',
 | 
			
		||||
				$ref: '#/components/schemas/DriveFolder'
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		required: ['id', 'createdAt', 'name']
 | 
			
		||||
@@ -361,4 +361,86 @@ export const schemas = {
 | 
			
		||||
		},
 | 
			
		||||
		required: ['id', 'createdAt', 'blockee']
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	Reaction: {
 | 
			
		||||
		type: 'object',
 | 
			
		||||
		properties: {
 | 
			
		||||
			id: {
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				format: 'id',
 | 
			
		||||
				description: 'The unique identifier for this reaction.',
 | 
			
		||||
				example: 'xxxxxxxxxxxxxxxxxxxxxxxx',
 | 
			
		||||
			},
 | 
			
		||||
			createdAt: {
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				format: 'date-time',
 | 
			
		||||
				description: 'The date that the reaction was created.'
 | 
			
		||||
			},
 | 
			
		||||
			user: {
 | 
			
		||||
				$ref: '#/components/schemas/User',
 | 
			
		||||
				description: 'User who performed this reaction.'
 | 
			
		||||
			},
 | 
			
		||||
			type: {
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				enum: [
 | 
			
		||||
					'like',
 | 
			
		||||
					'love',
 | 
			
		||||
					'laugh',
 | 
			
		||||
					'hmm',
 | 
			
		||||
					'surprise',
 | 
			
		||||
					'congrats',
 | 
			
		||||
					'angry',
 | 
			
		||||
					'confused',
 | 
			
		||||
					'rip',
 | 
			
		||||
					'pudding'
 | 
			
		||||
				],
 | 
			
		||||
				description: 'The reaction type.'
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		required: ['id', 'createdAt', 'user', 'type']
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	Hashtag: {
 | 
			
		||||
		type: 'object',
 | 
			
		||||
		properties: {
 | 
			
		||||
			tag: {
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				description: 'The hashtag name. No # prefixed.',
 | 
			
		||||
				example: 'misskey',
 | 
			
		||||
			},
 | 
			
		||||
			mentionedUsersCount: {
 | 
			
		||||
				type: 'number',
 | 
			
		||||
				description: 'Number of all users using this hashtag.'
 | 
			
		||||
			},
 | 
			
		||||
			mentionedLocalUsersCount: {
 | 
			
		||||
				type: 'number',
 | 
			
		||||
				description: 'Number of local users using this hashtag.'
 | 
			
		||||
			},
 | 
			
		||||
			mentionedRemoteUsersCount: {
 | 
			
		||||
				type: 'number',
 | 
			
		||||
				description: 'Number of remote users using this hashtag.'
 | 
			
		||||
			},
 | 
			
		||||
			attachedUsersCount: {
 | 
			
		||||
				type: 'number',
 | 
			
		||||
				description: 'Number of all users who attached this hashtag to profile.'
 | 
			
		||||
			},
 | 
			
		||||
			attachedLocalUsersCount: {
 | 
			
		||||
				type: 'number',
 | 
			
		||||
				description: 'Number of local users who attached this hashtag to profile.'
 | 
			
		||||
			},
 | 
			
		||||
			attachedRemoteUsersCount: {
 | 
			
		||||
				type: 'number',
 | 
			
		||||
				description: 'Number of remote users who attached this hashtag to profile.'
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		required: [
 | 
			
		||||
			'tag',
 | 
			
		||||
			'mentionedUsersCount',
 | 
			
		||||
			'mentionedLocalUsersCount',
 | 
			
		||||
			'mentionedRemoteUsersCount',
 | 
			
		||||
			'attachedUsersCount',
 | 
			
		||||
			'attachedLocalUsersCount',
 | 
			
		||||
			'attachedRemoteUsersCount',
 | 
			
		||||
		]
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,15 @@ import * as Koa from 'koa';
 | 
			
		||||
import * as request from 'request-promise-native';
 | 
			
		||||
import summaly from 'summaly';
 | 
			
		||||
import fetchMeta from '../../misc/fetch-meta';
 | 
			
		||||
import Logger from '../../misc/logger';
 | 
			
		||||
 | 
			
		||||
const logger = new Logger('url-preview');
 | 
			
		||||
 | 
			
		||||
module.exports = async (ctx: Koa.BaseContext) => {
 | 
			
		||||
	const meta = await fetchMeta();
 | 
			
		||||
 | 
			
		||||
	logger.info(`Getting preview of ${ctx.query.url} ...`);
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
		const summary = meta.summalyProxy ? await request.get({
 | 
			
		||||
			url: meta.summalyProxy,
 | 
			
		||||
@@ -17,6 +22,8 @@ module.exports = async (ctx: Koa.BaseContext) => {
 | 
			
		||||
			followRedirects: false
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		logger.succ(`Got preview of ${ctx.query.url}: ${summary.title}`);
 | 
			
		||||
 | 
			
		||||
		summary.icon = wrap(summary.icon);
 | 
			
		||||
		summary.thumbnail = wrap(summary.thumbnail);
 | 
			
		||||
 | 
			
		||||
@@ -25,6 +32,7 @@ module.exports = async (ctx: Koa.BaseContext) => {
 | 
			
		||||
 | 
			
		||||
		ctx.body = summary;
 | 
			
		||||
	} catch (e) {
 | 
			
		||||
		logger.error(`Failed to get preview of ${ctx.query.url}: ${e}`);
 | 
			
		||||
		ctx.status = 200;
 | 
			
		||||
		ctx.set('Cache-Control', 'max-age=86400, immutable');
 | 
			
		||||
		ctx.body = '{}';
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user