summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2018-06-27 00:55:44 +0900
committerGitHub <noreply@github.com>2018-06-27 00:55:44 +0900
commit9ac34badad7a99166907e1a42f8a817413a74d58 (patch)
treeeb45b75f1f5f87a36f0eefec42f449fda109a6ad /src/client
parentNew translations ja.yml (Portuguese) (diff)
parentMerge pull request #1789 from syuilo/greenkeeper/uuid-3.3.0 (diff)
downloadmisskey-9ac34badad7a99166907e1a42f8a817413a74d58.tar.gz
misskey-9ac34badad7a99166907e1a42f8a817413a74d58.tar.bz2
misskey-9ac34badad7a99166907e1a42f8a817413a74d58.zip
Merge branch 'master' into l10n_master
Diffstat (limited to 'src/client')
-rw-r--r--src/client/app/common/scripts/can-hide-text.ts16
-rw-r--r--src/client/app/common/scripts/check-for-update.ts5
-rw-r--r--src/client/app/common/scripts/compose-notification.ts62
-rw-r--r--src/client/app/common/scripts/get-kao.ts2
-rw-r--r--src/client/app/common/views/components/google.vue2
-rw-r--r--src/client/app/common/views/components/index.ts4
-rw-r--r--src/client/app/common/views/components/messaging-room.message.vue4
-rw-r--r--src/client/app/common/views/components/misskey-flavored-markdown.ts (renamed from src/client/app/common/views/components/note-html.ts)15
-rw-r--r--src/client/app/common/views/components/note-header.vue2
-rw-r--r--src/client/app/common/views/components/reaction-picker.vue35
-rw-r--r--src/client/app/common/views/components/reversi.game.vue162
-rw-r--r--src/client/app/common/views/components/reversi.vue2
-rw-r--r--src/client/app/common/views/components/welcome-timeline.vue10
-rw-r--r--src/client/app/common/views/pages/follow.vue215
-rw-r--r--src/client/app/common/views/widgets/hashtags.vue7
-rw-r--r--src/client/app/desktop/script.ts32
-rw-r--r--src/client/app/desktop/views/components/home.vue2
-rw-r--r--src/client/app/desktop/views/components/note-detail.vue36
-rw-r--r--src/client/app/desktop/views/components/notes.note.vue39
-rw-r--r--src/client/app/desktop/views/components/notes.vue8
-rw-r--r--src/client/app/desktop/views/components/notifications.vue9
-rw-r--r--src/client/app/desktop/views/components/post-form.vue2
-rw-r--r--src/client/app/desktop/views/components/settings.vue7
-rw-r--r--src/client/app/desktop/views/components/sub-note-content.vue2
-rw-r--r--src/client/app/desktop/views/components/ui.header.vue2
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.note.vue35
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.notes.vue8
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.notifications.vue7
-rw-r--r--src/client/app/desktop/views/pages/user/user.header.vue201
-rw-r--r--src/client/app/desktop/views/pages/user/user.home.vue103
-rw-r--r--src/client/app/desktop/views/pages/user/user.profile.vue82
-rw-r--r--src/client/app/desktop/views/pages/user/user.timeline.vue53
-rw-r--r--src/client/app/desktop/views/pages/user/user.twitter.vue26
-rw-r--r--src/client/app/desktop/views/pages/user/user.vue124
-rw-r--r--src/client/app/desktop/views/pages/welcome.vue55
-rw-r--r--src/client/app/mobile/api/dialog.ts19
-rw-r--r--src/client/app/mobile/script.ts6
-rw-r--r--src/client/app/mobile/views/components/dialog.vue171
-rw-r--r--src/client/app/mobile/views/components/note-detail.vue35
-rw-r--r--src/client/app/mobile/views/components/note.vue38
-rw-r--r--src/client/app/mobile/views/components/notes.vue7
-rw-r--r--src/client/app/mobile/views/components/notifications.vue7
-rw-r--r--src/client/app/mobile/views/components/post-form.vue2
-rw-r--r--src/client/app/mobile/views/components/sub-note-content.vue2
-rw-r--r--src/client/app/mobile/views/pages/settings.vue8
-rw-r--r--src/client/app/mobile/views/pages/user.vue4
-rw-r--r--src/client/app/mobile/views/pages/welcome.vue20
-rw-r--r--src/client/app/store.ts3
-rw-r--r--src/client/assets/manifest.json2
49 files changed, 1040 insertions, 660 deletions
diff --git a/src/client/app/common/scripts/can-hide-text.ts b/src/client/app/common/scripts/can-hide-text.ts
deleted file mode 100644
index 4a4be8d9d0..0000000000
--- a/src/client/app/common/scripts/can-hide-text.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-export default function(note) {
- if (note.text == null) return true;
-
- let txt = note.text;
-
- if (note.media) {
- note.media.forEach(file => {
- txt = txt.replace(file.url, '');
- if (file.src) txt = txt.replace(file.src, '');
- });
-
- if (txt == '') return true;
- }
-
- return false;
-}
diff --git a/src/client/app/common/scripts/check-for-update.ts b/src/client/app/common/scripts/check-for-update.ts
index b5ba6916d1..4445eefc39 100644
--- a/src/client/app/common/scripts/check-for-update.ts
+++ b/src/client/app/common/scripts/check-for-update.ts
@@ -23,7 +23,10 @@ export default async function(mios: MiOS, force = false, silent = false) {
}
if (!silent) {
- alert('%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current));
+ mios.apis.dialog({
+ title: '%i18n:common.update-available-title%',
+ text: '%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current)
+ });
}
return newer;
diff --git a/src/client/app/common/scripts/compose-notification.ts b/src/client/app/common/scripts/compose-notification.ts
index cc28f75998..2e58649ac2 100644
--- a/src/client/app/common/scripts/compose-notification.ts
+++ b/src/client/app/common/scripts/compose-notification.ts
@@ -20,34 +20,6 @@ export default function(type, data): Notification {
icon: data.url + '?thumbnail&size=64'
};
- case 'mention':
- return {
- title: `${getUserName(data.user)}さんから:`,
- body: getNoteSummary(data),
- icon: data.user.avatarUrl + '?thumbnail&size=64'
- };
-
- case 'reply':
- return {
- title: `${getUserName(data.user)}さんから返信:`,
- body: getNoteSummary(data),
- icon: data.user.avatarUrl + '?thumbnail&size=64'
- };
-
- case 'quote':
- return {
- title: `${getUserName(data.user)}さんが引用:`,
- body: getNoteSummary(data),
- icon: data.user.avatarUrl + '?thumbnail&size=64'
- };
-
- case 'reaction':
- return {
- title: `${getUserName(data.user)}: ${getReactionEmoji(data.reaction)}:`,
- body: getNoteSummary(data.note),
- icon: data.user.avatarUrl + '?thumbnail&size=64'
- };
-
case 'unread_messaging_message':
return {
title: `${getUserName(data.user)}さんからメッセージ:`,
@@ -62,6 +34,40 @@ export default function(type, data): Notification {
icon: data.parent.avatarUrl + '?thumbnail&size=64'
};
+ case 'notification':
+ switch (data.type) {
+ case 'mention':
+ return {
+ title: `${getUserName(data.user)}さんから:`,
+ body: getNoteSummary(data),
+ icon: data.user.avatarUrl + '?thumbnail&size=64'
+ };
+
+ case 'reply':
+ return {
+ title: `${getUserName(data.user)}さんから返信:`,
+ body: getNoteSummary(data),
+ icon: data.user.avatarUrl + '?thumbnail&size=64'
+ };
+
+ case 'quote':
+ return {
+ title: `${getUserName(data.user)}さんが引用:`,
+ body: getNoteSummary(data),
+ icon: data.user.avatarUrl + '?thumbnail&size=64'
+ };
+
+ case 'reaction':
+ return {
+ title: `${getUserName(data.user)}: ${getReactionEmoji(data.reaction)}:`,
+ body: getNoteSummary(data.note),
+ icon: data.user.avatarUrl + '?thumbnail&size=64'
+ };
+
+ default:
+ return null;
+ }
+
default:
return null;
}
diff --git a/src/client/app/common/scripts/get-kao.ts b/src/client/app/common/scripts/get-kao.ts
index 2168c5be88..d380187510 100644
--- a/src/client/app/common/scripts/get-kao.ts
+++ b/src/client/app/common/scripts/get-kao.ts
@@ -1,5 +1,5 @@
export default () => [
'(=^・・^=)',
- 'v(‘ω’)v',
+ 'v('ω')v',
'🐡( \'-\' 🐡 )フグパンチ!!!!'
][Math.floor(Math.random() * 3)];
diff --git a/src/client/app/common/views/components/google.vue b/src/client/app/common/views/components/google.vue
index 92817d3c1f..8272961ef2 100644
--- a/src/client/app/common/views/components/google.vue
+++ b/src/client/app/common/views/components/google.vue
@@ -1,7 +1,7 @@
<template>
<div class="mk-google">
<input type="search" v-model="query" :placeholder="q">
- <button @click="search">検索</button>
+ <button @click="search">%fa:search% %i18n:common.search%</button>
</div>
</template>
diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts
index 5b2fa084fb..e2cba2e53b 100644
--- a/src/client/app/common/views/components/index.ts
+++ b/src/client/app/common/views/components/index.ts
@@ -9,7 +9,7 @@ import forkit from './forkit.vue';
import acct from './acct.vue';
import avatar from './avatar.vue';
import nav from './nav.vue';
-import noteHtml from './note-html';
+import misskeyFlavoredMarkdown from './misskey-flavored-markdown';
import poll from './poll.vue';
import pollEditor from './poll-editor.vue';
import reactionIcon from './reaction-icon.vue';
@@ -47,7 +47,7 @@ Vue.component('mk-forkit', forkit);
Vue.component('mk-acct', acct);
Vue.component('mk-avatar', avatar);
Vue.component('mk-nav', nav);
-Vue.component('mk-note-html', noteHtml);
+Vue.component('misskey-flavored-markdown', misskeyFlavoredMarkdown);
Vue.component('mk-poll', poll);
Vue.component('mk-poll-editor', pollEditor);
Vue.component('mk-reaction-icon', reactionIcon);
diff --git a/src/client/app/common/views/components/messaging-room.message.vue b/src/client/app/common/views/components/messaging-room.message.vue
index a77b5f3658..f33173da6f 100644
--- a/src/client/app/common/views/components/messaging-room.message.vue
+++ b/src/client/app/common/views/components/messaging-room.message.vue
@@ -8,7 +8,7 @@
<img src="/assets/desktop/messaging/delete.png" alt="Delete"/>
</button>
<div class="content" v-if="!message.isDeleted">
- <mk-note-html class="text" v-if="message.text" ref="text" :text="message.text" :i="$store.state.i"/>
+ <misskey-flavored-markdown class="text" v-if="message.text" ref="text" :text="message.text" :i="$store.state.i"/>
<div class="file" v-if="message.file">
<a :href="message.file.url" target="_blank" :title="message.file.name">
<img v-if="message.file.type.split('/')[0] == 'image'" :src="message.file.url" :alt="message.file.name"/>
@@ -32,7 +32,7 @@
<script lang="ts">
import Vue from 'vue';
-import parse from '../../../../../text/parse';
+import parse from '../../../../../mfm/parse';
export default Vue.extend({
props: {
diff --git a/src/client/app/common/views/components/note-html.ts b/src/client/app/common/views/components/misskey-flavored-markdown.ts
index 8fa5f380dd..c321c76104 100644
--- a/src/client/app/common/views/components/note-html.ts
+++ b/src/client/app/common/views/components/misskey-flavored-markdown.ts
@@ -1,6 +1,6 @@
import Vue from 'vue';
import * as emojilib from 'emojilib';
-import parse from '../../../../../text/parse';
+import parse from '../../../../../mfm/parse';
import getAcct from '../../../../../acct/render';
import { url } from '../../../config';
import MkUrl from './url.vue';
@@ -10,7 +10,7 @@ const flatten = list => list.reduce(
(a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []
);
-export default Vue.component('mk-note-html', {
+export default Vue.component('misskey-flavored-markdown', {
props: {
text: {
type: String,
@@ -40,17 +40,6 @@ export default Vue.component('mk-note-html', {
ast = this.ast;
}
- if (ast.filter(x => x.type != 'hashtag').length == 0) {
- return;
- }
-
- while (ast[ast.length - 1] && (
- ast[ast.length - 1].type == 'hashtag' ||
- (ast[ast.length - 1].type == 'text' && ast[ast.length - 1].content == ' ') ||
- (ast[ast.length - 1].type == 'text' && ast[ast.length - 1].content == '\n'))) {
- ast.pop();
- }
-
// Parse ast to DOM
const els = flatten(ast.map(token => {
switch (token.type) {
diff --git a/src/client/app/common/views/components/note-header.vue b/src/client/app/common/views/components/note-header.vue
index 6e64a6a6d3..25a3339264 100644
--- a/src/client/app/common/views/components/note-header.vue
+++ b/src/client/app/common/views/components/note-header.vue
@@ -72,6 +72,7 @@ root(isDark)
> .is-admin
> .is-bot
> .is-cat
+ flex-shrink 0
align-self center
margin 0 .5em 0 0
padding 1px 6px
@@ -89,6 +90,7 @@ root(isDark)
overflow hidden
text-overflow ellipsis
color isDark ? #606984 : #ccc
+ flex-shrink 2147483647
> .info
margin-left auto
diff --git a/src/client/app/common/views/components/reaction-picker.vue b/src/client/app/common/views/components/reaction-picker.vue
index 0db6f66b37..ed7aedb58e 100644
--- a/src/client/app/common/views/components/reaction-picker.vue
+++ b/src/client/app/common/views/components/reaction-picker.vue
@@ -1,7 +1,7 @@
<template>
<div class="mk-reaction-picker">
<div class="backdrop" ref="backdrop" @click="close"></div>
- <div class="popover" :class="{ compact }" ref="popover">
+ <div class="popover" :class="{ compact, big }" ref="popover">
<p v-if="!compact">{{ title }}</p>
<div>
<button @click="react('like')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="1" title="%i18n:common.reactions.like%"><mk-reaction-icon reaction='like'/></button>
@@ -25,7 +25,28 @@ import * as anime from 'animejs';
const placeholder = '%i18n:@choose-reaction%';
export default Vue.extend({
- props: ['note', 'source', 'compact', 'cb'],
+ props: {
+ note: {
+ type: Object,
+ required: true
+ },
+ source: {
+ required: true
+ },
+ compact: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ cb: {
+ required: false
+ },
+ big: {
+ type: Boolean,
+ required: false,
+ default: false
+ }
+ },
data() {
return {
title: placeholder
@@ -162,6 +183,16 @@ root(isDark)
border-right solid $balloon-size transparent
border-bottom solid $balloon-size $bgcolor
+ &.compact
+ > div
+ width 280px
+
+ > button
+ width 50px
+ height 50px
+ font-size 28px
+ border-radius 4px
+
> p
display block
margin 0
diff --git a/src/client/app/common/views/components/reversi.game.vue b/src/client/app/common/views/components/reversi.game.vue
index dc79c95bb8..a2a6fd0dc1 100644
--- a/src/client/app/common/views/components/reversi.game.vue
+++ b/src/client/app/common/views/components/reversi.game.vue
@@ -3,24 +3,39 @@
<header><b>{{ blackUser.name }}</b>(黒) vs <b>{{ whiteUser.name }}</b>(白)</header>
<div style="overflow: hidden">
- <p class="turn" v-if="!iAmPlayer && !game.isEnded">{{ turnUser.name }}のターンです<mk-ellipsis/></p>
- <p class="turn" v-if="logPos != logs.length">{{ turnUser.name }}のターン</p>
- <p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">相手のターンです<mk-ellipsis/></p>
- <p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">あなたのターンです</p>
+ <p class="turn" v-if="!iAmPlayer && !game.isEnded">{{ '%i18n:common.reversi.turn-of%'.replace('{}', turnUser.name) }}<mk-ellipsis/></p>
+ <p class="turn" v-if="logPos != logs.length">{{ '%i18n:common.reversi.past-turn-of%'.replace('{}', turnUser.name) }}</p>
+ <p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">%i18n:common.reversi.opponent-turn%<mk-ellipsis/></p>
+ <p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">%i18n:common.reversi.my-turn%</p>
<p class="result" v-if="game.isEnded && logPos == logs.length">
<template v-if="game.winner"><b>{{ game.winner.name }}</b>の勝ち{{ game.settings.isLlotheo ? ' (ロセオ)' : '' }}</template>
- <template v-else>引き分け</template>
+ <template v-else>%i18n:common.reversi.drawn%</template>
</p>
</div>
- <div class="board" :style="{ 'grid-template-rows': `repeat(${ game.settings.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.settings.map[0].length }, 1fr)` }">
- <div v-for="(stone, i) in o.board"
- :class="{ empty: stone == null, none: o.map[i] == 'null', isEnded: game.isEnded, myTurn: !game.isEnded && isMyTurn, can: turnUser ? o.canPut(turnUser.id == blackUser.id, i) : null, prev: o.prevPos == i }"
- @click="set(i)"
- :title="'[' + (o.transformPosToXy(i)[0] + 1) + ', ' + (o.transformPosToXy(i)[1] + 1) + '] (' + i + ')'"
- >
- <img v-if="stone === true" :src="`${blackUser.avatarUrl}?thumbnail&size=128`" alt="">
- <img v-if="stone === false" :src="`${whiteUser.avatarUrl}?thumbnail&size=128`" alt="">
+ <div class="board">
+ <div class="labels-x" v-if="this.$store.state.settings.reversiBoardLabels">
+ <span v-for="i in game.settings.map[0].length">{{ String.fromCharCode(64 + i) }}</span>
+ </div>
+ <div class="flex">
+ <div class="labels-y" v-if="this.$store.state.settings.reversiBoardLabels">
+ <div v-for="i in game.settings.map.length">{{ i }}</div>
+ </div>
+ <div class="cells" :style="cellsStyle">
+ <div v-for="(stone, i) in o.board"
+ :class="{ empty: stone == null, none: o.map[i] == 'null', isEnded: game.isEnded, myTurn: !game.isEnded && isMyTurn, can: turnUser ? o.canPut(turnUser.id == blackUser.id, i) : null, prev: o.prevPos == i }"
+ @click="set(i)"
+ :title="`${String.fromCharCode(65 + o.transformPosToXy(i)[0])}${o.transformPosToXy(i)[1] + 1}`">
+ <img v-if="stone === true" :src="`${blackUser.avatarUrl}?thumbnail&size=128`" alt="">
+ <img v-if="stone === false" :src="`${whiteUser.avatarUrl}?thumbnail&size=128`" alt="">
+ </div>
+ </div>
+ <div class="labels-y" v-if="this.$store.state.settings.reversiBoardLabels">
+ <div v-for="i in game.settings.map.length">{{ i }}</div>
+ </div>
+ </div>
+ <div class="labels-x" v-if="this.$store.state.settings.reversiBoardLabels">
+ <span v-for="i in game.settings.map[0].length">{{ String.fromCharCode(64 + i) }}</span>
</div>
</div>
@@ -92,6 +107,12 @@ export default Vue.extend({
isMyTurn(): boolean {
if (this.turnUser == null) return null;
return this.turnUser.id == this.$store.state.i.id;
+ },
+ cellsStyle(): any {
+ return {
+ 'grid-template-rows': `repeat(${ this.game.settings.map.length }, 1fr)`,
+ 'grid-template-columns': `repeat(${ this.game.settings.map[0].length }, 1fr)`
+ };
}
},
@@ -244,54 +265,99 @@ export default Vue.extend({
border-bottom dashed 1px #c4cdd4
> .board
- display grid
- grid-gap 4px
- width 350px
- height 350px
+ width calc(100% - 16px)
+ max-width 500px
margin 0 auto
- > div
- background transparent
- border-radius 6px
- overflow hidden
+ $label-size = 16px
+ $gap = 4px
+
+ > .labels-x
+ height $label-size
+ padding 0 $label-size
+ display flex
+
+ > *
+ flex 1
+ display flex
+ align-items center
+ justify-content center
+ font-size 12px
+
+ &:first-child
+ margin-left -($gap / 2)
+
+ &:last-child
+ margin-right -($gap / 2)
+
+ > .flex
+ display flex
+
+ > .labels-y
+ width $label-size
+ display flex
+ flex-direction column
+
+ > *
+ flex 1
+ display flex
+ align-items center
+ justify-content center
+ font-size 12px
+
+ &:first-child
+ margin-top -($gap / 2)
+
+ &:last-child
+ margin-bottom -($gap / 2)
+
+ > .cells
+ flex 1
+ display grid
+ grid-gap $gap
+
+ > div
+ background transparent
+ border-radius 6px
+ overflow hidden
- *
- pointer-events none
- user-select none
+ *
+ pointer-events none
+ user-select none
- &.empty
- border solid 2px #eee
+ &.empty
+ border solid 2px #eee
- &.empty.can
- background #eee
+ &.empty.can
+ background #eee
- &.empty.myTurn
- border-color #ddd
+ &.empty.myTurn
+ border-color #ddd
- &.can
- background #eee
- cursor pointer
+ &.can
+ background #eee
+ cursor pointer
- &:hover
- border-color darken($theme-color, 10%)
- background $theme-color
+ &:hover
+ border-color darken($theme-color, 10%)
+ background $theme-color
- &:active
- background darken($theme-color, 10%)
+ &:active
+ background darken($theme-color, 10%)
- &.prev
- box-shadow 0 0 0 4px rgba($theme-color, 0.7)
+ &.prev
+ box-shadow 0 0 0 4px rgba($theme-color, 0.7)
- &.isEnded
- border-color #ddd
+ &.isEnded
+ border-color #ddd
- &.none
- border-color transparent !important
+ &.none
+ border-color transparent !important
- > img
- display block
- width 100%
- height 100%
+ > img
+ display block
+ width 100%
+ height 100%
> .graph
display grid
diff --git a/src/client/app/common/views/components/reversi.vue b/src/client/app/common/views/components/reversi.vue
index e4d7740bde..61705163ac 100644
--- a/src/client/app/common/views/components/reversi.vue
+++ b/src/client/app/common/views/components/reversi.vue
@@ -10,7 +10,7 @@
</div>
</div>
<div class="index" v-else>
- <h1>Misskey %fa:circle%thell%fa:circle R%</h1>
+ <h1>Misskey Reversi</h1>
<p>他のMisskeyユーザーとリバーシで対戦しよう</p>
<div class="play">
<el-button round>フリーマッチ(準備中)</el-button>
diff --git a/src/client/app/common/views/components/welcome-timeline.vue b/src/client/app/common/views/components/welcome-timeline.vue
index f3372bf062..5a8b9df476 100644
--- a/src/client/app/common/views/components/welcome-timeline.vue
+++ b/src/client/app/common/views/components/welcome-timeline.vue
@@ -13,7 +13,7 @@
</div>
</header>
<div class="text">
- <mk-note-html v-if="note.text" :text="note.text"/>
+ <misskey-flavored-markdown v-if="note.text" :text="note.text"/>
</div>
</div>
</div>
@@ -24,6 +24,13 @@
import Vue from 'vue';
export default Vue.extend({
+ props: {
+ max: {
+ type: Number,
+ required: false,
+ default: undefined
+ }
+ },
data() {
return {
fetching: true,
@@ -37,6 +44,7 @@ export default Vue.extend({
fetch(cb?) {
this.fetching = true;
(this as any).api('notes', {
+ limit: this.max,
local: true,
reply: false,
renote: false,
diff --git a/src/client/app/common/views/pages/follow.vue b/src/client/app/common/views/pages/follow.vue
new file mode 100644
index 0000000000..c8e838be85
--- /dev/null
+++ b/src/client/app/common/views/pages/follow.vue
@@ -0,0 +1,215 @@
+<template>
+<div class="syxhndwprovvuqhmyvveewmbqayniwkv" v-if="!fetching" :data-darkmode="$store.state.device.darkmode">
+ <div class="signed-in-as" v-html="'%i18n:@signed-in-as%'.replace('{}', '<b>' + myName + '</b>')"></div>
+
+ <main>
+ <div class="banner" :style="bannerStyle"></div>
+ <mk-avatar class="avatar" :user="user" :disable-preview="true"/>
+ <div class="body">
+ <router-link :to="user | userPage" class="name">{{ user | userName }}</router-link>
+ <span class="username">@{{ user | acct }}</span>
+ <div class="description">
+ <misskey-flavored-markdown v-if="user.description" :text="user.description" :i="$store.state.i"/>
+ </div>
+ </div>
+ </main>
+
+ <button
+ :class="{ wait: followWait, active: user.isFollowing || user.hasPendingFollowRequestFromYou }"
+ @click="onClick"
+ :disabled="followWait">
+ <template v-if="!followWait">
+ <template v-if="user.hasPendingFollowRequestFromYou">%fa:hourglass-half% %i18n:@request-pending%</template>
+ <template v-else-if="user.isFollowing">%fa:minus% %i18n:@following%</template>
+ <template v-else-if="!user.isFollowing && user.isLocked">%fa:plus% %i18n:@follow-request%</template>
+ <template v-else-if="!user.isFollowing && !user.isLocked">%fa:plus% %i18n:@follow%</template>
+ </template>
+ <template v-else>%fa:spinner .pulse .fw%</template>
+ </button>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import parseAcct from '../../../../../acct/parse';
+import getUserName from '../../../../../renderers/get-user-name';
+import Progress from '../../../common/scripts/loading';
+
+export default Vue.extend({
+ data() {
+ return {
+ fetching: true,
+ user: null,
+ followWait: false
+ };
+ },
+
+ computed: {
+ myName(): string {
+ return Vue.filter('userName')(this.$store.state.i);
+ },
+
+ bannerStyle(): any {
+ if (this.user.bannerUrl == null) return {};
+ return {
+ backgroundColor: this.user.bannerColor && this.user.bannerColor.length == 3 ? `rgb(${ this.user.bannerColor.join(',') })` : null,
+ backgroundImage: `url(${ this.user.bannerUrl })`
+ };
+ }
+ },
+
+ created() {
+ this.fetch();
+ },
+
+ methods: {
+ fetch() {
+ const acct = new URL(location.href).searchParams.get('acct');
+ this.fetching = true;
+ Progress.start();
+ (this as any).api('users/show', parseAcct(acct)).then(user => {
+ this.user = user;
+ this.fetching = false;
+ Progress.done();
+ document.title = getUserName(this.user) + ' | Misskey';
+ });
+ },
+
+ async onClick() {
+ this.followWait = true;
+
+ try {
+ if (this.user.isFollowing) {
+ this.user = await (this as any).api('following/delete', {
+ userId: this.user.id
+ });
+ } else {
+ if (this.user.isLocked && this.user.hasPendingFollowRequestFromYou) {
+ this.user = await (this as any).api('following/requests/cancel', {
+ userId: this.user.id
+ });
+ } else if (this.user.isLocked) {
+ this.user = await (this as any).api('following/create', {
+ userId: this.user.id
+ });
+ } else {
+ this.user = await (this as any).api('following/create', {
+ userId: this.user.id
+ });
+ }
+ }
+ } catch (e) {
+ console.error(e);
+ } finally {
+ this.followWait = false;
+ }
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+root(isDark)
+ padding 32px
+ max-width 500px
+ margin 0 auto
+ text-align center
+ color isDark ? #9baec8 : #868c8c
+
+ $bg = isDark ? #282C37 : #fff
+
+ @media (max-width 400px)
+ padding 16px
+
+ > .signed-in-as
+ margin-bottom 16px
+ font-size 14px
+ color isDark ? #9baec8 : #9daab3
+
+ > main
+ margin-bottom 16px
+ background $bg
+ border-radius 8px
+ box-shadow 0 4px 12px rgba(#000, 0.1)
+ overflow hidden
+
+ > .banner
+ height 128px
+ background-position center
+ background-size cover
+
+ > .avatar
+ display block
+ margin -50px auto 0 auto
+ width 100px
+ height 100px
+ border-radius 100%
+ border solid 4px $bg
+
+ > .body
+ padding 4px 32px 32px 32px
+
+ @media (max-width 400px)
+ padding 4px 16px 16px 16px
+
+ > .name
+ font-size 20px
+ font-weight bold
+
+ > .username
+ display block
+ opacity 0.7
+
+ > .description
+ margin-top 16px
+
+ > button
+ display block
+ user-select none
+ cursor pointer
+ padding 10px 16px
+ margin 0
+ width 100%
+ min-width 150px
+ font-size 14px
+ font-weight bold
+ color $theme-color
+ background transparent
+ outline none
+ border solid 1px $theme-color
+ border-radius 36px
+
+ &:hover
+ background rgba($theme-color, 0.1)
+
+ &:active
+ background rgba($theme-color, 0.2)
+
+ &.active
+ color $theme-color-foreground
+ background $theme-color
+
+ &:hover
+ background lighten($theme-color, 10%)
+ border-color lighten($theme-color, 10%)
+
+ &:active
+ background darken($theme-color, 10%)
+ border-color darken($theme-color, 10%)
+
+ &.wait
+ cursor wait !important
+ opacity 0.7
+
+ *
+ pointer-events none
+
+.syxhndwprovvuqhmyvveewmbqayniwkv[data-darkmode]
+ root(true)
+
+.syxhndwprovvuqhmyvveewmbqayniwkv:not([data-darkmode])
+ root(false)
+
+</style>
diff --git a/src/client/app/common/views/widgets/hashtags.vue b/src/client/app/common/views/widgets/hashtags.vue
index 9ab855d927..2065bd407c 100644
--- a/src/client/app/common/views/widgets/hashtags.vue
+++ b/src/client/app/common/views/widgets/hashtags.vue
@@ -6,7 +6,9 @@
<div class="mkw-hashtags--body" :data-mobile="platform == 'mobile'">
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
<p class="empty" v-else-if="stats.length == 0">%fa:exclamation-circle%%i18n:@empty%</p>
- <transition-group v-else tag="div" name="chart">
+ <!-- トランジションを有効にするとなぜかメモリリークする -->
+ <!-- <transition-group v-else tag="div" name="chart"> -->
+ <div>
<div v-for="stat in stats" :key="stat.tag">
<div class="tag">
<router-link :to="`/tags/${ stat.tag }`" :title="stat.tag">#{{ stat.tag }}</router-link>
@@ -14,7 +16,8 @@
</div>
<x-chart class="chart" :src="stat.chart"/>
</div>
- </transition-group>
+ </div>
+ <!-- </transition-group> -->
</div>
</mk-widget-container>
</div>
diff --git a/src/client/app/desktop/script.ts b/src/client/app/desktop/script.ts
index 201ab0a83d..297100e0e0 100644
--- a/src/client/app/desktop/script.ts
+++ b/src/client/app/desktop/script.ts
@@ -36,6 +36,7 @@ import MkSearch from './views/pages/search.vue';
import MkTag from './views/pages/tag.vue';
import MkReversi from './views/pages/reversi.vue';
import MkShare from './views/pages/share.vue';
+import MkFollow from '../common/views/pages/follow.vue';
/**
* init
@@ -67,7 +68,8 @@ init(async (launch) => {
{ path: '/reversi', component: MkReversi },
{ path: '/reversi/:game', component: MkReversi },
{ path: '/@:user', component: MkUser },
- { path: '/notes/:note', component: MkNote }
+ { path: '/notes/:note', component: MkNote },
+ { path: '/authorize-follow', component: MkFollow }
]
});
@@ -115,26 +117,8 @@ function registerNotifications(stream: HomeStreamManager) {
});
function attach(connection) {
- connection.on('drive_file_created', file => {
- const _n = composeNotification('drive_file_created', file);
- const n = new Notification(_n.title, {
- body: _n.body,
- icon: _n.icon
- });
- setTimeout(n.close.bind(n), 5000);
- });
-
- connection.on('mention', note => {
- const _n = composeNotification('mention', note);
- const n = new Notification(_n.title, {
- body: _n.body,
- icon: _n.icon
- });
- setTimeout(n.close.bind(n), 6000);
- });
-
- connection.on('reply', note => {
- const _n = composeNotification('reply', note);
+ connection.on('notification', notification => {
+ const _n = composeNotification('notification', notification);
const n = new Notification(_n.title, {
body: _n.body,
icon: _n.icon
@@ -142,13 +126,13 @@ function registerNotifications(stream: HomeStreamManager) {
setTimeout(n.close.bind(n), 6000);
});
- connection.on('quote', note => {
- const _n = composeNotification('quote', note);
+ connection.on('drive_file_created', file => {
+ const _n = composeNotification('drive_file_created', file);
const n = new Notification(_n.title, {
body: _n.body,
icon: _n.icon
});
- setTimeout(n.close.bind(n), 6000);
+ setTimeout(n.close.bind(n), 5000);
});
connection.on('unread_messaging_message', message => {
diff --git a/src/client/app/desktop/views/components/home.vue b/src/client/app/desktop/views/components/home.vue
index ba48ce24e8..ab276d3c4b 100644
--- a/src/client/app/desktop/views/components/home.vue
+++ b/src/client/app/desktop/views/components/home.vue
@@ -66,7 +66,7 @@
</div>
<div class="main">
<mk-post-form class="form" v-if="$store.state.settings.showPostFormOnTopOfTl"/>
- <mk-timeline class="tl" cref="tl" @loaded="onTlLoaded" v-if="mode == 'timeline'"/>
+ <mk-timeline class="tl" ref="tl" @loaded="onTlLoaded" v-if="mode == 'timeline'"/>
</div>
</template>
</div>
diff --git a/src/client/app/desktop/views/components/note-detail.vue b/src/client/app/desktop/views/components/note-detail.vue
index 4b5e5bebdf..e3d16d7056 100644
--- a/src/client/app/desktop/views/components/note-detail.vue
+++ b/src/client/app/desktop/views/components/note-detail.vue
@@ -40,16 +40,13 @@
<div class="text">
<span v-if="p.isHidden" style="opacity: 0.5">%i18n:@private%</span>
<span v-if="p.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
- <mk-note-html v-if="p.text" :text="p.text" :i="$store.state.i"/>
+ <misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i"/>
</div>
<div class="media" v-if="p.media.length > 0">
<mk-media-list :media-list="p.media" :raw="true"/>
</div>
<mk-poll v-if="p.poll" :note="p"/>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
- <div class="tags" v-if="p.tags && p.tags.length > 0">
- <router-link v-for="tag in p.tags" :key="tag" :to="`/tags/${tag}`">{{ tag }}</router-link>
- </div>
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
<div class="map" v-if="p.geo" ref="map"></div>
<div class="renote" v-if="p.renote">
@@ -83,7 +80,7 @@
<script lang="ts">
import Vue from 'vue';
import dateStringify from '../../../common/scripts/date-stringify';
-import parse from '../../../../../text/parse';
+import parse from '../../../../../mfm/parse';
import MkPostFormWindow from './post-form-window.vue';
import MkRenoteFormWindow from './renote-form-window.vue';
@@ -363,35 +360,6 @@ root(isDark)
> .mk-url-preview
margin-top 8px
- > .tags
- margin 4px 0 0 0
-
- > *
- display inline-block
- margin 0 8px 0 0
- padding 2px 8px 2px 16px
- font-size 90%
- color #8d969e
- background #edf0f3
- border-radius 4px
-
- &:before
- content ""
- display block
- position absolute
- top 0
- bottom 0
- left 4px
- width 8px
- height 8px
- margin auto 0
- background #fff
- border-radius 100%
-
- &:hover
- text-decoration none
- background #e2e7ec
-
> footer
font-size 1.2em
diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue
index ee11fcc55f..b8aff2d86e 100644
--- a/src/client/app/desktop/views/components/notes.note.vue
+++ b/src/client/app/desktop/views/components/notes.note.vue
@@ -25,16 +25,13 @@
<span v-if="p.isHidden" style="opacity: 0.5">%i18n:@private%</span>
<span v-if="p.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
<a class="reply" v-if="p.reply">%fa:reply%</a>
- <mk-note-html v-if="p.text && !canHideText(p)" :text="p.text" :i="$store.state.i" :class="$style.text"/>
+ <misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i" :class="$style.text"/>
<a class="rp" v-if="p.renote">RP:</a>
</div>
<div class="media" v-if="p.media.length > 0">
<mk-media-list :media-list="p.media"/>
</div>
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
- <div class="tags" v-if="p.tags && p.tags.length > 0">
- <router-link v-for="tag in p.tags" :key="tag" :to="`/tags/${tag}`">{{ tag }}</router-link>
- </div>
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
<div class="map" v-if="p.geo" ref="map"></div>
<div class="renote" v-if="p.renote">
@@ -75,8 +72,7 @@
<script lang="ts">
import Vue from 'vue';
import dateStringify from '../../../common/scripts/date-stringify';
-import canHideText from '../../../common/scripts/can-hide-text';
-import parse from '../../../../../text/parse';
+import parse from '../../../../../mfm/parse';
import MkPostFormWindow from './post-form-window.vue';
import MkRenoteFormWindow from './renote-form-window.vue';
@@ -190,8 +186,6 @@ export default Vue.extend({
},
methods: {
- canHideText,
-
capture(withHandler = false) {
if (this.$store.getters.isSignedIn) {
this.connection.send({
@@ -468,35 +462,6 @@ root(isDark)
&:empty
display none
- > .tags
- margin 4px 0 0 0
-
- > *
- display inline-block
- margin 0 8px 0 0
- padding 2px 8px 2px 16px
- font-size 90%
- color #8d969e
- background isDark ? #313543 : #edf0f3
- border-radius 4px
-
- &:before
- content ""
- display block
- position absolute
- top 0
- bottom 0
- left 4px
- width 8px
- height 8px
- margin auto 0
- background isDark ? #282c37 : #fff
- border-radius 100%
-
- &:hover
- text-decoration none
- background #e2e7ec
-
.mk-url-preview
margin-top 8px
diff --git a/src/client/app/desktop/views/components/notes.vue b/src/client/app/desktop/views/components/notes.vue
index 69f3739f79..1206eb7136 100644
--- a/src/client/app/desktop/views/components/notes.vue
+++ b/src/client/app/desktop/views/components/notes.vue
@@ -9,7 +9,9 @@
<button @click="resolveInitPromise">%i18n:@retry%</button>
</div>
- <transition-group name="mk-notes" class="transition">
+ <!-- トランジションを有効にするとなぜかメモリリークする -->
+ <!--<transition-group name="mk-notes" class="transition">-->
+ <div class="notes">
<template v-for="(note, i) in _notes">
<x-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)"/>
<p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
@@ -17,7 +19,8 @@
<span>%fa:angle-down%{{ _notes[i + 1]._datetext }}</span>
</p>
</template>
- </transition-group>
+ </div>
+ <!--</transition-group>-->
<footer v-if="more">
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
@@ -221,6 +224,7 @@ root(isDark)
> *
transition transform .3s ease, opacity .3s ease
+ > .notes
> .date
display block
margin 0
diff --git a/src/client/app/desktop/views/components/notifications.vue b/src/client/app/desktop/views/components/notifications.vue
index e479ffadbf..32b36994db 100644
--- a/src/client/app/desktop/views/components/notifications.vue
+++ b/src/client/app/desktop/views/components/notifications.vue
@@ -1,7 +1,9 @@
<template>
<div class="mk-notifications">
<div class="notifications" v-if="notifications.length != 0">
- <transition-group name="mk-notifications" class="transition">
+ <!-- トランジションを有効にするとなぜかメモリリークする -->
+ <!-- <transition-group name="mk-notifications" class="transition"> -->
+ <div>
<template v-for="(notification, i) in _notifications">
<div class="notification" :class="notification.type" :key="notification.id">
<mk-time :time="notification.createdAt"/>
@@ -95,7 +97,8 @@
<span>%fa:angle-down%{{ _notifications[i + 1]._datetext }}</span>
</p>
</template>
- </transition-group>
+ </div>
+ <!-- </transition-group> -->
</div>
<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }}
@@ -201,7 +204,7 @@ root(isDark)
transition transform .3s ease, opacity .3s ease
> .notifications
- > *
+ > div
> .notification
margin 0
padding 16px
diff --git a/src/client/app/desktop/views/components/post-form.vue b/src/client/app/desktop/views/components/post-form.vue
index 33f2288e06..3832e5b38c 100644
--- a/src/client/app/desktop/views/components/post-form.vue
+++ b/src/client/app/desktop/views/components/post-form.vue
@@ -49,7 +49,7 @@ import Vue from 'vue';
import * as XDraggable from 'vuedraggable';
import getKao from '../../../common/scripts/get-kao';
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
-import parse from '../../../../../text/parse';
+import parse from '../../../../../mfm/parse';
import { host } from '../../../config';
export default Vue.extend({
diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue
index 536d270dce..74ab45626d 100644
--- a/src/client/app/desktop/views/components/settings.vue
+++ b/src/client/app/desktop/views/components/settings.vue
@@ -54,6 +54,7 @@
<mk-switch v-model="$store.state.settings.showMaps" @change="onChangeShowMaps" text="%i18n:@show-maps%">
<span>%i18n:@show-maps-desc%</span>
</mk-switch>
+ <mk-switch v-model="$store.state.settings.reversiBoardLabels" @change="onChangeReversiBoardLabels" text="%i18n:common.show-reversi-board-labels%"/>
</section>
<section class="web" v-show="page == 'web'">
@@ -369,6 +370,12 @@ export default Vue.extend({
value: v
});
},
+ onChangeReversiBoardLabels(v) {
+ this.$store.dispatch('settings/set', {
+ key: 'reversiBoardLabels',
+ value: v
+ });
+ },
onChangeGradientWindowHeader(v) {
this.$store.dispatch('settings/set', {
key: 'gradientWindowHeader',
diff --git a/src/client/app/desktop/views/components/sub-note-content.vue b/src/client/app/desktop/views/components/sub-note-content.vue
index 45ce6a6f8f..cb0374b910 100644
--- a/src/client/app/desktop/views/components/sub-note-content.vue
+++ b/src/client/app/desktop/views/components/sub-note-content.vue
@@ -4,7 +4,7 @@
<span v-if="note.isHidden" style="opacity: 0.5">%i18n:@private%</span>
<span v-if="note.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
<a class="reply" v-if="note.replyId">%fa:reply%</a>
- <mk-note-html v-if="note.text" :text="note.text" :i="$store.state.i"/>
+ <misskey-flavored-markdown v-if="note.text" :text="note.text" :i="$store.state.i"/>
<a class="rp" v-if="note.renoteId" :href="`/notes/${note.renoteId}`">RP: ...</a>
</div>
<details v-if="note.media.length > 0">
diff --git a/src/client/app/desktop/views/components/ui.header.vue b/src/client/app/desktop/views/components/ui.header.vue
index 7045790054..aa7c3ac44d 100644
--- a/src/client/app/desktop/views/components/ui.header.vue
+++ b/src/client/app/desktop/views/components/ui.header.vue
@@ -4,7 +4,7 @@
<div class="main" ref="main">
<div class="backdrop"></div>
<div class="main">
- <p ref="welcomeback" v-if="$store.getters.isSignedIn">おかえりなさい、<b>{{ $store.state.i | userName }}</b>さん</p>
+ <p ref="welcomeback" v-if="$store.getters.isSignedIn">%i18n:@welcome-back%<b>{{ $store.state.i | userName }}</b>さん</p>
<div class="container" ref="mainContainer">
<div class="left">
<x-nav/>
diff --git a/src/client/app/desktop/views/pages/deck/deck.note.vue b/src/client/app/desktop/views/pages/deck/deck.note.vue
index 5a8dc2ea65..c7df715a05 100644
--- a/src/client/app/desktop/views/pages/deck/deck.note.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.note.vue
@@ -25,16 +25,13 @@
<span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
<span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
<a class="reply" v-if="p.reply">%fa:reply%</a>
- <mk-note-html v-if="p.text && !canHideText(p)" :text="p.text" :i="$store.state.i"/>
+ <misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i"/>
<a class="rp" v-if="p.renote != null">RP:</a>
</div>
<div class="media" v-if="p.media.length > 0">
<mk-media-list :media-list="p.media"/>
</div>
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
- <div class="tags" v-if="p.tags && p.tags.length > 0">
- <router-link v-for="tag in p.tags" :key="tag" :to="`/tags/${tag}`">{{ tag }}</router-link>
- </div>
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
<div class="renote" v-if="p.renote">
<mk-note-preview :note="p.renote" :mini="true"/>
@@ -67,8 +64,7 @@
<script lang="ts">
import Vue from 'vue';
-import parse from '../../../../../../text/parse';
-import canHideText from '../../../../common/scripts/can-hide-text';
+import parse from '../../../../../../mfm/parse';
import MkNoteMenu from '../../../../common/views/components/note-menu.vue';
import MkReactionPicker from '../../../../common/views/components/reaction-picker.vue';
@@ -148,8 +144,6 @@ export default Vue.extend({
},
methods: {
- canHideText,
-
capture(withHandler = false) {
if (this.$store.getters.isSignedIn) {
this.connection.send({
@@ -376,31 +370,6 @@ root(isDark)
.mk-url-preview
margin-top 8px
- > .tags
- margin 4px 0 0 0
-
- > *
- display inline-block
- margin 0 8px 0 0
- padding 2px 8px 2px 16px
- font-size 90%
- color #8d969e
- background isDark ? #313543 : #edf0f3
- border-radius 4px
-
- &:before
- content ""
- display block
- position absolute
- top 0
- bottom 0
- left 4px
- width 8px
- height 8px
- margin auto 0
- background isDark ? #282c37 : #fff
- border-radius 100%
-
> .media
> img
display block
diff --git a/src/client/app/desktop/views/pages/deck/deck.notes.vue b/src/client/app/desktop/views/pages/deck/deck.notes.vue
index 8862b0e0fc..a5ed45b64c 100644
--- a/src/client/app/desktop/views/pages/deck/deck.notes.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.notes.vue
@@ -7,7 +7,9 @@
<button @click="resolveInitPromise">%i18n:@retry%</button>
</div>
- <transition-group name="mk-notes" class="transition">
+ <!-- トランジションを有効にするとなぜかメモリリークする -->
+ <!--<transition-group name="mk-notes" class="transition">-->
+ <div class="notes">
<template v-for="(note, i) in _notes">
<x-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)" :media-view="mediaView"/>
<p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
@@ -15,7 +17,8 @@
<span>%fa:angle-down%{{ _notes[i + 1]._datetext }}</span>
</p>
</template>
- </transition-group>
+ </div>
+ <!--</transition-group>-->
<footer v-if="more">
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
@@ -198,6 +201,7 @@ root(isDark)
> *
transition transform .3s ease, opacity .3s ease
+ > .notes
> .date
display block
margin 0
diff --git a/src/client/app/desktop/views/pages/deck/deck.notifications.vue b/src/client/app/desktop/views/pages/deck/deck.notifications.vue
index f54ad1a3cd..10c06b0ad2 100644
--- a/src/client/app/desktop/views/pages/deck/deck.notifications.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.notifications.vue
@@ -1,6 +1,8 @@
<template>
<div class="oxynyeqmfvracxnglgulyqfgqxnxmehl">
- <transition-group name="mk-notifications" class="transition notifications">
+ <!-- トランジションを有効にするとなぜかメモリリークする -->
+ <!--<transition-group name="mk-notifications" class="transition notifications">-->
+ <div class="notifications">
<template v-for="(notification, i) in _notifications">
<x-notification class="notification" :notification="notification" :key="notification.id"/>
<p class="date" v-if="i != notifications.length - 1 && notification._date != _notifications[i + 1]._date" :key="notification.id + '-time'">
@@ -8,7 +10,8 @@
<span>%fa:angle-down%{{ _notifications[i + 1]._datetext }}</span>
</p>
</template>
- </transition-group>
+ </div>
+ <!--</transition-group>-->
<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }}
</button>
diff --git a/src/client/app/desktop/views/pages/user/user.header.vue b/src/client/app/desktop/views/pages/user/user.header.vue
index d52c6b762c..00545723e2 100644
--- a/src/client/app/desktop/views/pages/user/user.header.vue
+++ b/src/client/app/desktop/views/pages/user/user.header.vue
@@ -1,27 +1,37 @@
<template>
<div class="header" :data-is-dark-background="user.bannerUrl != null">
- <div class="is-suspended" v-if="user.isSuspended"><p>%fa:exclamation-triangle% %i18n:@is-suspended%</p></div>
- <div class="is-remote" v-if="user.host != null"><p>%fa:exclamation-triangle% %i18n:@is-remote%<a :href="user.url || user.uri" target="_blank">%i18n:@view-remote%</a></p></div>
<div class="banner-container" :style="style">
<div class="banner" ref="banner" :style="style" @click="onBannerClick"></div>
<div class="fade"></div>
- </div>
- <div class="container">
- <mk-avatar class="avatar" :user="user" :disable-preview="true"/>
<div class="title">
<p class="name">{{ user | userName }}</p>
- <p class="username"><mk-acct :user="user"/></p>
- <p class="location" v-if="user.host === null && user.profile.location">%fa:map-marker%{{ user.profile.location }}</p>
+ <div>
+ <span class="username"><mk-acct :user="user"/></span>
+ <span v-if="user.isBot" title="%i18n:@is-bot%">%fa:robot%</span>
+ <span class="location" v-if="user.host === null && user.profile.location">%fa:map-marker% {{ user.profile.location }}</span>
+ <span class="birthday" v-if="user.host === null && user.profile.birthday">%fa:birthday-cake% {{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' }} ({{ age }}歳)</span>
+ </div>
+ </div>
+ </div>
+ <mk-avatar class="avatar" :user="user" :disable-preview="true"/>
+ <div class="body">
+ <div class="description">
+ <misskey-flavored-markdown v-if="user.description" :text="user.description" :i="$store.state.i"/>
+ </div>
+ <div class="status">
+ <span class="notes-count"><b>{{ user.notesCount | number }}</b>%i18n:@posts%</span>
+ <span class="following clickable" @click="showFollowing"><b>{{ user.followingCount | number }}</b>%i18n:@following%</span>
+ <span class="followers clickable" @click="showFollowers"><b>{{ user.followersCount | number }}</b>%i18n:@followers%</span>
</div>
- <footer>
- <router-link :to="user | userPage" :data-active="$parent.page == 'home'">%fa:home%ホーム</router-link>
- </footer>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
+import MkFollowingWindow from '../../components/following-window.vue';
+import MkFollowersWindow from '../../components/followers-window.vue';
+import * as age from 's-age';
export default Vue.extend({
props: ['user'],
@@ -32,20 +42,24 @@ export default Vue.extend({
backgroundColor: this.user.bannerColor && this.user.bannerColor.length == 3 ? `rgb(${ this.user.bannerColor.join(',') })` : null,
backgroundImage: `url(${ this.user.bannerUrl })`
};
+ },
+
+ age(): number {
+ return age(this.user.profile.birthday);
}
},
mounted() {
if (this.user.bannerUrl) {
- window.addEventListener('load', this.onScroll);
- window.addEventListener('scroll', this.onScroll, { passive: true });
- window.addEventListener('resize', this.onScroll);
+ //window.addEventListener('load', this.onScroll);
+ //window.addEventListener('scroll', this.onScroll, { passive: true });
+ //window.addEventListener('resize', this.onScroll);
}
},
beforeDestroy() {
if (this.user.bannerUrl) {
- window.removeEventListener('load', this.onScroll);
- window.removeEventListener('scroll', this.onScroll);
- window.removeEventListener('resize', this.onScroll);
+ //window.removeEventListener('load', this.onScroll);
+ //window.removeEventListener('scroll', this.onScroll);
+ //window.removeEventListener('resize', this.onScroll);
}
},
methods: {
@@ -68,7 +82,19 @@ export default Vue.extend({
(this as any).apis.updateBanner().then(i => {
this.user.bannerUrl = i.bannerUrl;
});
- }
+ },
+
+ showFollowing() {
+ (this as any).os.new(MkFollowingWindow, {
+ user: this.user
+ });
+ },
+
+ showFollowers() {
+ (this as any).os.new(MkFollowersWindow, {
+ user: this.user
+ });
+ },
}
});
</script>
@@ -76,31 +102,11 @@ export default Vue.extend({
<style lang="stylus" scoped>
@import '~const.styl'
-.header
- $footer-height = 58px
-
+root(isDark)
+ background isDark ? #282C37 : #fff
+ border 1px solid rgba(#000, 0.075)
+ border-radius 6px
overflow hidden
- background #f7f7f7
- box-shadow 0 1px 1px rgba(#000, 0.075)
-
- > .is-suspended
- > .is-remote
- &.is-suspended
- color #570808
- background #ffdbdb
-
- &.is-remote
- color #573c08
- background #fff0db
-
- > p
- margin 0 auto
- padding 14px 16px
- max-width 1200px
- font-size 14px
-
- > a
- font-weight bold
&[data-is-dark-background]
> .banner-container
@@ -110,7 +116,6 @@ export default Vue.extend({
> .fade
background linear-gradient(transparent, rgba(#000, 0.7))
- > .container
> .title
color #fff
@@ -118,7 +123,7 @@ export default Vue.extend({
text-shadow 0 0 8px #000
> .banner-container
- height 320px
+ height 250px
overflow hidden
background-size cover
background-position center
@@ -136,83 +141,75 @@ export default Vue.extend({
width 100%
height 78px
- > .container
- max-width 1200px
- margin 0 auto
-
- > .avatar
- display block
- position absolute
- bottom 16px
- left 16px
- z-index 2
- width 160px
- height 160px
- border solid 3px #fff
- border-radius 8px
- box-shadow 1px 1px 3px rgba(#000, 0.2)
-
> .title
position absolute
- bottom $footer-height
+ bottom 0
left 0
width 100%
- padding 0 0 8px 195px
+ padding 0 0 8px 154px
color #5e6367
- font-family '游ゴシック', 'YuGothic', 'ヒラギノ角ゴ ProN W3', 'Hiragino Kaku Gothic ProN', 'Meiryo', 'メイリオ', sans-serif
> .name
display block
margin 0
- line-height 40px
+ line-height 32px
font-weight bold
- font-size 2em
+ font-size 1.8em
- > .username
- > .location
- display inline-block
- margin 0 16px 0 0
- line-height 20px
- opacity 0.8
+ > div
+ > *
+ display inline-block
+ margin-right 16px
+ line-height 20px
+ opacity 0.8
- > i
- margin-right 4px
+ &.username
+ font-weight bold
+
+ > .avatar
+ display block
+ position absolute
+ top 170px
+ left 16px
+ z-index 2
+ width 120px
+ height 120px
+ box-shadow 1px 1px 3px rgba(#000, 0.2)
- > footer
- z-index 1
- height $footer-height
- padding-left 195px
+ > .body
+ padding 16px 16px 16px 154px
+ color isDark ? #c5ced6 : #555
- > a
+ > .status
+ margin-top 16px
+ padding-top 16px
+ border-top solid 1px rgba(#000, isDark ? 0.2 : 0.1)
+ font-size 80%
+
+ > *
display inline-block
- margin 0
- padding 0 16px
- height $footer-height
- line-height $footer-height
- color #555
+ padding-right 16px
+ margin-right 16px
- &[data-active]
- border-bottom solid 4px $theme-color
+ &:not(:last-child)
+ border-right solid 1px rgba(#000, isDark ? 0.2 : 0.1)
- > i
- margin-right 6px
+ &.clickable
+ cursor pointer
- > button
- display block
- position absolute
- top 0
- right 0
- margin 8px
- padding 0
- width $footer-height - 16px
- line-height $footer-height - 16px - 2px
- font-size 1.2em
- color #777
- border solid 1px #eee
- border-radius 4px
+ &:hover
+ color isDark ? #fff : #000
+
+ > b
+ margin-right 4px
+ font-size 1rem
+ font-weight bold
+ color $theme-color
+
+.header[data-darkmode]
+ root(true)
- &:hover
- color #555
- border solid 1px #ddd
+.header:not([data-darkmode])
+ root(false)
</style>
diff --git a/src/client/app/desktop/views/pages/user/user.home.vue b/src/client/app/desktop/views/pages/user/user.home.vue
deleted file mode 100644
index afaf97dc9e..0000000000
--- a/src/client/app/desktop/views/pages/user/user.home.vue
+++ /dev/null
@@ -1,103 +0,0 @@
-<template>
-<div class="home">
- <div>
- <div ref="left">
- <x-profile :user="user"/>
- <x-photos :user="user"/>
- <x-followers-you-know v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
- <p v-if="user.host === null">%i18n:@last-used-at%: <b><mk-time :time="user.lastUsedAt"/></b></p>
- </div>
- </div>
- <main>
- <mk-note-detail v-if="user.pinnedNote" :note="user.pinnedNote" :compact="true"/>
- <x-timeline class="timeline" ref="tl" :user="user"/>
- </main>
- <div>
- <div ref="right">
- <mk-calendar @chosen="warp" :start="new Date(user.createdAt)"/>
- <mk-activity :user="user"/>
- <x-friends :user="user"/>
- <div class="nav"><mk-nav/></div>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import XTimeline from './user.timeline.vue';
-import XProfile from './user.profile.vue';
-import XPhotos from './user.photos.vue';
-import XFollowersYouKnow from './user.followers-you-know.vue';
-import XFriends from './user.friends.vue';
-
-export default Vue.extend({
- components: {
- XTimeline,
- XProfile,
- XPhotos,
- XFollowersYouKnow,
- XFriends
- },
- props: ['user'],
- methods: {
- warp(date) {
- (this.$refs.tl as any).warp(date);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.home
- display flex
- justify-content center
- margin 0 auto
- max-width 1200px
-
- > main
- > div > div
- > *:not(:last-child)
- margin-bottom 16px
-
- > main
- padding 16px
- width calc(100% - 275px * 2)
-
- > .timeline
- border solid 1px rgba(#000, 0.075)
- border-radius 6px
-
- > div
- width 275px
- margin 0
-
- &:first-child > div
- padding 16px 0 16px 16px
-
- > p
- display block
- margin 0
- padding 0 12px
- text-align center
- font-size 0.8em
- color #aaa
-
- &:last-child > div
- padding 16px 16px 16px 0
-
- > .nav
- padding 16px
- font-size 12px
- color #aaa
- background #fff
- border solid 1px rgba(#000, 0.075)
- border-radius 6px
-
- a
- color #999
-
- i
- color #ccc
-
-</style>
diff --git a/src/client/app/desktop/views/pages/user/user.profile.vue b/src/client/app/desktop/views/pages/user/user.profile.vue
index 5aa08f7c85..0134d6f0be 100644
--- a/src/client/app/desktop/views/pages/user/user.profile.vue
+++ b/src/client/app/desktop/views/pages/user/user.profile.vue
@@ -15,48 +15,17 @@
</button>
<button class="mute ui" @click="list">%fa:list% リストに追加</button>
</div>
- <div class="description" v-if="user.description">{{ user.description }}</div>
- <div class="birthday" v-if="user.host === null && user.profile.birthday">
- <p>%fa:birthday-cake%{{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' }} ({{ age }}歳)</p>
- </div>
- <div class="twitter" v-if="user.host === null && user.twitter">
- <p>%fa:B twitter%<a :href="`https://twitter.com/${user.twitter.screenName}`" target="_blank">@{{ user.twitter.screenName }}</a></p>
- </div>
- <div class="status">
- <p class="notes-count">%fa:angle-right%<a>{{ user.notesCount }}</a><b>投稿</b></p>
- <p class="following">%fa:angle-right%<a @click="showFollowing">{{ user.followingCount }}</a>人を<b>フォロー</b></p>
- <p class="followers">%fa:angle-right%<a @click="showFollowers">{{ user.followersCount }}</a>人の<b>フォロワー</b></p>
- </div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
-import * as age from 's-age';
-import MkFollowingWindow from '../../components/following-window.vue';
-import MkFollowersWindow from '../../components/followers-window.vue';
import MkUserListsWindow from '../../components/user-lists-window.vue';
export default Vue.extend({
props: ['user'],
- computed: {
- age(): number {
- return age(this.user.profile.birthday);
- }
- },
- methods: {
- showFollowing() {
- (this as any).os.new(MkFollowingWindow, {
- user: this.user
- });
- },
-
- showFollowers() {
- (this as any).os.new(MkFollowersWindow, {
- user: this.user
- });
- },
+ methods: {
stalk() {
(this as any).api('following/stalk', {
userId: this.user.id
@@ -116,8 +85,8 @@ export default Vue.extend({
</script>
<style lang="stylus" scoped>
-.profile
- background #fff
+root(isDark)
+ background isDark ? #282C37 : #fff
border solid 1px rgba(#000, 0.075)
border-radius 6px
@@ -127,7 +96,7 @@ export default Vue.extend({
> .friend-form
padding 16px
text-align center
- border-top solid 1px #eee
+ border-bottom solid 1px isDark ? #21242f : #eee
> .followed
margin 12px 0 0 0
@@ -145,7 +114,7 @@ export default Vue.extend({
> .action-form
padding 16px
text-align center
- border-top solid 1px #eee
+ border-bottom solid 1px isDark ? #21242f : #eee
> *
width 100%
@@ -153,43 +122,10 @@ export default Vue.extend({
&:not(:last-child)
margin-bottom 12px
- > .description
- padding 16px
- color #555
- border-top solid 1px #eee
-
- > .birthday
- padding 16px
- color #555
- border-top solid 1px #eee
-
- > p
- margin 0
-
- > i
- margin-right 8px
-
- > .twitter
- padding 16px
- color #555
- border-top solid 1px #eee
-
- > p
- margin 0
-
- > i
- margin-right 8px
-
- > .status
- padding 16px
- color #555
- border-top solid 1px #eee
-
- > p
- margin 8px 0
+.profile[data-darkmode]
+ root(true)
- > i
- margin-left 8px
- margin-right 8px
+.profile:not([data-darkmode])
+ root(false)
</style>
diff --git a/src/client/app/desktop/views/pages/user/user.timeline.vue b/src/client/app/desktop/views/pages/user/user.timeline.vue
index 812b5b4229..67987fcb94 100644
--- a/src/client/app/desktop/views/pages/user/user.timeline.vue
+++ b/src/client/app/desktop/views/pages/user/user.timeline.vue
@@ -1,9 +1,9 @@
<template>
-<div class="timeline">
+<div class="oh5y2r7l5lx8j6jj791ykeiwgihheguk">
<header>
- <span :data-active="mode == 'default'" @click="mode = 'default'">%i18n:@default%</span>
- <span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'">%i18n:@with-replies%</span>
- <span :data-active="mode == 'with-media'" @click="mode = 'with-media'">%i18n:@with-media%</span>
+ <span :data-active="mode == 'default'" @click="mode = 'default'">%fa:comment-alt R% %i18n:@default%</span>
+ <span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'">%fa:comments% %i18n:@with-replies%</span>
+ <span :data-active="mode == 'with-media'" @click="mode = 'with-media'">%fa:images% %i18n:@with-media%</span>
</header>
<div class="loading" v-if="fetching">
<mk-ellipsis-icon/>
@@ -114,25 +114,44 @@ export default Vue.extend({
<style lang="stylus" scoped>
@import '~const.styl'
-.timeline
- background #fff
+root(isDark)
+ background isDark ? #282C37 : #fff
> header
- padding 8px 16px
- border-bottom solid 1px #eee
+ padding 0 8px
+ z-index 10
+ background isDark ? #313543 : #fff
+ border-radius 6px 6px 0 0
+ box-shadow 0 1px isDark ? rgba(#000, 0.15) : rgba(#000, 0.08)
> span
- margin-right 16px
- line-height 27px
- font-size 18px
- color #555
+ display inline-block
+ padding 0 10px
+ line-height 42px
+ font-size 12px
+ user-select none
- &:not([data-active])
+ &[data-active]
color $theme-color
+ cursor default
+ font-weight bold
+
+ &:before
+ content ""
+ display block
+ position absolute
+ bottom 0
+ left -8px
+ width calc(100% + 16px)
+ height 2px
+ background $theme-color
+
+ &:not([data-active])
+ color isDark ? #9aa2a7 : #6f7477
cursor pointer
&:hover
- text-decoration underline
+ color isDark ? #d9dcde : #525a5f
> .loading
padding 64px 0
@@ -151,4 +170,10 @@ export default Vue.extend({
font-size 3em
color #ccc
+.oh5y2r7l5lx8j6jj791ykeiwgihheguk[data-darkmode]
+ root(true)
+
+.oh5y2r7l5lx8j6jj791ykeiwgihheguk:not([data-darkmode])
+ root(false)
+
</style>
diff --git a/src/client/app/desktop/views/pages/user/user.twitter.vue b/src/client/app/desktop/views/pages/user/user.twitter.vue
new file mode 100644
index 0000000000..228ce1de96
--- /dev/null
+++ b/src/client/app/desktop/views/pages/user/user.twitter.vue
@@ -0,0 +1,26 @@
+<template>
+<div class="adsvaidqfznoartcbplullnejvxjphcn">
+ <span>%fa:B twitter%<a :href="`https://twitter.com/${user.twitter.screenName}`" target="_blank">@{{ user.twitter.screenName }}</a></span>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+
+export default Vue.extend({
+ props: ['user']
+});
+</script>
+
+<style lang="stylus" scoped>
+.adsvaidqfznoartcbplullnejvxjphcn
+ padding 32px
+ background #1a94f2
+ border-radius 6px
+ color #fff
+
+ a
+ margin-left 8px
+ color #fff
+
+</style>
diff --git a/src/client/app/desktop/views/pages/user/user.vue b/src/client/app/desktop/views/pages/user/user.vue
index 3644286fbc..fc5c900037 100644
--- a/src/client/app/desktop/views/pages/user/user.vue
+++ b/src/client/app/desktop/views/pages/user/user.vue
@@ -1,8 +1,26 @@
<template>
<mk-ui>
- <div class="user" v-if="!fetching">
- <x-header :user="user"/>
- <x-home v-if="page == 'home'" :user="user"/>
+ <div class="xygkxeaeontfaokvqmiblezmhvhostak" v-if="!fetching" :data-darkmode="$store.state.device.darkmode">
+ <div class="is-suspended" v-if="user.isSuspended">%fa:exclamation-triangle% %i18n:@is-suspended%</div>
+ <div class="is-remote" v-if="user.host != null">%fa:exclamation-triangle% %i18n:@is-remote%<a :href="user.url || user.uri" target="_blank">%i18n:@view-remote%</a></div>
+ <main>
+ <div class="main">
+ <x-header :user="user"/>
+ <mk-note-detail v-if="user.pinnedNote" :note="user.pinnedNote" :compact="true"/>
+ <x-timeline class="timeline" ref="tl" :user="user"/>
+ </div>
+ <div class="side">
+ <x-profile :user="user"/>
+ <x-twitter :user="user" v-if="user.host === null && user.twitter"/>
+ <mk-calendar @chosen="warp" :start="new Date(user.createdAt)"/>
+ <mk-activity :user="user"/>
+ <x-photos :user="user"/>
+ <x-friends :user="user"/>
+ <x-followers-you-know v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
+ <div class="nav"><mk-nav/></div>
+ <p v-if="user.host === null">%i18n:@last-used-at%: <b><mk-time :time="user.lastUsedAt"/></b></p>
+ </div>
+ </main>
</div>
</mk-ui>
</template>
@@ -13,17 +31,22 @@ import parseAcct from '../../../../../../acct/parse';
import getUserName from '../../../../../../renderers/get-user-name';
import Progress from '../../../../common/scripts/loading';
import XHeader from './user.header.vue';
-import XHome from './user.home.vue';
+import XTimeline from './user.timeline.vue';
+import XProfile from './user.profile.vue';
+import XPhotos from './user.photos.vue';
+import XFollowersYouKnow from './user.followers-you-know.vue';
+import XFriends from './user.friends.vue';
+import XTwitter from './user.twitter.vue';
export default Vue.extend({
components: {
XHeader,
- XHome
- },
- props: {
- page: {
- default: 'home'
- }
+ XTimeline,
+ XProfile,
+ XPhotos,
+ XFollowersYouKnow,
+ XFriends,
+ XTwitter
},
data() {
return {
@@ -47,8 +70,89 @@ export default Vue.extend({
Progress.done();
document.title = getUserName(this.user) + ' | Misskey';
});
+ },
+
+ warp(date) {
+ (this.$refs.tl as any).warp(date);
}
}
});
</script>
+<style lang="stylus" scoped>
+root(isDark)
+ width 980px
+ padding 16px
+ margin 0 auto
+
+ > .is-suspended
+ > .is-remote
+ margin-bottom 16px
+ padding 14px 16px
+ font-size 14px
+ border-radius 6px
+
+ &.is-suspended
+ color isDark ? #ffb4b4 : #570808
+ background isDark ? #611d1d : #ffdbdb
+ border solid 1px isDark ? #d64a4a : #e09696
+
+ &.is-remote
+ color isDark ? #ffbd3e : #573c08
+ background isDark ? #42321c : #fff0db
+ border solid 1px isDark ? #90733c : #dcbb7b
+
+ > a
+ font-weight bold
+
+ > main
+ display flex
+ justify-content center
+
+ > .main
+ > .side
+ > *:not(:last-child)
+ margin-bottom 16px
+
+ > .main
+ flex 1
+ min-width 0 // SEE: http://kudakurage.hatenadiary.com/entry/2016/04/01/232722
+ margin-right 16px
+
+ > .timeline
+ border 1px solid rgba(#000, 0.075)
+ border-radius 6px
+
+ > .side
+ width 275px
+ flex-shrink 0
+
+ > p
+ display block
+ margin 0
+ padding 0 12px
+ text-align center
+ font-size 0.8em
+ color #aaa
+
+ > .nav
+ padding 16px
+ font-size 12px
+ color #aaa
+ background #fff
+ border solid 1px rgba(#000, 0.075)
+ border-radius 6px
+
+ a
+ color #999
+
+ i
+ color #ccc
+
+.xygkxeaeontfaokvqmiblezmhvhostak[data-darkmode]
+ root(true)
+
+.xygkxeaeontfaokvqmiblezmhvhostak:not([data-darkmode])
+ root(false)
+
+</style>
diff --git a/src/client/app/desktop/views/pages/welcome.vue b/src/client/app/desktop/views/pages/welcome.vue
index 029e44e27c..cac4007b40 100644
--- a/src/client/app/desktop/views/pages/welcome.vue
+++ b/src/client/app/desktop/views/pages/welcome.vue
@@ -7,6 +7,13 @@
</button>
<div class="body" :style="{ backgroundImage: `url('${ welcomeBgUrl }')` }">
<div class="container">
+ <div class="info">
+ <span>%i18n:common.misskey% <b>{{ host }}</b></span>
+ <span class="stats" v-if="stats">
+ <span>%fa:user% {{ stats.originalUsersCount | number }}</span>
+ <span>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</span>
+ </span>
+ </div>
<main>
<div class="about">
<h1 v-if="name">{{ name }}</h1>
@@ -19,12 +26,8 @@
<mk-signin/>
</div>
</main>
- <div class="info">
- <span>%i18n:common.misskey% <b>{{ host }}</b></span>
- <span class="stats" v-if="stats">
- <span>%fa:user% {{ stats.originalUsersCount | number }}</span>
- <span>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</span>
- </span>
+ <div class="hashtags">
+ <router-link v-for="tag in tags" :key="tag" :to="`/tags/${ tag }`" :title="tag">#{{ tag }}</router-link>
</div>
<mk-nav class="nav"/>
</div>
@@ -32,7 +35,7 @@
<img src="assets/title.dark.svg" alt="Misskey">
</div>
<div class="tl">
- <mk-welcome-timeline/>
+ <mk-welcome-timeline :max="20"/>
</div>
<modal name="signup" width="500px" height="auto" scrollable>
<header :class="$style.signupFormHeader">%i18n:@signup%</header>
@@ -54,13 +57,18 @@ export default Vue.extend({
host,
name,
description,
- pointerInterval: null
+ pointerInterval: null,
+ tags: []
};
},
created() {
(this as any).api('stats').then(stats => {
this.stats = stats;
});
+
+ (this as any).api('hashtags/trend').then(stats => {
+ this.tags = stats.map(x => x.tag);
+ });
},
mounted() {
this.point();
@@ -161,6 +169,20 @@ root(isDark)
$loginWidth = 340px
$width = $aboutWidth + $loginWidth
+ > .info
+ margin 0 auto 16px auto
+ width $width
+ font-size 14px
+ color #fff
+
+ > .stats
+ margin-left 16px
+ padding-left 16px
+ border-left solid 1px #fff
+
+ > *
+ margin-right 16px
+
> main
display flex
margin auto
@@ -199,24 +221,19 @@ root(isDark)
> .login
width $loginWidth
padding 16px 32px 32px 32px
- background #f5f5f5
+ background isDark ? #2e3440 : #f5f5f5
- > .info
+ > .hashtags
margin 16px auto
- padding 12px
width $width
font-size 14px
color #fff
- background rgba(#000, 0.2)
+ background rgba(#000, 0.3)
border-radius 8px
- > .stats
- margin-left 16px
- padding-left 16px
- border-left solid 1px #fff
-
- > *
- margin-right 16px
+ > *
+ display inline-block
+ margin 14px
> .nav
display block
diff --git a/src/client/app/mobile/api/dialog.ts b/src/client/app/mobile/api/dialog.ts
index a2378767be..23f35b7aa9 100644
--- a/src/client/app/mobile/api/dialog.ts
+++ b/src/client/app/mobile/api/dialog.ts
@@ -1,5 +1,18 @@
-export default function(opts) {
+import OS from '../../mios';
+import Dialog from '../views/components/dialog.vue';
+
+export default (os: OS) => opts => {
return new Promise<string>((res, rej) => {
- alert('dialog not implemented yet');
+ const o = opts || {};
+ const d = os.new(Dialog, {
+ title: o.title,
+ text: o.text,
+ modal: o.modal,
+ buttons: o.actions
+ });
+ d.$once('clicked', id => {
+ res(id);
+ });
+ document.body.appendChild(d.$el);
});
-}
+};
diff --git a/src/client/app/mobile/script.ts b/src/client/app/mobile/script.ts
index cc0a8331ba..cb43f9d520 100644
--- a/src/client/app/mobile/script.ts
+++ b/src/client/app/mobile/script.ts
@@ -38,6 +38,7 @@ import MkSettings from './views/pages/settings.vue';
import MkReversi from './views/pages/reversi.vue';
import MkTag from './views/pages/tag.vue';
import MkShare from './views/pages/share.vue';
+import MkFollow from '../common/views/pages/follow.vue';
/**
* init
@@ -80,7 +81,8 @@ init((launch) => {
{ path: '/@:user', component: MkUser },
{ path: '/@:user/followers', component: MkFollowers },
{ path: '/@:user/following', component: MkFollowing },
- { path: '/notes/:note', component: MkNote }
+ { path: '/notes/:note', component: MkNote },
+ { path: '/authorize-follow', component: MkFollow }
]
});
@@ -88,7 +90,7 @@ init((launch) => {
launch(router, os => ({
chooseDriveFolder,
chooseDriveFile,
- dialog,
+ dialog: dialog(os),
input,
post: post(os),
notify
diff --git a/src/client/app/mobile/views/components/dialog.vue b/src/client/app/mobile/views/components/dialog.vue
new file mode 100644
index 0000000000..9ee01cb782
--- /dev/null
+++ b/src/client/app/mobile/views/components/dialog.vue
@@ -0,0 +1,171 @@
+<template>
+<div class="mk-dialog">
+ <div class="bg" ref="bg" @click="onBgClick"></div>
+ <div class="main" ref="main">
+ <header v-html="title" :class="$style.header"></header>
+ <div class="body" v-html="text"></div>
+ <div class="buttons">
+ <button v-for="button in buttons" @click="click(button)">{{ button.text }}</button>
+ </div>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import * as anime from 'animejs';
+
+export default Vue.extend({
+ props: {
+ title: {
+ type: String,
+ required: false
+ },
+ text: {
+ type: String,
+ required: true
+ },
+ buttons: {
+ type: Array,
+ default: () => {
+ return [{
+ text: 'OK'
+ }];
+ }
+ },
+ modal: {
+ type: Boolean,
+ default: false
+ }
+ },
+ mounted() {
+ this.$nextTick(() => {
+ (this.$refs.bg as any).style.pointerEvents = 'auto';
+ anime({
+ targets: this.$refs.bg,
+ opacity: 1,
+ duration: 100,
+ easing: 'linear'
+ });
+
+ anime({
+ targets: this.$refs.main,
+ opacity: 1,
+ scale: [1.2, 1],
+ duration: 300,
+ easing: [0, 0.5, 0.5, 1]
+ });
+ });
+ },
+ methods: {
+ click(button) {
+ this.$emit('clicked', button.id);
+ this.close();
+ },
+ close() {
+ (this.$refs.bg as any).style.pointerEvents = 'none';
+ anime({
+ targets: this.$refs.bg,
+ opacity: 0,
+ duration: 300,
+ easing: 'linear'
+ });
+
+ (this.$refs.main as any).style.pointerEvents = 'none';
+ anime({
+ targets: this.$refs.main,
+ opacity: 0,
+ scale: 0.8,
+ duration: 300,
+ easing: [ 0.5, -0.5, 1, 0.5 ],
+ complete: () => this.$destroy()
+ });
+ },
+ onBgClick() {
+ if (!this.modal) {
+ this.close();
+ }
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.mk-dialog
+ > .bg
+ display block
+ position fixed
+ z-index 8192
+ top 0
+ left 0
+ width 100%
+ height 100%
+ background rgba(#000, 0.7)
+ opacity 0
+ pointer-events none
+
+ > .main
+ display block
+ position fixed
+ z-index 8192
+ top 20%
+ left 0
+ right 0
+ margin 0 auto 0 auto
+ padding 16px
+ width calc(100% - 32px)
+ max-width 300px
+ background #fff
+ opacity 0
+
+ > .body
+ margin 1em 0
+ color #888
+
+ > .buttons
+ > button
+ display inline-block
+ float right
+ margin 0
+ padding 0 10px
+ font-size 1.1em
+ font-weight normal
+ text-decoration none
+ color #888
+ background transparent
+ outline none
+ border none
+ border-radius 0
+ cursor pointer
+ transition color 0.1s ease
+
+ i
+ margin 0 0.375em
+
+ &:hover
+ color $theme-color
+
+ &:active
+ color darken($theme-color, 10%)
+ transition color 0s ease
+
+</style>
+
+<style lang="stylus" module>
+@import '~const.styl'
+
+.header
+ margin 0 0 1em 0
+ color $theme-color
+ // color #43A4EC
+ font-weight bold
+
+ &:empty
+ display none
+
+ > i
+ margin-right 0.5em
+
+</style>
diff --git a/src/client/app/mobile/views/components/note-detail.vue b/src/client/app/mobile/views/components/note-detail.vue
index f3e77d7062..fa15922183 100644
--- a/src/client/app/mobile/views/components/note-detail.vue
+++ b/src/client/app/mobile/views/components/note-detail.vue
@@ -38,10 +38,7 @@
<div class="text">
<span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
<span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
- <mk-note-html v-if="p.text" :text="p.text" :i="$store.state.i"/>
- </div>
- <div class="tags" v-if="p.tags && p.tags.length > 0">
- <router-link v-for="tag in p.tags" :key="tag" :to="`/tags/${tag}`">{{ tag }}</router-link>
+ <misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i"/>
</div>
<div class="media" v-if="p.media.length > 0">
<mk-media-list :media-list="p.media" :raw="true"/>
@@ -83,7 +80,7 @@
<script lang="ts">
import Vue from 'vue';
-import parse from '../../../../../text/parse';
+import parse from '../../../../../mfm/parse';
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
@@ -197,7 +194,8 @@ export default Vue.extend({
(this as any).os.new(MkReactionPicker, {
source: this.$refs.reactButton,
note: this.p,
- compact: true
+ compact: true,
+ big: true
});
},
menu() {
@@ -369,31 +367,6 @@ root(isDark)
display block
max-width 100%
- > .tags
- margin 4px 0 0 0
-
- > *
- display inline-block
- margin 0 8px 0 0
- padding 2px 8px 2px 16px
- font-size 90%
- color #8d969e
- background #edf0f3
- border-radius 4px
-
- &:before
- content ""
- display block
- position absolute
- top 0
- bottom 0
- left 4px
- width 8px
- height 8px
- margin auto 0
- background #fff
- border-radius 100%
-
> .time
font-size 16px
color isDark ? #606984 : #c0c0c0
diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue
index 4498bb5633..8fc8af7f8d 100644
--- a/src/client/app/mobile/views/components/note.vue
+++ b/src/client/app/mobile/views/components/note.vue
@@ -25,16 +25,13 @@
<span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
<span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
<a class="reply" v-if="p.reply">%fa:reply%</a>
- <mk-note-html v-if="p.text && !canHideText(p)" :text="p.text" :i="$store.state.i" :class="$style.text"/>
+ <misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i" :class="$style.text"/>
<a class="rp" v-if="p.renote != null">RP:</a>
</div>
<div class="media" v-if="p.media.length > 0">
<mk-media-list :media-list="p.media"/>
</div>
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
- <div class="tags" v-if="p.tags && p.tags.length > 0">
- <router-link v-for="tag in p.tags" :key="tag" :to="`/tags/${tag}`">{{ tag }}</router-link>
- </div>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
<div class="map" v-if="p.geo" ref="map"></div>
@@ -68,8 +65,7 @@
<script lang="ts">
import Vue from 'vue';
-import parse from '../../../../../text/parse';
-import canHideText from '../../../common/scripts/can-hide-text';
+import parse from '../../../../../mfm/parse';
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
@@ -165,8 +161,6 @@ export default Vue.extend({
},
methods: {
- canHideText,
-
capture(withHandler = false) {
if (this.$store.getters.isSignedIn) {
this.connection.send({
@@ -216,7 +210,8 @@ export default Vue.extend({
(this as any).os.new(MkReactionPicker, {
source: this.$refs.reactButton,
note: this.p,
- compact: true
+ compact: true,
+ big: true
});
},
@@ -419,31 +414,6 @@ root(isDark)
.mk-url-preview
margin-top 8px
- > .tags
- margin 4px 0 0 0
-
- > *
- display inline-block
- margin 0 8px 0 0
- padding 2px 8px 2px 16px
- font-size 90%
- color #8d969e
- background isDark ? #313543 : #edf0f3
- border-radius 4px
-
- &:before
- content ""
- display block
- position absolute
- top 0
- bottom 0
- left 4px
- width 8px
- height 8px
- margin auto 0
- background isDark ? #282c37 : #fff
- border-radius 100%
-
> .media
> img
display block
diff --git a/src/client/app/mobile/views/components/notes.vue b/src/client/app/mobile/views/components/notes.vue
index 7aaf0424c7..06d22c7258 100644
--- a/src/client/app/mobile/views/components/notes.vue
+++ b/src/client/app/mobile/views/components/notes.vue
@@ -13,7 +13,9 @@
<button @click="resolveInitPromise">%i18n:@retry%</button>
</div>
- <transition-group name="mk-notes" class="transition">
+ <!-- トランジションを有効にするとなぜかメモリリークする -->
+ <!-- <transition-group name="mk-notes" class="transition"> -->
+ <div class="transition">
<template v-for="(note, i) in _notes">
<mk-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)"/>
<p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
@@ -21,7 +23,8 @@
<span>%fa:angle-down%{{ _notes[i + 1]._datetext }}</span>
</p>
</template>
- </transition-group>
+ </div>
+ <!-- </transition-group> -->
<footer v-if="more">
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
diff --git a/src/client/app/mobile/views/components/notifications.vue b/src/client/app/mobile/views/components/notifications.vue
index 6bb9e9bb2c..fc220c252a 100644
--- a/src/client/app/mobile/views/components/notifications.vue
+++ b/src/client/app/mobile/views/components/notifications.vue
@@ -1,6 +1,8 @@
<template>
<div class="mk-notifications">
- <transition-group name="mk-notifications" class="transition notifications">
+ <!-- トランジションを有効にするとなぜかメモリリークする -->
+ <!-- <transition-group name="mk-notifications" class="transition notifications"> -->
+ <div class="transition notifications">
<template v-for="(notification, i) in _notifications">
<mk-notification :notification="notification" :key="notification.id"/>
<p class="date" :key="notification.id + '_date'" v-if="i != notifications.length - 1 && notification._date != _notifications[i + 1]._date">
@@ -8,7 +10,8 @@
<span>%fa:angle-down%{{ _notifications[i + 1]._datetext }}</span>
</p>
</template>
- </transition-group>
+ </div>
+ <!-- </transition-group> -->
<button class="more" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>
diff --git a/src/client/app/mobile/views/components/post-form.vue b/src/client/app/mobile/views/components/post-form.vue
index 62fa185085..1015a44115 100644
--- a/src/client/app/mobile/views/components/post-form.vue
+++ b/src/client/app/mobile/views/components/post-form.vue
@@ -45,7 +45,7 @@ import Vue from 'vue';
import * as XDraggable from 'vuedraggable';
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
import getKao from '../../../common/scripts/get-kao';
-import parse from '../../../../../text/parse';
+import parse from '../../../../../mfm/parse';
import { host } from '../../../config';
export default Vue.extend({
diff --git a/src/client/app/mobile/views/components/sub-note-content.vue b/src/client/app/mobile/views/components/sub-note-content.vue
index 4ad90b97df..a4ce49786e 100644
--- a/src/client/app/mobile/views/components/sub-note-content.vue
+++ b/src/client/app/mobile/views/components/sub-note-content.vue
@@ -4,7 +4,7 @@
<span v-if="note.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
<span v-if="note.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
<a class="reply" v-if="note.replyId">%fa:reply%</a>
- <mk-note-html v-if="note.text" :text="note.text" :i="$store.state.i"/>
+ <misskey-flavored-markdown v-if="note.text" :text="note.text" :i="$store.state.i"/>
<a class="rp" v-if="note.renoteId">RP: ...</a>
</div>
<details v-if="note.media.length > 0">
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index 34482fccba..89e5eaff67 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -13,6 +13,7 @@
<ui-switch v-model="darkmode">%i18n:@dark-mode%</ui-switch>
<ui-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</ui-switch>
<ui-switch v-model="$store.state.settings.iLikeSushi" @change="onChangeILikeSushi">%i18n:common.i-like-sushi%</ui-switch>
+ <ui-switch v-model="$store.state.settings.reversiBoardLabels" @change="onChangeReversiBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch>
<div>
<div>%i18n:@timeline%</div>
@@ -182,6 +183,13 @@ export default Vue.extend({
});
},
+ onChangeReversiBoardLabels(v) {
+ this.$store.dispatch('settings/set', {
+ key: 'reversiBoardLabels',
+ value: v
+ });
+ },
+
onChangeShowReplyTarget(v) {
this.$store.dispatch('settings/set', {
key: 'showReplyTarget',
diff --git a/src/client/app/mobile/views/pages/user.vue b/src/client/app/mobile/views/pages/user.vue
index 3d37015906..ba9de9f8a6 100644
--- a/src/client/app/mobile/views/pages/user.vue
+++ b/src/client/app/mobile/views/pages/user.vue
@@ -18,7 +18,9 @@
<span class="username"><mk-acct :user="user"/></span>
<span class="followed" v-if="user.isFollowed">%i18n:@follows-you%</span>
</div>
- <div class="description">{{ user.description }}</div>
+ <div class="description">
+ <misskey-flavored-markdown v-if="user.description" :text="user.description" :i="$store.state.i"/>
+ </div>
<div class="info">
<p class="location" v-if="user.host === null && user.profile.location">
%fa:map-marker%{{ user.profile.location }}
diff --git a/src/client/app/mobile/views/pages/welcome.vue b/src/client/app/mobile/views/pages/welcome.vue
index cd8f5841e7..ec21588ade 100644
--- a/src/client/app/mobile/views/pages/welcome.vue
+++ b/src/client/app/mobile/views/pages/welcome.vue
@@ -14,6 +14,9 @@
<div class="tl">
<mk-welcome-timeline/>
</div>
+ <div class="hashtags">
+ <router-link v-for="tag in tags" :key="tag" :to="`/tags/${ tag }`" :title="tag">#{{ tag }}</router-link>
+ </div>
<div class="stats" v-if="stats">
<span>%fa:user% {{ stats.originalUsersCount | number }}</span>
<span>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</span>
@@ -37,13 +40,18 @@ export default Vue.extend({
stats: null,
host,
name,
- description
+ description,
+ tags: []
};
},
created() {
(this as any).api('stats').then(stats => {
this.stats = stats;
});
+
+ (this as any).api('hashtags/trend').then(stats => {
+ this.tags = stats.map(x => x.tag);
+ });
}
});
</script>
@@ -116,12 +124,22 @@ export default Vue.extend({
box-shadow 0 1px 3px rgba(#000, 0.075), inset 0 0 5px rgba(#000, 0.2)
> .tl
+ margin 16px 0
+
> *
max-height 300px
border-radius 6px
overflow auto
-webkit-overflow-scrolling touch
+ > .hashtags
+ padding 16px 0
+ border solid 2px #ddd
+ border-radius 8px
+
+ > *
+ margin 0 16px
+
> .stats
margin 16px 0
padding 8px
diff --git a/src/client/app/store.ts b/src/client/app/store.ts
index 1bc39ae66d..dfb24bb5f4 100644
--- a/src/client/app/store.ts
+++ b/src/client/app/store.ts
@@ -19,7 +19,8 @@ const defaultSettings = {
loadRemoteMedia: true,
disableViaMobile: false,
memo: null,
- iLikeSushi: false
+ iLikeSushi: false,
+ reversiBoardLabels: false
};
const defaultDeviceSettings = {
diff --git a/src/client/assets/manifest.json b/src/client/assets/manifest.json
index dcd1e26790..bae0ee7f1d 100644
--- a/src/client/assets/manifest.json
+++ b/src/client/assets/manifest.json
@@ -37,6 +37,6 @@
}
],
"share_target": {
- "url_template": "share?text={title}%20-%20{text}%20-%20{url}"
+ "url_template": "share?text=【{title}】%0A{text}%0A{url}"
}
}