summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build/fa.ts5
-rw-r--r--src/client/app/boot.js2
-rw-r--r--src/client/app/common/scripts/can-hide-text.ts16
-rw-r--r--src/client/app/common/scripts/streaming/home.ts11
-rw-r--r--src/client/app/common/views/components/acct.vue19
-rw-r--r--src/client/app/common/views/components/avatar.vue2
-rw-r--r--src/client/app/common/views/components/index.ts2
-rw-r--r--src/client/app/common/views/components/url-preview.vue17
-rw-r--r--src/client/app/common/views/widgets/calendar.vue185
-rw-r--r--src/client/app/common/views/widgets/rss.vue10
-rw-r--r--src/client/app/desktop/views/components/drive.file.vue4
-rw-r--r--src/client/app/desktop/views/components/home.vue48
-rw-r--r--src/client/app/desktop/views/components/media-image.vue2
-rw-r--r--src/client/app/desktop/views/components/note-detail.sub.vue2
-rw-r--r--src/client/app/desktop/views/components/note-detail.vue2
-rw-r--r--src/client/app/desktop/views/components/note-preview.vue7
-rw-r--r--src/client/app/desktop/views/components/notes.note.sub.vue2
-rw-r--r--src/client/app/desktop/views/components/notes.note.vue21
-rw-r--r--src/client/app/desktop/views/components/settings.api.vue2
-rw-r--r--src/client/app/desktop/views/components/settings.vue11
-rw-r--r--src/client/app/desktop/views/components/user-lists-window.vue2
-rw-r--r--src/client/app/desktop/views/components/user-preview.vue6
-rw-r--r--src/client/app/desktop/views/components/users-list.vue10
-rw-r--r--src/client/app/desktop/views/pages/user/user.header.vue2
-rw-r--r--src/client/app/mobile/api/post.ts49
-rw-r--r--src/client/app/mobile/script.ts2
-rw-r--r--src/client/app/mobile/views/components/drive.file-detail.vue2
-rw-r--r--src/client/app/mobile/views/components/drive.file.vue2
-rw-r--r--src/client/app/mobile/views/components/media-image.vue2
-rw-r--r--src/client/app/mobile/views/components/note-preview.vue3
-rw-r--r--src/client/app/mobile/views/components/note.vue16
-rw-r--r--src/client/app/mobile/views/components/notes.vue19
-rw-r--r--src/client/app/mobile/views/components/post-form.vue12
-rw-r--r--src/client/app/mobile/views/components/ui.header.vue5
-rw-r--r--src/client/app/mobile/views/components/ui.nav.vue1
-rw-r--r--src/client/app/mobile/views/components/widget-container.vue22
-rw-r--r--src/client/app/mobile/views/pages/user.vue2
-rw-r--r--src/client/app/mobile/views/pages/widgets.vue (renamed from src/client/app/mobile/views/pages/dashboard.vue)69
-rw-r--r--src/client/app/store.ts41
-rw-r--r--src/client/docs/api/gulpfile.ts4
-rw-r--r--src/client/docs/gulpfile.ts2
-rw-r--r--src/models/drive-file.ts1
-rw-r--r--src/server/web/url-preview.ts18
-rw-r--r--src/services/drive/add-file.ts5
44 files changed, 394 insertions, 273 deletions
diff --git a/src/build/fa.ts b/src/build/fa.ts
index f6f2427d0a..111c19ae66 100644
--- a/src/build/fa.ts
+++ b/src/build/fa.ts
@@ -7,10 +7,7 @@ import * as regular from '@fortawesome/fontawesome-free-regular';
import * as solid from '@fortawesome/fontawesome-free-solid';
import * as brands from '@fortawesome/fontawesome-free-brands';
-// Add icons
-fontawesome.library.add(regular);
-fontawesome.library.add(solid);
-fontawesome.library.add(brands);
+fontawesome.library.add(regular, solid, brands);
export const pattern = /%fa:(.+?)%/g;
diff --git a/src/client/app/boot.js b/src/client/app/boot.js
index 35d02cf9c5..9338bc501e 100644
--- a/src/client/app/boot.js
+++ b/src/client/app/boot.js
@@ -32,7 +32,7 @@
// Detect the user language
// Note: The default language is Japanese
let lang = navigator.language.split('-')[0];
- if (!/^(en|ja)$/.test(lang)) lang = 'ja';
+ if (!LANGS.includes(lang)) lang = 'en';
if (localStorage.getItem('lang')) lang = localStorage.getItem('lang');
// Detect the user agent
diff --git a/src/client/app/common/scripts/can-hide-text.ts b/src/client/app/common/scripts/can-hide-text.ts
new file mode 100644
index 0000000000..4a4be8d9d0
--- /dev/null
+++ b/src/client/app/common/scripts/can-hide-text.ts
@@ -0,0 +1,16 @@
+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/streaming/home.ts b/src/client/app/common/scripts/streaming/home.ts
index 32685f3c2c..09d830bece 100644
--- a/src/client/app/common/scripts/streaming/home.ts
+++ b/src/client/app/common/scripts/streaming/home.ts
@@ -48,6 +48,17 @@ export class HomeStream extends Stream {
}
});
+ this.on('mobile_home_updated', x => {
+ if (x.home) {
+ os.store.commit('settings/setMobileHome', x.home);
+ } else {
+ os.store.commit('settings/setMobileHomeWidget', {
+ id: x.id,
+ data: x.data
+ });
+ }
+ });
+
// トークンが再生成されたとき
// このままではMisskeyが利用できないので強制的にサインアウトさせる
this.on('my_token_regenerated', () => {
diff --git a/src/client/app/common/views/components/acct.vue b/src/client/app/common/views/components/acct.vue
new file mode 100644
index 0000000000..1ad222afdd
--- /dev/null
+++ b/src/client/app/common/views/components/acct.vue
@@ -0,0 +1,19 @@
+<template>
+<span class="mk-acct">
+ <span class="name">@{{ user.username }}</span>
+ <span class="host" v-if="user.host">@{{ user.host }}</span>
+</span>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+ props: ['user']
+});
+</script>
+
+<style lang="stylus" scoped>
+.mk-acct
+ > .host
+ opacity 0.5
+</style>
diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue
index a4648c272e..8ec359e83c 100644
--- a/src/client/app/common/views/components/avatar.vue
+++ b/src/client/app/common/views/components/avatar.vue
@@ -23,7 +23,7 @@ export default Vue.extend({
computed: {
style(): any {
return {
- backgroundColor: this.user.avatarColor ? `rgb(${ this.user.avatarColor.join(',') })` : null,
+ backgroundColor: this.user.avatarColor && this.user.avatarColor.length == 3 ? `rgb(${ this.user.avatarColor.join(',') })` : null,
backgroundImage: `url(${ this.user.avatarUrl }?thumbnail)`,
borderRadius: (this as any).clientSettings.circleIcons ? '100%' : null
};
diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts
index 69fed00c74..c1a7bc61d7 100644
--- a/src/client/app/common/views/components/index.ts
+++ b/src/client/app/common/views/components/index.ts
@@ -3,6 +3,7 @@ import Vue from 'vue';
import signin from './signin.vue';
import signup from './signup.vue';
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';
@@ -29,6 +30,7 @@ import welcomeTimeline from './welcome-timeline.vue';
Vue.component('mk-signin', signin);
Vue.component('mk-signup', signup);
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);
diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue
index 3bae6e5078..028b911e24 100644
--- a/src/client/app/common/views/components/url-preview.vue
+++ b/src/client/app/common/views/components/url-preview.vue
@@ -126,16 +126,21 @@ root(isDark)
line-height 16px
vertical-align top
+ @media (max-width 700px)
+ > .thumbnail
+ position relative
+ width 100%
+ height 100px
+
+ & + article
+ left 0
+ width 100%
+
@media (max-width 500px)
font-size 8px
- border none
> .thumbnail
- width 70px
-
- & + article
- left 70px
- width calc(100% - 70px)
+ height 70px
> article
padding 8px
diff --git a/src/client/app/common/views/widgets/calendar.vue b/src/client/app/common/views/widgets/calendar.vue
index 41e9253784..0e9714960a 100644
--- a/src/client/app/common/views/widgets/calendar.vue
+++ b/src/client/app/common/views/widgets/calendar.vue
@@ -1,37 +1,37 @@
<template>
-<div class="mkw-calendar"
- :data-melt="props.design == 1"
- :data-special="special"
- :data-mobile="isMobile"
->
- <div class="calendar" :data-is-holiday="isHoliday">
- <p class="month-and-year">
- <span class="year">{{ year }}年</span>
- <span class="month">{{ month }}月</span>
- </p>
- <p class="day">{{ day }}日</p>
- <p class="week-day">{{ weekDay }}曜日</p>
- </div>
- <div class="info">
- <div>
- <p>今日:<b>{{ dayP.toFixed(1) }}%</b></p>
- <div class="meter">
- <div class="val" :style="{ width: `${dayP}%` }"></div>
+<div class="mkw-calendar" :data-special="special" :data-mobile="isMobile">
+ <mk-widget-container :naked="props.design == 1" :show-header="false">
+ <div class="mkw-calendar--body">
+ <div class="calendar" :data-is-holiday="isHoliday">
+ <p class="month-and-year">
+ <span class="year">{{ year }}年</span>
+ <span class="month">{{ month }}月</span>
+ </p>
+ <p class="day">{{ day }}日</p>
+ <p class="week-day">{{ weekDay }}曜日</p>
</div>
- </div>
- <div>
- <p>今月:<b>{{ monthP.toFixed(1) }}%</b></p>
- <div class="meter">
- <div class="val" :style="{ width: `${monthP}%` }"></div>
- </div>
- </div>
- <div>
- <p>今年:<b>{{ yearP.toFixed(1) }}%</b></p>
- <div class="meter">
- <div class="val" :style="{ width: `${yearP}%` }"></div>
+ <div class="info">
+ <div>
+ <p>今日:<b>{{ dayP.toFixed(1) }}%</b></p>
+ <div class="meter">
+ <div class="val" :style="{ width: `${dayP}%` }"></div>
+ </div>
+ </div>
+ <div>
+ <p>今月:<b>{{ monthP.toFixed(1) }}%</b></p>
+ <div class="meter">
+ <div class="val" :style="{ width: `${monthP}%` }"></div>
+ </div>
+ </div>
+ <div>
+ <p>今年:<b>{{ yearP.toFixed(1) }}%</b></p>
+ <div class="meter">
+ <div class="val" :style="{ width: `${yearP}%` }"></div>
+ </div>
+ </div>
</div>
</div>
- </div>
+ </mk-widget-container>
</div>
</template>
@@ -111,93 +111,82 @@ export default define({
@import '~const.styl'
root(isDark)
- padding 16px 0
- color isDark ? #c5ced6 :#777
- background isDark ? #282C37 : #fff
- border solid 1px rgba(#000, 0.075)
- border-radius 6px
-
&[data-special='on-new-years-day']
border-color #ef95a0
- &[data-melt]
- background transparent
- border none
-
- &[data-mobile]
- border none
- border-radius 8px
- box-shadow 0 0 0 1px rgba(#000, 0.2)
+ .mkw-calendar--body
+ padding 16px 0
+ color isDark ? #c5ced6 : #777
- &:after
- content ""
- display block
- clear both
+ &:after
+ content ""
+ display block
+ clear both
- > .calendar
- float left
- width 60%
- text-align center
+ > .calendar
+ float left
+ width 60%
+ text-align center
- &[data-is-holiday]
- > .day
- color #ef95a0
+ &[data-is-holiday]
+ > .day
+ color #ef95a0
- > p
- margin 0
- line-height 18px
- font-size 14px
+ > p
+ margin 0
+ line-height 18px
+ font-size 14px
- > span
- margin 0 4px
+ > span
+ margin 0 4px
- > .day
- margin 10px 0
- line-height 32px
- font-size 28px
+ > .day
+ margin 10px 0
+ line-height 32px
+ font-size 28px
- > .info
- display block
- float left
- width 40%
- padding 0 16px 0 0
+ > .info
+ display block
+ float left
+ width 40%
+ padding 0 16px 0 0
- > div
- margin-bottom 8px
+ > div
+ margin-bottom 8px
- &:last-child
- margin-bottom 4px
+ &:last-child
+ margin-bottom 4px
- > p
- margin 0 0 2px 0
- font-size 12px
- line-height 18px
- color isDark ? #7a8692 : #888
+ > p
+ margin 0 0 2px 0
+ font-size 12px
+ line-height 18px
+ color isDark ? #7a8692 : #888
- > b
- margin-left 2px
+ > b
+ margin-left 2px
- > .meter
- width 100%
- overflow hidden
- background isDark ? #1c1f25 : #eee
- border-radius 8px
+ > .meter
+ width 100%
+ overflow hidden
+ background isDark ? #1c1f25 : #eee
+ border-radius 8px
- > .val
- height 4px
- background $theme-color
+ > .val
+ height 4px
+ background $theme-color
- &:nth-child(1)
- > .meter > .val
- background #f7796c
+ &:nth-child(1)
+ > .meter > .val
+ background #f7796c
- &:nth-child(2)
- > .meter > .val
- background #a1de41
+ &:nth-child(2)
+ > .meter > .val
+ background #a1de41
- &:nth-child(3)
- > .meter > .val
- background #41ddde
+ &:nth-child(3)
+ > .meter > .val
+ background #41ddde
.mkw-calendar[data-darkmode]
root(true)
diff --git a/src/client/app/common/views/widgets/rss.vue b/src/client/app/common/views/widgets/rss.vue
index b5339add0b..9e2c6b6490 100644
--- a/src/client/app/common/views/widgets/rss.vue
+++ b/src/client/app/common/views/widgets/rss.vue
@@ -1,10 +1,10 @@
<template>
-<div class="mkw-rss" :data-mobile="isMobile">
+<div class="mkw-rss">
<mk-widget-container :show-header="!props.compact">
<template slot="header">%fa:rss-square%RSS</template>
<button slot="func" title="設定" @click="setting">%fa:cog%</button>
- <div class="mkw-rss--body">
+ <div class="mkw-rss--body" :data-mobile="isMobile">
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
<div class="feed" v-else>
<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
@@ -85,15 +85,17 @@ root(isDark)
margin-right 4px
&[data-mobile]
+ background isDark ? #21242f : #f3f3f3
+
.feed
padding 0
- font-size 1em
> a
padding 8px 16px
+ border-bottom none
&:nth-child(even)
- background rgba(#000, 0.05)
+ background isDark ? rgba(#000, 0.05) : rgba(#fff, 0.7)
.mkw-rss[data-darkmode]
root(true)
diff --git a/src/client/app/desktop/views/components/drive.file.vue b/src/client/app/desktop/views/components/drive.file.vue
index 39881711fa..d8b8420ece 100644
--- a/src/client/app/desktop/views/components/drive.file.vue
+++ b/src/client/app/desktop/views/components/drive.file.vue
@@ -50,7 +50,7 @@ export default Vue.extend({
return `${this.file.name}\n${this.file.type} ${Vue.filter('bytes')(this.file.datasize)}`;
},
background(): string {
- return this.file.properties.avgColor
+ return this.file.properties.avgColor && this.file.properties.avgColor.length == 3
? `rgb(${this.file.properties.avgColor.join(',')})`
: 'transparent';
}
@@ -129,7 +129,7 @@ export default Vue.extend({
},
onThumbnailLoaded() {
- if (this.file.properties.avgColor) {
+ if (this.file.properties.avgColor && this.file.properties.avgColor.length == 3) {
anime({
targets: this.$refs.thumbnail,
backgroundColor: `rgba(${this.file.properties.avgColor.join(',')}, 0)`,
diff --git a/src/client/app/desktop/views/components/home.vue b/src/client/app/desktop/views/components/home.vue
index cae6233cd8..a3d7927cfc 100644
--- a/src/client/app/desktop/views/components/home.vue
+++ b/src/client/app/desktop/views/components/home.vue
@@ -1,34 +1,34 @@
<template>
<div class="mk-home" :data-customize="customize">
<div class="customize" v-if="customize">
- <router-link to="/">%fa:check%完了</router-link>
+ <router-link to="/">%fa:check%%i18n:@done%</router-link>
<div>
<div class="adder">
- <p>ウィジェットを追加:</p>
+ <p>%i18n:@add-widget%</p>
<select v-model="widgetAdderSelected">
- <option value="profile">プロフィール</option>
- <option value="calendar">カレンダー</option>
- <option value="timemachine">カレンダー(タイムマシン)</option>
- <option value="activity">アクティビティ</option>
- <option value="rss">RSSリーダー</option>
- <option value="trends">トレンド</option>
- <option value="photo-stream">フォトストリーム</option>
- <option value="slideshow">スライドショー</option>
- <option value="version">バージョン</option>
- <option value="broadcast">ブロードキャスト</option>
- <option value="notifications">通知</option>
- <option value="users">おすすめユーザー</option>
- <option value="polls">投票</option>
- <option value="post-form">投稿フォーム</option>
- <option value="messaging">メッセージ</option>
- <option value="channel">チャンネル</option>
- <option value="access-log">アクセスログ</option>
- <option value="server">サーバー情報</option>
- <option value="donation">寄付のお願い</option>
- <option value="nav">ナビゲーション</option>
- <option value="tips">ヒント</option>
+ <option value="profile">%i18n:@profile%</option>
+ <option value="calendar">%i18n:@calendar%</option>
+ <option value="timemachine">%i18n:@timemachine%</option>
+ <option value="activity">%i18n:@activity%</option>
+ <option value="rss">%i18n:@rss%</option>
+ <option value="trends">%i18n:@trends%</option>
+ <option value="photo-stream">%i18n:@photo-stream%</option>
+ <option value="slideshow">%i18n:@slideshow%</option>
+ <option value="version">%i18n:@version%</option>
+ <option value="broadcast">%i18n:@broadcast%</option>
+ <option value="notifications">%i18n:@notifications%</option>
+ <option value="users">%i18n:@users%</option>
+ <option value="polls">%i18n:@polls%</option>
+ <option value="post-form">%i18n:@post-form%</option>
+ <option value="messaging">%i18n:@messaging%</option>
+ <option value="channel">%i18n:@channel%</option>
+ <option value="access-log">%i18n:@access-log%</option>
+ <option value="server">%i18n:@server%</option>
+ <option value="donation">%i18n:@donation%</option>
+ <option value="nav">%i18n:@nav%</option>
+ <option value="tips">%i18n:@tips%</option>
</select>
- <button @click="addWidget">追加</button>
+ <button @click="addWidget">%i18n:@add%</button>
</div>
<div class="trash">
<x-draggable v-model="trash" :options="{ group: 'x' }" @add="onTrash"></x-draggable>
diff --git a/src/client/app/desktop/views/components/media-image.vue b/src/client/app/desktop/views/components/media-image.vue
index e5803cc36e..b98a4707ec 100644
--- a/src/client/app/desktop/views/components/media-image.vue
+++ b/src/client/app/desktop/views/components/media-image.vue
@@ -26,7 +26,7 @@ export default Vue.extend({
computed: {
style(): any {
return {
- 'background-color': this.image.properties.avgColor ? `rgb(${this.image.properties.avgColor.join(',')})` : 'transparent',
+ 'background-color': this.image.properties.avgColor && this.image.properties.avgColor.length == 3 ? `rgb(${this.image.properties.avgColor.join(',')})` : 'transparent',
'background-image': this.raw ? `url(${this.image.url})` : `url(${this.image.url}?thumbnail&size=512)`
};
}
diff --git a/src/client/app/desktop/views/components/note-detail.sub.vue b/src/client/app/desktop/views/components/note-detail.sub.vue
index 24550c4e94..32119da50d 100644
--- a/src/client/app/desktop/views/components/note-detail.sub.vue
+++ b/src/client/app/desktop/views/components/note-detail.sub.vue
@@ -5,7 +5,7 @@
<header>
<div class="left">
<router-link class="name" :to="note.user | userPage" v-user-preview="note.userId">{{ note.user | userName }}</router-link>
- <span class="username">@{{ note.user | acct }}</span>
+ <span class="username"><mk-acct :user="note.user"/></span>
</div>
<div class="right">
<router-link class="time" :to="note | notePage">
diff --git a/src/client/app/desktop/views/components/note-detail.vue b/src/client/app/desktop/views/components/note-detail.vue
index a0e3915149..bda53db918 100644
--- a/src/client/app/desktop/views/components/note-detail.vue
+++ b/src/client/app/desktop/views/components/note-detail.vue
@@ -28,7 +28,7 @@
<mk-avatar class="avatar" :user="p.user"/>
<header>
<router-link class="name" :to="p.user | userPage" v-user-preview="p.user.id">{{ p.user | userName }}</router-link>
- <span class="username">@{{ p.user | acct }}</span>
+ <span class="username"><mk-acct :user="p.user"/></span>
<router-link class="time" :to="p | notePage">
<mk-time :time="p.createdAt"/>
</router-link>
diff --git a/src/client/app/desktop/views/components/note-preview.vue b/src/client/app/desktop/views/components/note-preview.vue
index d04abfc5a7..302c5e803f 100644
--- a/src/client/app/desktop/views/components/note-preview.vue
+++ b/src/client/app/desktop/views/components/note-preview.vue
@@ -4,7 +4,7 @@
<div class="main">
<header>
<router-link class="name" :to="note.user | userPage" v-user-preview="note.userId">{{ note.user | userName }}</router-link>
- <span class="username">@{{ note.user | acct }}</span>
+ <span class="username"><mk-acct :user="note.user"/></span>
<router-link class="time" :to="note | notePage">
<mk-time :time="note.createdAt"/>
</router-link>
@@ -59,17 +59,20 @@ root(isDark)
> .name
margin 0 .5em 0 0
padding 0
+ overflow hidden
color isDark ? #fff : #607073
font-size 1em
font-weight bold
text-decoration none
- white-space normal
+ text-overflow ellipsis
&:hover
text-decoration underline
> .username
margin 0 .5em 0 0
+ overflow hidden
+ text-overflow ellipsis
color isDark ? #606984 : #d1d8da
> .time
diff --git a/src/client/app/desktop/views/components/notes.note.sub.vue b/src/client/app/desktop/views/components/notes.note.sub.vue
index 575d605203..503982b1a8 100644
--- a/src/client/app/desktop/views/components/notes.note.sub.vue
+++ b/src/client/app/desktop/views/components/notes.note.sub.vue
@@ -4,7 +4,7 @@
<div class="main">
<header>
<router-link class="name" :to="note.user | userPage" v-user-preview="note.userId">{{ note.user | userName }}</router-link>
- <span class="username">@{{ note.user | acct }}</span>
+ <span class="username"><mk-acct :user="note.user"/></span>
<div class="info">
<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
<router-link class="created-at" :to="note | notePage">
diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue
index 057c3c0956..8660a5f899 100644
--- a/src/client/app/desktop/views/components/notes.note.vue
+++ b/src/client/app/desktop/views/components/notes.note.vue
@@ -17,7 +17,7 @@
<header>
<router-link class="name" :to="p.user | userPage" v-user-preview="p.user.id">{{ p.user | userName }}</router-link>
<span class="is-bot" v-if="p.user.host === null && p.user.isBot">bot</span>
- <span class="username">@{{ p.user | acct }}</span>
+ <span class="username"><mk-acct :user="p.user"/></span>
<div class="info">
<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
<span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
@@ -44,7 +44,7 @@
<div class="text">
<span v-if="p.isHidden" style="opacity: 0.5">(この投稿は非公開です)</span>
<a class="reply" v-if="p.reply">%fa:reply%</a>
- <mk-note-html v-if="p.text" :text="p.text" :i="os.i" :class="$style.text"/>
+ <mk-note-html v-if="p.text && !canHideText(p)" :text="p.text" :i="os.i" :class="$style.text"/>
<a class="rp" v-if="p.renote">RP:</a>
</div>
<div class="media" v-if="p.media.length > 0">
@@ -94,6 +94,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 MkPostFormWindow from './post-form-window.vue';
@@ -130,16 +131,17 @@ export default Vue.extend({
},
computed: {
-
isRenote(): boolean {
return (this.note.renote &&
this.note.text == null &&
this.note.mediaIds.length == 0 &&
this.note.poll == null);
},
+
p(): any {
return this.isRenote ? this.note.renote : this.note;
},
+
reactionsCount(): number {
return this.p.reactionCounts
? Object.keys(this.p.reactionCounts)
@@ -147,9 +149,11 @@ export default Vue.extend({
.reduce((a, b) => a + b)
: 0;
},
+
title(): string {
return dateStringify(this.p.createdAt);
},
+
urls(): string[] {
if (this.p.text) {
const ast = parse(this.p.text);
@@ -205,6 +209,8 @@ export default Vue.extend({
},
methods: {
+ canHideText,
+
capture(withHandler = false) {
if ((this as any).os.isSignedIn) {
this.connection.send({
@@ -214,6 +220,7 @@ export default Vue.extend({
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
}
},
+
decapture(withHandler = false) {
if ((this as any).os.isSignedIn) {
this.connection.send({
@@ -223,9 +230,11 @@ export default Vue.extend({
if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
}
},
+
onStreamConnected() {
this.capture();
},
+
onStreamNoteUpdated(data) {
const note = data.note;
if (note.id == this.note.id) {
@@ -234,28 +243,33 @@ export default Vue.extend({
this.note.renote = note;
}
},
+
reply() {
(this as any).os.new(MkPostFormWindow, {
reply: this.p
});
},
+
renote() {
(this as any).os.new(MkRenoteFormWindow, {
note: this.p
});
},
+
react() {
(this as any).os.new(MkReactionPicker, {
source: this.$refs.reactButton,
note: this.p
});
},
+
menu() {
(this as any).os.new(MkNoteMenu, {
source: this.$refs.menuButton,
note: this.p
});
},
+
onKeydown(e) {
let shouldBeCancel = true;
@@ -336,6 +350,7 @@ root(isDark)
align-items center
padding 16px 32px
line-height 28px
+ white-space pre
color #9dbb00
background isDark ? linear-gradient(to bottom, #314027 0%, #282c37 100%) : linear-gradient(to bottom, #edfde2 0%, #fff 100%)
diff --git a/src/client/app/desktop/views/components/settings.api.vue b/src/client/app/desktop/views/components/settings.api.vue
index 377f2e689b..b22ee6cdab 100644
--- a/src/client/app/desktop/views/components/settings.api.vue
+++ b/src/client/app/desktop/views/components/settings.api.vue
@@ -1,6 +1,6 @@
<template>
<div class="root api">
- <p>Token: <code>{{ os.i.token }}</code></p>
+ <p>%i18n:@token% <code>{{ os.i.token }}</code></p>
<p>%i18n:@intro%</p>
<div class="ui info warn"><p>%fa:exclamation-triangle%%i18n:@caution%</p></div>
<p>%i18n:@regeneration-of-token%</p>
diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue
index 9439ded2fc..4e5e281fd0 100644
--- a/src/client/app/desktop/views/components/settings.vue
+++ b/src/client/app/desktop/views/components/settings.vue
@@ -80,10 +80,11 @@
<el-option label="自動" value=""/>
</el-option-group>
<el-option-group label="言語を指定">
- <el-option label="ja-JP" value="ja"/>
- <el-option label="en-US" value="en"/>
+ <el-option label="ja" value="ja"/>
+ <el-option label="en" value="en"/>
<el-option label="fr" value="fr"/>
<el-option label="pl" value="pl"/>
+ <el-option label="de" value="de"/>
</el-option-group>
</el-select>
<div class="none ui info">
@@ -100,7 +101,7 @@
</section>
<section class="notification" v-show="page == 'notification'">
- <h1>通知</h1>
+ <h1>%i18n:@notification%</h1>
<mk-switch v-model="os.i.settings.autoWatch" @change="onChangeAutoWatch" text="投稿の自動ウォッチ">
<span>リアクションしたり返信したりした投稿に関する通知を自動的に受け取るようにします。</span>
</mk-switch>
@@ -117,7 +118,7 @@
</section>
<section class="apps" v-show="page == 'apps'">
- <h1>アプリケーション</h1>
+ <h1>%i18n:@apps%</h1>
<x-apps/>
</section>
@@ -137,7 +138,7 @@
</section>
<section class="signin" v-show="page == 'security'">
- <h1>サインイン履歴</h1>
+ <h1>%i18n:@signin%</h1>
<x-signins/>
</section>
diff --git a/src/client/app/desktop/views/components/user-lists-window.vue b/src/client/app/desktop/views/components/user-lists-window.vue
index d082610132..585c0a864f 100644
--- a/src/client/app/desktop/views/components/user-lists-window.vue
+++ b/src/client/app/desktop/views/components/user-lists-window.vue
@@ -3,7 +3,7 @@
<span slot="header">%fa:list% リスト</span>
<div data-id="6e4caea3-d8f9-4ab7-96de-ab67fe8d5c82" :data-darkmode="_darkmode_">
- <button class="ui" @click="add">リストを作成</button>
+ <button class="ui" @click="add">%i18n:@create-list%</button>
<a v-for="list in lists" :key="list.id" @click="choice(list)">{{ list.title }}</a>
</div>
</mk-window>
diff --git a/src/client/app/desktop/views/components/user-preview.vue b/src/client/app/desktop/views/components/user-preview.vue
index cc5e021390..f40e60dff9 100644
--- a/src/client/app/desktop/views/components/user-preview.vue
+++ b/src/client/app/desktop/views/components/user-preview.vue
@@ -10,13 +10,13 @@
<div class="description">{{ u.description }}</div>
<div class="status">
<div>
- <p>投稿</p><a>{{ u.notesCount }}</a>
+ <p>%i18n:@notes%</p><a>{{ u.notesCount }}</a>
</div>
<div>
- <p>フォロー</p><a>{{ u.followingCount }}</a>
+ <p>%i18n:@following%</p><a>{{ u.followingCount }}</a>
</div>
<div>
- <p>フォロワー</p><a>{{ u.followersCount }}</a>
+ <p>%i18n:@followers%</p><a>{{ u.followersCount }}</a>
</div>
</div>
<mk-follow-button v-if="os.isSignedIn && user.id != os.i.id" :user="u"/>
diff --git a/src/client/app/desktop/views/components/users-list.vue b/src/client/app/desktop/views/components/users-list.vue
index 13d0d07bbc..1ed5c33b13 100644
--- a/src/client/app/desktop/views/components/users-list.vue
+++ b/src/client/app/desktop/views/components/users-list.vue
@@ -2,8 +2,8 @@
<div class="mk-users-list">
<nav>
<div>
- <span :data-active="mode == 'all'" @click="mode = 'all'">すべて<span>{{ count }}</span></span>
- <span v-if="os.isSignedIn && youKnowCount" :data-active="mode == 'iknow'" @click="mode = 'iknow'">知り合い<span>{{ youKnowCount }}</span></span>
+ <span :data-active="mode == 'all'" @click="mode = 'all'">%i18n:@all%<span>{{ count }}</span></span>
+ <span v-if="os.isSignedIn && youKnowCount" :data-active="mode == 'iknow'" @click="mode = 'iknow'">%i18n:@iknow%<span>{{ youKnowCount }}</span></span>
</div>
</nav>
<div class="users" v-if="!fetching && users.length != 0">
@@ -12,13 +12,13 @@
</div>
</div>
<button class="more" v-if="!fetching && next != null" @click="more" :disabled="moreFetching">
- <span v-if="!moreFetching">もっと</span>
- <span v-if="moreFetching">読み込み中<mk-ellipsis/></span>
+ <span v-if="!moreFetching">%i18n:@load-more%</span>
+ <span v-if="moreFetching">%i18n:common.loading%<mk-ellipsis/></span>
</button>
<p class="no" v-if="!fetching && users.length == 0">
<slot></slot>
</p>
- <p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%読み込んでいます<mk-ellipsis/></p>
+ <p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:@fetching%<mk-ellipsis/></p>
</div>
</template>
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 60dc15b15d..edb248dac7 100644
--- a/src/client/app/desktop/views/pages/user/user.header.vue
+++ b/src/client/app/desktop/views/pages/user/user.header.vue
@@ -29,7 +29,7 @@ export default Vue.extend({
style(): any {
if (this.user.bannerUrl == null) return {};
return {
- backgroundColor: this.user.bannerColor ? `rgb(${ this.user.bannerColor.join(',') })` : null,
+ backgroundColor: this.user.bannerColor && this.user.bannerColor.length == 3 ? `rgb(${ this.user.bannerColor.join(',') })` : null,
backgroundImage: `url(${ this.user.bannerUrl })`
};
}
diff --git a/src/client/app/mobile/api/post.ts b/src/client/app/mobile/api/post.ts
index 72919c6505..0634c52642 100644
--- a/src/client/app/mobile/api/post.ts
+++ b/src/client/app/mobile/api/post.ts
@@ -1,43 +1,24 @@
import PostForm from '../views/components/post-form.vue';
-//import RenoteForm from '../views/components/renote-form.vue';
-import getNoteSummary from '../../../../renderers/get-note-summary';
export default (os) => (opts) => {
const o = opts || {};
- if (o.renote) {
- /*const vm = new RenoteForm({
- propsData: {
- renote: o.renote
- }
- }).$mount();
- vm.$once('cancel', recover);
- vm.$once('note', recover);
- document.body.appendChild(vm.$el);*/
+ const app = document.getElementById('app');
+ app.style.display = 'none';
- const text = window.prompt(`「${getNoteSummary(o.renote)}」をRenote`);
- if (text == null) return;
- os.api('notes/create', {
- renoteId: o.renote.id,
- text: text == '' ? undefined : text
- });
- } else {
- const app = document.getElementById('app');
- app.style.display = 'none';
+ function recover() {
+ app.style.display = 'block';
+ }
- function recover() {
- app.style.display = 'block';
+ const vm = new PostForm({
+ parent: os.app,
+ propsData: {
+ reply: o.reply,
+ renote: o.renote
}
-
- const vm = new PostForm({
- parent: os.app,
- propsData: {
- reply: o.reply
- }
- }).$mount();
- vm.$once('cancel', recover);
- vm.$once('note', recover);
- document.body.appendChild(vm.$el);
- (vm as any).focus();
- }
+ }).$mount();
+ vm.$once('cancel', recover);
+ vm.$once('note', recover);
+ document.body.appendChild(vm.$el);
+ (vm as any).focus();
};
diff --git a/src/client/app/mobile/script.ts b/src/client/app/mobile/script.ts
index 2e9805e0d0..1405139be6 100644
--- a/src/client/app/mobile/script.ts
+++ b/src/client/app/mobile/script.ts
@@ -23,6 +23,7 @@ import MkUser from './views/pages/user.vue';
import MkSelectDrive from './views/pages/selectdrive.vue';
import MkDrive from './views/pages/drive.vue';
import MkNotifications from './views/pages/notifications.vue';
+import MkWidgets from './views/pages/widgets.vue';
import MkMessaging from './views/pages/messaging.vue';
import MkMessagingRoom from './views/pages/messaging-room.vue';
import MkNote from './views/pages/note.vue';
@@ -56,6 +57,7 @@ init((launch) => {
{ path: '/i/settings', component: MkSettings },
{ path: '/i/settings/profile', component: MkProfileSetting },
{ path: '/i/notifications', name: 'notifications', component: MkNotifications },
+ { path: '/i/widgets', name: 'widgets', component: MkWidgets },
{ path: '/i/messaging', name: 'messaging', component: MkMessaging },
{ path: '/i/messaging/:user', component: MkMessagingRoom },
{ path: '/i/drive', name: 'drive', component: MkDrive },
diff --git a/src/client/app/mobile/views/components/drive.file-detail.vue b/src/client/app/mobile/views/components/drive.file-detail.vue
index 764822e98c..ddf17d2723 100644
--- a/src/client/app/mobile/views/components/drive.file-detail.vue
+++ b/src/client/app/mobile/views/components/drive.file-detail.vue
@@ -86,7 +86,7 @@ export default Vue.extend({
return this.file.type.split('/')[0];
},
style(): any {
- return this.file.properties.avgColor ? {
+ return this.file.properties.avgColor && this.file.properties.avgColor.length == 3 ? {
'background-color': `rgb(${ this.file.properties.avgColor.join(',') })`
} : {};
}
diff --git a/src/client/app/mobile/views/components/drive.file.vue b/src/client/app/mobile/views/components/drive.file.vue
index 7d1957042b..94c8ae3535 100644
--- a/src/client/app/mobile/views/components/drive.file.vue
+++ b/src/client/app/mobile/views/components/drive.file.vue
@@ -42,7 +42,7 @@ export default Vue.extend({
},
thumbnail(): any {
return {
- 'background-color': this.file.properties.avgColor ? `rgb(${this.file.properties.avgColor.join(',')})` : 'transparent',
+ 'background-color': this.file.properties.avgColor && this.file.properties.avgColor.length == 3 ? `rgb(${this.file.properties.avgColor.join(',')})` : 'transparent',
'background-image': `url(${this.file.url}?thumbnail&size=128)`
};
}
diff --git a/src/client/app/mobile/views/components/media-image.vue b/src/client/app/mobile/views/components/media-image.vue
index 92d1cdc6f5..9e0f8e5f7e 100644
--- a/src/client/app/mobile/views/components/media-image.vue
+++ b/src/client/app/mobile/views/components/media-image.vue
@@ -18,7 +18,7 @@ export default Vue.extend({
computed: {
style(): any {
return {
- 'background-color': this.image.properties.avgColor ? `rgb(${this.image.properties.avgColor.join(',')})` : 'transparent',
+ 'background-color': this.image.properties.avgColor && this.image.properties.avgColor.length == 3 ? `rgb(${this.image.properties.avgColor.join(',')})` : 'transparent',
'background-image': this.raw ? `url(${this.image.url})` : `url(${this.image.url}?thumbnail&size=512)`
};
}
diff --git a/src/client/app/mobile/views/components/note-preview.vue b/src/client/app/mobile/views/components/note-preview.vue
index ec11f23315..b3ab088ffe 100644
--- a/src/client/app/mobile/views/components/note-preview.vue
+++ b/src/client/app/mobile/views/components/note-preview.vue
@@ -69,8 +69,9 @@ root(isDark)
text-decoration underline
> .username
- text-align left
margin 0 .5em 0 0
+ overflow hidden
+ text-overflow ellipsis
color isDark ? #606984 : #d1d8da
> .time
diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue
index d66f5a1016..77a766f327 100644
--- a/src/client/app/mobile/views/components/note.vue
+++ b/src/client/app/mobile/views/components/note.vue
@@ -41,7 +41,7 @@
<div class="text">
<span v-if="p.isHidden" style="opacity: 0.5">(この投稿は非公開です)</span>
<a class="reply" v-if="p.reply">%fa:reply%</a>
- <mk-note-html v-if="p.text" :text="p.text" :i="os.i" :class="$style.text"/>
+ <mk-note-html v-if="p.text && !canHideText(p)" :text="p.text" :i="os.i" :class="$style.text"/>
<a class="rp" v-if="p.renote != null">RP:</a>
</div>
<div class="media" v-if="p.media.length > 0">
@@ -85,6 +85,7 @@
<script lang="ts">
import Vue from 'vue';
import parse from '../../../../../text/parse';
+import canHideText from '../../../common/scripts/can-hide-text';
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
@@ -112,9 +113,11 @@ export default Vue.extend({
this.note.mediaIds.length == 0 &&
this.note.poll == null);
},
+
p(): any {
return this.isRenote ? this.note.renote : this.note;
},
+
reactionsCount(): number {
return this.p.reactionCounts
? Object.keys(this.p.reactionCounts)
@@ -122,6 +125,7 @@ export default Vue.extend({
.reduce((a, b) => a + b)
: 0;
},
+
urls(): string[] {
if (this.p.text) {
const ast = parse(this.p.text);
@@ -177,6 +181,8 @@ export default Vue.extend({
},
methods: {
+ canHideText,
+
capture(withHandler = false) {
if ((this as any).os.isSignedIn) {
this.connection.send({
@@ -186,6 +192,7 @@ export default Vue.extend({
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
}
},
+
decapture(withHandler = false) {
if ((this as any).os.isSignedIn) {
this.connection.send({
@@ -195,9 +202,11 @@ export default Vue.extend({
if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
}
},
+
onStreamConnected() {
this.capture();
},
+
onStreamNoteUpdated(data) {
const note = data.note;
if (note.id == this.note.id) {
@@ -206,16 +215,19 @@ export default Vue.extend({
this.note.renote = note;
}
},
+
reply() {
(this as any).apis.post({
reply: this.p
});
},
+
renote() {
(this as any).apis.post({
renote: this.p
});
},
+
react() {
(this as any).os.new(MkReactionPicker, {
source: this.$refs.reactButton,
@@ -223,6 +235,7 @@ export default Vue.extend({
compact: true
});
},
+
menu() {
(this as any).os.new(MkNoteMenu, {
source: this.$refs.menuButton,
@@ -255,6 +268,7 @@ root(isDark)
align-items center
padding 8px 16px
line-height 28px
+ white-space pre
color #9dbb00
background isDark ? linear-gradient(to bottom, #314027 0%, #282c37 100%) : linear-gradient(to bottom, #edfde2 0%, #fff 100%)
diff --git a/src/client/app/mobile/views/components/notes.vue b/src/client/app/mobile/views/components/notes.vue
index 53e232e521..e77698dea9 100644
--- a/src/client/app/mobile/views/components/notes.vue
+++ b/src/client/app/mobile/views/components/notes.vue
@@ -1,7 +1,5 @@
<template>
<div class="mk-notes">
- <div class="newer-indicator" :style="{ top: $store.state.uiHeaderHeight + 'px' }" v-show="queue.length > 0"></div>
-
<slot name="head"></slot>
<slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
@@ -71,6 +69,16 @@ export default Vue.extend({
}
},
+ watch: {
+ queue(x) {
+ if (x.length > 0) {
+ this.$store.commit('indicate', true);
+ } else {
+ this.$store.commit('indicate', false);
+ }
+ }
+ },
+
mounted() {
document.addEventListener('visibilitychange', this.onVisibilitychange, false);
window.addEventListener('scroll', this.onScroll);
@@ -238,13 +246,6 @@ root(isDark)
[data-fa]
margin-right 8px
- > .newer-indicator
- position -webkit-sticky
- position sticky
- z-index 100
- height 3px
- background $theme-color
-
> .init
padding 64px 0
text-align center
diff --git a/src/client/app/mobile/views/components/post-form.vue b/src/client/app/mobile/views/components/post-form.vue
index 6d80b3046b..0bb498e5d7 100644
--- a/src/client/app/mobile/views/components/post-form.vue
+++ b/src/client/app/mobile/views/components/post-form.vue
@@ -5,17 +5,22 @@
<div>
<span class="text-count" :class="{ over: text.length > 1000 }">{{ 1000 - text.length }}</span>
<span class="geo" v-if="geo">%fa:map-marker-alt%</span>
- <button class="submit" :disabled="posting" @click="post">{{ reply ? '返信' : '%i18n:!@submit%' }}</button>
+ <button class="submit" :disabled="posting" @click="post">
+ <template v-if="reply">%i18n:@reply%</template>
+ <template v-else-if="renote">%i18n:@renote%</template>
+ <template v-else>%i18n:@submit%</template>
+ </button>
</div>
</header>
<div class="form">
<mk-note-preview v-if="reply" :note="reply"/>
+ <mk-note-preview v-if="renote" :note="renote"/>
<div v-if="visibility == 'specified'" class="visibleUsers">
<span v-for="u in visibleUsers">{{ u | userName }}<a @click="removeVisibleUser(u)">[x]</a></span>
<a @click="addVisibleUser">+ユーザーを追加</a>
</div>
<input v-show="useCw" v-model="cw" placeholder="内容への注釈 (オプション)">
- <textarea v-model="text" ref="text" :disabled="posting" :placeholder="reply ? '%i18n:!@reply-placeholder%' : '%i18n:!@note-placeholder%'"></textarea>
+ <textarea v-model="text" ref="text" :disabled="posting" :placeholder="reply ? '%i18n:!@reply-placeholder%' : renote ? '%i18n:!@renote-placeholder%' : '%i18n:!@note-placeholder%'"></textarea>
<div class="attaches" v-show="files.length != 0">
<x-draggable class="files" :list="files" :options="{ animation: 150 }">
<div class="file" v-for="file in files" :key="file.id">
@@ -51,7 +56,7 @@ export default Vue.extend({
MkVisibilityChooser
},
- props: ['reply'],
+ props: ['reply', 'renote'],
data() {
return {
@@ -177,6 +182,7 @@ export default Vue.extend({
text: this.text == '' ? undefined : this.text,
mediaIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
replyId: this.reply ? this.reply.id : undefined,
+ renoteId: this.renote ? this.renote.id : undefined,
poll: this.poll ? (this.$refs.poll as any).get() : undefined,
cw: this.useCw ? this.cw || '' : undefined,
geo: this.geo ? {
diff --git a/src/client/app/mobile/views/components/ui.header.vue b/src/client/app/mobile/views/components/ui.header.vue
index 509463333d..a49462b159 100644
--- a/src/client/app/mobile/views/components/ui.header.vue
+++ b/src/client/app/mobile/views/components/ui.header.vue
@@ -13,6 +13,7 @@
<slot name="func"></slot>
</div>
</div>
+ <div class="indicator" v-show="$store.state.indicate"></div>
</div>
</template>
@@ -156,6 +157,10 @@ root(isDark)
&, *
user-select none
+ > .indicator
+ height 3px
+ background $theme-color
+
> .main
color rgba(#fff, 0.9)
diff --git a/src/client/app/mobile/views/components/ui.nav.vue b/src/client/app/mobile/views/components/ui.nav.vue
index 5c65d52237..ec42dbc99d 100644
--- a/src/client/app/mobile/views/components/ui.nav.vue
+++ b/src/client/app/mobile/views/components/ui.nav.vue
@@ -21,6 +21,7 @@
<li><router-link to="/othello" :data-active="$route.name == 'othello'">%fa:gamepad%ゲーム<template v-if="hasGameInvitations">%fa:circle%</template>%fa:angle-right%</router-link></li>
</ul>
<ul>
+ <li><router-link to="/i/widgets" :data-active="$route.name == 'widgets'">%fa:quidditch%%i18n:@widgets%%fa:angle-right%</router-link></li>
<li><router-link to="/i/drive" :data-active="$route.name == 'drive'">%fa:cloud%%i18n:@drive%%fa:angle-right%</router-link></li>
</ul>
<ul>
diff --git a/src/client/app/mobile/views/components/widget-container.vue b/src/client/app/mobile/views/components/widget-container.vue
index 1bdc875763..a713a10621 100644
--- a/src/client/app/mobile/views/components/widget-container.vue
+++ b/src/client/app/mobile/views/components/widget-container.vue
@@ -25,27 +25,27 @@ export default Vue.extend({
</script>
<style lang="stylus" scoped>
-.mk-widget-container
- background #eee
+root(isDark)
+ background isDark ? #21242f : #eee
border-radius 8px
- box-shadow 0 0 0 1px rgba(#000, 0.2)
+ box-shadow 0 4px 16px rgba(#000, 0.1)
overflow hidden
- &.hideHeader
- background #fff
-
&.naked
background transparent !important
box-shadow none !important
+ &.hideHeader
+ background isDark ? #21242f : #fff
+
> header
> .title
margin 0
padding 8px 10px
font-size 15px
font-weight normal
- color #465258
- background #fff
+ color isDark ? #b8c5cc : #465258
+ background isDark ? #282c37 : #fff
border-radius 8px 8px 0 0
> [data-fa]
@@ -65,4 +65,10 @@ export default Vue.extend({
font-size 15px
color #465258
+.mk-widget-container[data-darkmode]
+ root(true)
+
+.mk-widget-container:not([data-darkmode])
+ root(false)
+
</style>
diff --git a/src/client/app/mobile/views/pages/user.vue b/src/client/app/mobile/views/pages/user.vue
index 27482dc215..f43454f9db 100644
--- a/src/client/app/mobile/views/pages/user.vue
+++ b/src/client/app/mobile/views/pages/user.vue
@@ -84,7 +84,7 @@ export default Vue.extend({
style(): any {
if (this.user.bannerUrl == null) return {};
return {
- backgroundColor: this.user.bannerColor ? `rgb(${ this.user.bannerColor.join(',') })` : null,
+ backgroundColor: this.user.bannerColor && this.user.bannerColor.length == 3 ? `rgb(${ this.user.bannerColor.join(',') })` : null,
backgroundImage: `url(${ this.user.bannerUrl })`
};
}
diff --git a/src/client/app/mobile/views/pages/dashboard.vue b/src/client/app/mobile/views/pages/widgets.vue
index a5ca6cb4a2..509ce16eef 100644
--- a/src/client/app/mobile/views/pages/dashboard.vue
+++ b/src/client/app/mobile/views/pages/widgets.vue
@@ -40,7 +40,7 @@
</x-draggable>
</template>
<template v-else>
- <component class="widget" v-for="widget in widgets" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" :is-mobile="true" @chosen="warp"/>
+ <component class="widget" v-for="widget in widgets" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" :is-mobile="true"/>
</template>
</main>
</mk-ui>
@@ -55,17 +55,24 @@ export default Vue.extend({
components: {
XDraggable
},
+
data() {
return {
showNav: false,
- widgets: [],
customizing: false,
widgetAdderSelected: null
};
},
+
+ computed: {
+ widgets(): any[] {
+ return this.$store.state.settings.data.mobileHome;
+ }
+ },
+
created() {
- if ((this as any).clientSettings.mobileHome == null) {
- Vue.set((this as any).clientSettings, 'mobileHome', [{
+ if (this.widgets.length == 0) {
+ this.widgets = [{
name: 'calendar',
id: 'a', data: {}
}, {
@@ -86,18 +93,9 @@ export default Vue.extend({
}, {
name: 'version',
id: 'g', data: {}
- }]);
- this.widgets = (this as any).clientSettings.mobileHome;
+ }];
this.saveHome();
- } else {
- this.widgets = (this as any).clientSettings.mobileHome;
}
-
- this.$watch('clientSettings', i => {
- this.widgets = (this as any).clientSettings.mobileHome;
- }, {
- deep: true
- });
},
mounted() {
@@ -105,46 +103,33 @@ export default Vue.extend({
},
methods: {
- onHomeUpdated(data) {
- if (data.home) {
- (this as any).clientSettings.mobileHome = data.home;
- this.widgets = data.home;
- } else {
- const w = (this as any).clientSettings.mobileHome.find(w => w.id == data.id);
- if (w != null) {
- w.data = data.data;
- this.$refs[w.id][0].preventSave = true;
- this.$refs[w.id][0].props = w.data;
- this.widgets = (this as any).clientSettings.mobileHome;
- }
- }
- },
hint() {
alert('ウィジェットを追加/削除したり並べ替えたりできます。ウィジェットを移動するには「三」をドラッグします。ウィジェットを削除するには「x」をタップします。いくつかのウィジェットはタップすることで表示を変更できます。');
},
+
widgetFunc(id) {
const w = this.$refs[id][0];
if (w.func) w.func();
},
+
onWidgetSort() {
this.saveHome();
},
+
addWidget() {
- const widget = {
+ this.$store.dispatch('settings/addMobileHomeWidget', {
name: this.widgetAdderSelected,
id: uuid(),
data: {}
- };
-
- this.widgets.unshift(widget);
- this.saveHome();
+ });
},
+
removeWidget(widget) {
- this.widgets = this.widgets.filter(w => w.id != widget.id);
- this.saveHome();
+ this.$store.dispatch('settings/removeMobileHomeWidget', widget);
},
+
saveHome() {
- (this as any).clientSettings.mobileHome = this.widgets;
+ this.$store.commit('settings/setMobileHome', this.widgets);
(this as any).api('i/update_mobile_home', {
home: this.widgets
});
@@ -156,17 +141,25 @@ export default Vue.extend({
<style lang="stylus" scoped>
main
margin 0 auto
+ padding 8px
max-width 500px
+ width 100%
@media (min-width 500px)
- padding 8px
+ padding 16px 8px
+
+ @media (min-width 600px)
+ padding 32px 8px
> header
padding 8px
background #fff
.widget
- margin 8px
+ margin-bottom 8px
+
+ @media (min-width 600px)
+ margin-bottom 16px
.customize-container
margin 8px
diff --git a/src/client/app/store.ts b/src/client/app/store.ts
index 0bdfdef6a0..1f1189054d 100644
--- a/src/client/app/store.ts
+++ b/src/client/app/store.ts
@@ -3,6 +3,7 @@ import MiOS from './mios';
const defaultSettings = {
home: [],
+ mobileHome: [],
fetchOnScroll: true,
showMaps: true,
showPostFormOnTopOfTl: false,
@@ -23,10 +24,15 @@ export default (os: MiOS) => new Vuex.Store({
}],
state: {
+ indicate: false,
uiHeaderHeight: 0
},
mutations: {
+ indicate(state, x) {
+ state.indicate = x;
+ },
+
setUiHeaderHeight(state, height) {
state.uiHeaderHeight = height;
}
@@ -58,6 +64,25 @@ export default (os: MiOS) => new Vuex.Store({
addHomeWidget(state, widget) {
state.data.home.unshift(widget);
+ },
+
+ setMobileHome(state, data) {
+ state.data.mobileHome = data;
+ },
+
+ setMobileHomeWidget(state, x) {
+ const w = state.data.mobileHome.find(w => w.id == x.id);
+ if (w) {
+ w.data = x.data;
+ }
+ },
+
+ addMobileHomeWidget(state, widget) {
+ state.data.mobileHome.unshift(widget);
+ },
+
+ removeMobileHomeWidget(state, widget) {
+ state.data.mobileHome = state.data.mobileHome.filter(w => w.id != widget.id);
}
},
@@ -85,6 +110,22 @@ export default (os: MiOS) => new Vuex.Store({
os.api('i/update_home', {
home: ctx.state.data.home
});
+ },
+
+ addMobileHomeWidget(ctx, widget) {
+ ctx.commit('addMobileHomeWidget', widget);
+
+ os.api('i/update_mobile_home', {
+ home: ctx.state.data.mobileHome
+ });
+ },
+
+ removeMobileHomeWidget(ctx, widget) {
+ ctx.commit('removeMobileHomeWidget', widget);
+
+ os.api('i/update_mobile_home', {
+ home: ctx.state.data.mobileHome.filter(w => w.id != widget.id)
+ });
}
}
}
diff --git a/src/client/docs/api/gulpfile.ts b/src/client/docs/api/gulpfile.ts
index 31027c0be3..9980ede231 100644
--- a/src/client/docs/api/gulpfile.ts
+++ b/src/client/docs/api/gulpfile.ts
@@ -127,7 +127,7 @@ gulp.task('doc:api:endpoints', async () => {
return;
}
const i18n = new I18nReplacer(lang);
- html = html.replace(i18n.pattern, i18n.replacement.bind(null, null));
+ html = html.replace(i18n.pattern, i18n.replacement);
html = fa(html);
const htmlPath = `./built/client/docs/${lang}/api/endpoints/${ep.endpoint}.html`;
mkdirp(path.dirname(htmlPath), (mkdirErr) => {
@@ -171,7 +171,7 @@ gulp.task('doc:api:entities', async () => {
return;
}
const i18n = new I18nReplacer(lang);
- html = html.replace(i18n.pattern, i18n.replacement.bind(null, null));
+ html = html.replace(i18n.pattern, i18n.replacement);
html = fa(html);
const htmlPath = `./built/client/docs/${lang}/api/entities/${kebab(entity.name)}.html`;
mkdirp(path.dirname(htmlPath), (mkdirErr) => {
diff --git a/src/client/docs/gulpfile.ts b/src/client/docs/gulpfile.ts
index 5e81d6d3b5..56bf6188c8 100644
--- a/src/client/docs/gulpfile.ts
+++ b/src/client/docs/gulpfile.ts
@@ -53,7 +53,7 @@ gulp.task('doc:docs', async () => {
return;
}
const i18n = new I18nReplacer(lang);
- html = html.replace(i18n.pattern, i18n.replacement.bind(null, null));
+ html = html.replace(i18n.pattern, i18n.replacement);
html = fa(html);
const htmlPath = `./built/client/docs/${lang}/${name}.html`;
mkdirp(path.dirname(htmlPath), (mkdirErr) => {
diff --git a/src/models/drive-file.ts b/src/models/drive-file.ts
index f8cad36f9a..8a18567dc6 100644
--- a/src/models/drive-file.ts
+++ b/src/models/drive-file.ts
@@ -154,6 +154,7 @@ export const pack = (
_target = Object.assign(_target, _file.metadata);
+ _target.src = _file.metadata.url;
_target.url = `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}`;
if (_target.properties == null) _target.properties = {};
diff --git a/src/server/web/url-preview.ts b/src/server/web/url-preview.ts
index cd53837a25..99ee2eaebd 100644
--- a/src/server/web/url-preview.ts
+++ b/src/server/web/url-preview.ts
@@ -2,14 +2,20 @@ import * as Koa from 'koa';
import summaly from 'summaly';
module.exports = async (ctx: Koa.Context) => {
- const summary = await summaly(ctx.query.url);
- summary.icon = wrap(summary.icon);
- summary.thumbnail = wrap(summary.thumbnail);
+ try {
+ const summary = await summaly(ctx.query.url, {
+ followRedirects: false
+ });
+ summary.icon = wrap(summary.icon);
+ summary.thumbnail = wrap(summary.thumbnail);
- // Cache 7days
- ctx.set('Cache-Control', 'max-age=604800, immutable');
+ // Cache 7days
+ ctx.set('Cache-Control', 'max-age=604800, immutable');
- ctx.body = summary;
+ ctx.body = summary;
+ } catch (e) {
+ ctx.status = 500;
+ }
};
function wrap(url: string): string {
diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts
index efabe345d1..bcd5bee512 100644
--- a/src/services/drive/add-file.ts
+++ b/src/services/drive/add-file.ts
@@ -171,6 +171,9 @@ const addFile = async (
log('calculate average color...');
+ const info = await prominence(gm(fs.createReadStream(path), name)).identify();
+ const isTransparent = info ? info['Channel depth'].Alpha != null : false;
+
const buffer = await prominence(gm(fs.createReadStream(path), name)
.setFormat('ppm')
.resize(1, 1)) // 1pxのサイズに縮小して平均色を取得するというハック
@@ -182,7 +185,7 @@ const addFile = async (
log(`average color is calculated: ${r}, ${g}, ${b}`);
- return [r, g, b];
+ return isTransparent ? [r, g, b, 255] : [r, g, b];
})(),
// folder
(async () => {