summaryrefslogtreecommitdiff
path: root/src/client/app/desktop
diff options
context:
space:
mode:
authortamaina <tamaina@hotmail.co.jp>2018-09-15 22:15:56 +0900
committerGitHub <noreply@github.com>2018-09-15 22:15:56 +0900
commit8f8c67ad6d33eb9ced1844bb372d517204df6c6f (patch)
treebcaedee83bb5960a6b63291795dd95f9ed603a99 /src/client/app/desktop
parentfix mk-media darkmode (diff)
parent8.41.0 (diff)
downloadmisskey-8f8c67ad6d33eb9ced1844bb372d517204df6c6f.tar.gz
misskey-8f8c67ad6d33eb9ced1844bb372d517204df6c6f.tar.bz2
misskey-8f8c67ad6d33eb9ced1844bb372d517204df6c6f.zip
Merge branch 'develop' into improve-media
Diffstat (limited to 'src/client/app/desktop')
-rw-r--r--src/client/app/desktop/script.ts1
-rw-r--r--src/client/app/desktop/views/components/charts.vue103
-rw-r--r--src/client/app/desktop/views/components/context-menu.vue2
-rw-r--r--src/client/app/desktop/views/components/dialog.vue2
-rw-r--r--src/client/app/desktop/views/components/friends-maker.vue2
-rw-r--r--src/client/app/desktop/views/components/media-image-dialog.vue2
-rw-r--r--src/client/app/desktop/views/components/media-image.vue2
-rw-r--r--src/client/app/desktop/views/components/media-video-dialog.vue2
-rw-r--r--src/client/app/desktop/views/components/note-detail.vue100
-rw-r--r--src/client/app/desktop/views/components/note-preview.vue37
-rw-r--r--src/client/app/desktop/views/components/notes.note.sub.vue50
-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.vue6
-rw-r--r--src/client/app/desktop/views/components/post-form-window.vue10
-rw-r--r--src/client/app/desktop/views/components/post-form.vue40
-rw-r--r--src/client/app/desktop/views/components/renote-form.vue4
-rw-r--r--src/client/app/desktop/views/components/settings.drive.vue1
-rw-r--r--src/client/app/desktop/views/components/settings.profile.vue9
-rw-r--r--src/client/app/desktop/views/components/settings.vue293
-rw-r--r--src/client/app/desktop/views/components/sub-note-content.vue6
-rw-r--r--src/client/app/desktop/views/components/taskmanager.vue219
-rw-r--r--src/client/app/desktop/views/components/timeline.vue11
-rw-r--r--src/client/app/desktop/views/components/ui-notification.vue2
-rw-r--r--src/client/app/desktop/views/components/user-preview.vue2
-rw-r--r--src/client/app/desktop/views/components/users-list.item.vue126
-rw-r--r--src/client/app/desktop/views/components/users-list.vue26
-rw-r--r--src/client/app/desktop/views/components/window.vue4
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.dashboard.vue35
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.hashtags.vue41
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.vue7
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.column.vue3
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.list-tl.vue6
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.note.vue20
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.notes.vue2
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.tl.vue6
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.vue1
-rw-r--r--src/client/app/desktop/views/pages/user/user.header.vue2
-rw-r--r--src/client/app/desktop/views/pages/user/user.photos.vue6
-rw-r--r--src/client/app/desktop/views/pages/user/user.timeline.vue4
-rw-r--r--src/client/app/desktop/views/pages/welcome.vue442
-rw-r--r--src/client/app/desktop/views/widgets/trends.vue2
42 files changed, 971 insertions, 715 deletions
diff --git a/src/client/app/desktop/script.ts b/src/client/app/desktop/script.ts
index f0e8a42662..e32682286c 100644
--- a/src/client/app/desktop/script.ts
+++ b/src/client/app/desktop/script.ts
@@ -6,7 +6,6 @@ import VueRouter from 'vue-router';
// Style
import './style.styl';
-import '../../element.scss';
import init from '../init';
import fuckAdBlock from '../common/scripts/fuck-ad-block';
diff --git a/src/client/app/desktop/views/components/charts.vue b/src/client/app/desktop/views/components/charts.vue
index c4e92e429f..6514cdf788 100644
--- a/src/client/app/desktop/views/components/charts.vue
+++ b/src/client/app/desktop/views/components/charts.vue
@@ -19,6 +19,11 @@
<option value="drive">%i18n:@charts.drive%</option>
<option value="drive-total">%i18n:@charts.drive-total%</option>
</optgroup>
+ <optgroup label="%i18n:@network%">
+ <option value="network-requests">%i18n:@charts.network-requests%</option>
+ <option value="network-time">%i18n:@charts.network-time%</option>
+ <option value="network-usage">%i18n:@charts.network-usage%</option>
+ </optgroup>
</select>
<div>
<span @click="span = 'day'" :class="{ active: span == 'day' }">%i18n:@per-day%</span> | <span @click="span = 'hour'" :class="{ active: span == 'hour' }">%i18n:@per-hour%</span>
@@ -41,7 +46,10 @@ const colors = {
localPlus: 'rgb(52, 178, 118)',
remotePlus: 'rgb(158, 255, 209)',
localMinus: 'rgb(255, 97, 74)',
- remoteMinus: 'rgb(255, 149, 134)'
+ remoteMinus: 'rgb(255, 149, 134)',
+
+ incoming: 'rgb(52, 178, 118)',
+ outgoing: 'rgb(255, 97, 74)',
};
const rgba = (color: string): string => {
@@ -75,6 +83,9 @@ export default Vue.extend({
case 'drive-total': return this.driveTotalChart();
case 'drive-files': return this.driveFilesChart();
case 'drive-files-total': return this.driveFilesTotalChart();
+ case 'network-requests': return this.networkRequestsChart();
+ case 'network-time': return this.networkTimeChart();
+ case 'network-usage': return this.networkUsageChart();
}
},
@@ -544,7 +555,95 @@ export default Vue.extend({
}
}
}];
- }
+ },
+
+ networkRequestsChart(): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ requests: x.network.requests
+ }));
+
+ return [{
+ datasets: [{
+ label: 'Requests',
+ fill: true,
+ backgroundColor: rgba(colors.localPlus),
+ borderColor: colors.localPlus,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.requests }))
+ }]
+ }];
+ },
+
+ networkTimeChart(): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ time: x.network.requests != 0 ? (x.network.totalTime / x.network.requests) : 0,
+ }));
+
+ return [{
+ datasets: [{
+ label: 'Avg time (ms)',
+ fill: true,
+ backgroundColor: rgba(colors.localPlus),
+ borderColor: colors.localPlus,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.time }))
+ }]
+ }];
+ },
+
+ networkUsageChart(): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ incoming: x.network.incomingBytes,
+ outgoing: x.network.outgoingBytes
+ }));
+
+ return [{
+ datasets: [{
+ label: 'Incoming',
+ fill: true,
+ backgroundColor: rgba(colors.incoming),
+ borderColor: colors.incoming,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.incoming }))
+ }, {
+ label: 'Outgoing',
+ fill: true,
+ backgroundColor: rgba(colors.outgoing),
+ borderColor: colors.outgoing,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.outgoing }))
+ }]
+ }, {
+ scales: {
+ yAxes: [{
+ ticks: {
+ callback: value => {
+ return Vue.filter('bytes')(value, 1);
+ }
+ }
+ }]
+ },
+ tooltips: {
+ callbacks: {
+ label: (tooltipItem, data) => {
+ const label = data.datasets[tooltipItem.datasetIndex].label || '';
+ return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
+ }
+ }
+ }
+ }];
+ },
}
});
</script>
diff --git a/src/client/app/desktop/views/components/context-menu.vue b/src/client/app/desktop/views/components/context-menu.vue
index afb6838eb6..49aeac143f 100644
--- a/src/client/app/desktop/views/components/context-menu.vue
+++ b/src/client/app/desktop/views/components/context-menu.vue
@@ -64,7 +64,7 @@ export default Vue.extend({
});
this.$emit('closed');
- this.$destroy();
+ this.destroyDom();
}
}
});
diff --git a/src/client/app/desktop/views/components/dialog.vue b/src/client/app/desktop/views/components/dialog.vue
index aff21c1754..bbb1e0030c 100644
--- a/src/client/app/desktop/views/components/dialog.vue
+++ b/src/client/app/desktop/views/components/dialog.vue
@@ -78,7 +78,7 @@ export default Vue.extend({
scale: 0.8,
duration: 300,
easing: [ 0.5, -0.5, 1, 0.5 ],
- complete: () => this.$destroy()
+ complete: () => this.destroyDom()
});
},
onBgClick() {
diff --git a/src/client/app/desktop/views/components/friends-maker.vue b/src/client/app/desktop/views/components/friends-maker.vue
index 7dfd9e4359..4e8a212b00 100644
--- a/src/client/app/desktop/views/components/friends-maker.vue
+++ b/src/client/app/desktop/views/components/friends-maker.vue
@@ -14,7 +14,7 @@
<p class="empty" v-if="!fetching && users.length == 0">%i18n:@empty%</p>
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:@fetching%<mk-ellipsis/></p>
<a class="refresh" @click="refresh">%i18n:@refresh%</a>
- <button class="close" @click="$destroy()" title="%i18n:@close%">%fa:times%</button>
+ <button class="close" @click="destroyDom()" title="%i18n:@close%">%fa:times%</button>
</div>
</template>
diff --git a/src/client/app/desktop/views/components/media-image-dialog.vue b/src/client/app/desktop/views/components/media-image-dialog.vue
index 026522d907..89a340d3ae 100644
--- a/src/client/app/desktop/views/components/media-image-dialog.vue
+++ b/src/client/app/desktop/views/components/media-image-dialog.vue
@@ -26,7 +26,7 @@ export default Vue.extend({
opacity: 0,
duration: 100,
easing: 'linear',
- complete: () => this.$destroy()
+ complete: () => this.destroyDom()
});
}
}
diff --git a/src/client/app/desktop/views/components/media-image.vue b/src/client/app/desktop/views/components/media-image.vue
index 904dc7f832..3cff8cfc04 100644
--- a/src/client/app/desktop/views/components/media-image.vue
+++ b/src/client/app/desktop/views/components/media-image.vue
@@ -1,5 +1,5 @@
<template>
-<div class="ldwbgwstjsdgcjruamauqdrffetqudry" v-if="image.isSensitive && hide" @click="hide = false">
+<div class="ldwbgwstjsdgcjruamauqdrffetqudry" v-if="image.isSensitive && hide && !$store.state.device.alwaysShowNsfw" @click="hide = false">
<div>
<b>%fa:exclamation-triangle% %i18n:@sensitive%</b>
<span>%i18n:@click-to-show%</span>
diff --git a/src/client/app/desktop/views/components/media-video-dialog.vue b/src/client/app/desktop/views/components/media-video-dialog.vue
index 959cefa42c..03c93c8939 100644
--- a/src/client/app/desktop/views/components/media-video-dialog.vue
+++ b/src/client/app/desktop/views/components/media-video-dialog.vue
@@ -28,7 +28,7 @@ export default Vue.extend({
opacity: 0,
duration: 100,
easing: 'linear',
- complete: () => this.$destroy()
+ complete: () => this.destroyDom()
});
}
}
diff --git a/src/client/app/desktop/views/components/note-detail.vue b/src/client/app/desktop/views/components/note-detail.vue
index 1ba4a9a447..7307eeb7dc 100644
--- a/src/client/app/desktop/views/components/note-detail.vue
+++ b/src/client/app/desktop/views/components/note-detail.vue
@@ -37,20 +37,26 @@
</router-link>
</header>
<div class="body">
- <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>
- <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" :detail="true"/>
- <a class="location" v-if="p.geo" :href="`https://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">
- <mk-note-preview :note="p.renote"/>
+ <p v-if="p.cw != null" class="cw">
+ <span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
+ <mk-cw-button v-model="showContent"/>
+ </p>
+ <div class="content" v-show="p.cw == null || showContent">
+ <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>
+ <misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i"/>
+ </div>
+ <div class="files" v-if="p.files.length > 0">
+ <mk-media-list :media-list="p.files" :raw="true"/>
+ </div>
+ <mk-poll v-if="p.poll" :note="p"/>
+ <mk-url-preview v-for="url in urls" :url="url" :key="url" :detail="true"/>
+ <a class="location" v-if="p.geo" :href="`https://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">
+ <mk-note-preview :note="p.renote"/>
+ </div>
</div>
</div>
<footer>
@@ -86,6 +92,7 @@ import MkRenoteFormWindow from './renote-form-window.vue';
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
import XSub from './notes.note.sub.vue';
+import { sum } from '../../../../../prelude/array';
export default Vue.extend({
components: {
@@ -104,6 +111,7 @@ export default Vue.extend({
data() {
return {
+ showContent: false,
conversation: [],
conversationFetching: false,
replies: []
@@ -114,22 +122,24 @@ export default Vue.extend({
isRenote(): boolean {
return (this.note.renote &&
this.note.text == null &&
- this.note.mediaIds.length == 0 &&
+ this.note.fileIds.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)
- .map(key => this.p.reactionCounts[key])
- .reduce((a, b) => a + b)
+ ? sum(Object.values(this.p.reactionCounts))
: 0;
},
+
title(): string {
return new Date(this.p.createdAt).toLocaleString();
},
+
urls(): string[] {
if (this.p.text) {
const ast = parse(this.p.text);
@@ -184,22 +194,26 @@ export default Vue.extend({
this.conversation = conversation.reverse();
});
},
+
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,
@@ -327,37 +341,49 @@ root(isDark)
> .body
padding 8px 0
- > .text
+ > .cw
cursor default
display block
margin 0
padding 0
overflow-wrap break-word
- font-size 1.5em
color isDark ? #fff : #717171
- > .renote
- margin 8px 0
+ > .text
+ margin-right 8px
+
+ > .content
+ > .text
+ cursor default
+ display block
+ margin 0
+ padding 0
+ overflow-wrap break-word
+ font-size 1.5em
+ color isDark ? #fff : #717171
+
+ > .renote
+ margin 8px 0
- > .mk-note-preview
- padding 16px
- border dashed 1px #c0dac6
- border-radius 8px
+ > *
+ padding 16px
+ border dashed 1px #c0dac6
+ border-radius 8px
- > .location
- margin 4px 0
- font-size 12px
- color #ccc
+ > .location
+ margin 4px 0
+ font-size 12px
+ color #ccc
- > .map
- width 100%
- height 300px
+ > .map
+ width 100%
+ height 300px
- &:empty
- display none
+ &:empty
+ display none
- > .mk-url-preview
- margin-top 8px
+ > .mk-url-preview
+ margin-top 8px
> footer
font-size 1.2em
diff --git a/src/client/app/desktop/views/components/note-preview.vue b/src/client/app/desktop/views/components/note-preview.vue
index c723db98c0..6c84165356 100644
--- a/src/client/app/desktop/views/components/note-preview.vue
+++ b/src/client/app/desktop/views/components/note-preview.vue
@@ -1,10 +1,16 @@
<template>
-<div class="mk-note-preview" :title="title">
+<div class="qiziqtywpuaucsgarwajitwaakggnisj" :title="title">
<mk-avatar class="avatar" :user="note.user" v-if="!mini"/>
<div class="main">
<mk-note-header class="header" :note="note" :mini="true"/>
<div class="body">
- <mk-sub-note-content class="text" :note="note"/>
+ <p v-if="note.cw != null" class="cw">
+ <span class="text" v-if="note.cw != ''">{{ note.cw }}</span>
+ <mk-cw-button v-model="showContent"/>
+ </p>
+ <div class="content" v-show="note.cw == null || showContent">
+ <mk-sub-note-content class="text" :note="note"/>
+ </div>
</div>
</div>
</div>
@@ -25,6 +31,13 @@ export default Vue.extend({
default: false
}
},
+
+ data() {
+ return {
+ showContent: false
+ };
+ },
+
computed: {
title(): string {
return new Date(this.note.createdAt).toLocaleString();
@@ -52,16 +65,28 @@ root(isDark)
> .body
- > .text
+ > .cw
cursor default
+ display block
margin 0
padding 0
- color isDark ? #959ba7 : #717171
+ overflow-wrap break-word
+ color isDark ? #fff : #717171
+
+ > .text
+ margin-right 8px
+
+ > .content
+ > .text
+ cursor default
+ margin 0
+ padding 0
+ color isDark ? #959ba7 : #717171
-.mk-note-preview[data-darkmode]
+.qiziqtywpuaucsgarwajitwaakggnisj[data-darkmode]
root(true)
-.mk-note-preview:not([data-darkmode])
+.qiziqtywpuaucsgarwajitwaakggnisj:not([data-darkmode])
root(false)
</style>
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 fc851e83e9..8f01ddd43c 100644
--- a/src/client/app/desktop/views/components/notes.note.sub.vue
+++ b/src/client/app/desktop/views/components/notes.note.sub.vue
@@ -1,10 +1,16 @@
<template>
-<div class="sub" :title="title">
+<div class="tkfdzaxtkdeianobciwadajxzbddorql" :title="title">
<mk-avatar class="avatar" :user="note.user"/>
<div class="main">
<mk-note-header class="header" :note="note"/>
<div class="body">
- <mk-sub-note-content class="text" :note="note"/>
+ <p v-if="note.cw != null" class="cw">
+ <span class="text" v-if="note.cw != ''">{{ note.cw }}</span>
+ <mk-cw-button v-model="showContent"/>
+ </p>
+ <div class="content" v-show="note.cw == null || showContent">
+ <mk-sub-note-content class="text" :note="note"/>
+ </div>
</div>
</div>
</div>
@@ -14,7 +20,19 @@
import Vue from 'vue';
export default Vue.extend({
- props: ['note'],
+ props: {
+ note: {
+ type: Object,
+ required: true
+ }
+ },
+
+ data() {
+ return {
+ showContent: false
+ };
+ },
+
computed: {
title(): string {
return new Date(this.note.createdAt).toLocaleString();
@@ -48,20 +66,32 @@ root(isDark)
> .body
- > .text
+ > .cw
cursor default
+ display block
margin 0
padding 0
- color isDark ? #959ba7 : #717171
+ overflow-wrap break-word
+ color isDark ? #fff : #717171
+
+ > .text
+ margin-right 8px
+
+ > .content
+ > .text
+ cursor default
+ margin 0
+ padding 0
+ color isDark ? #959ba7 : #717171
- pre
- max-height 120px
- font-size 80%
+ pre
+ max-height 120px
+ font-size 80%
-.sub[data-darkmode]
+.tkfdzaxtkdeianobciwadajxzbddorql[data-darkmode]
root(true)
-.sub:not([data-darkmode])
+.tkfdzaxtkdeianobciwadajxzbddorql:not([data-darkmode])
root(false)
</style>
diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue
index 7592ae3905..46a866f9a7 100644
--- a/src/client/app/desktop/views/components/notes.note.vue
+++ b/src/client/app/desktop/views/components/notes.note.vue
@@ -18,7 +18,7 @@
<div class="body">
<p v-if="p.cw != null" class="cw">
<span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
- <span class="toggle" @click="showContent = !showContent">{{ showContent ? '%i18n:@hide%' : '%i18n:@see-more%' }}</span>
+ <mk-cw-button v-model="showContent"/>
</p>
<div class="content" v-show="p.cw == null || showContent">
<div class="text">
@@ -28,15 +28,13 @@
<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 class="files" v-if="p.files.length > 0">
+ <mk-media-list :media-list="p.files"/>
</div>
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
<a class="location" v-if="p.geo" :href="`https://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">
- <mk-note-preview :note="p.renote"/>
- </div>
+ <div class="renote" v-if="p.renote"><mk-note-preview :note="p.renote"/></div>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
</div>
</div>
@@ -78,6 +76,7 @@ import MkRenoteFormWindow from './renote-form-window.vue';
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
import XSub from './notes.note.sub.vue';
+import { sum } from '../../../../../prelude/array';
function focus(el, fn) {
const target = fn(el);
@@ -95,7 +94,12 @@ export default Vue.extend({
XSub
},
- props: ['note'],
+ props: {
+ note: {
+ type: Object,
+ required: true
+ }
+ },
data() {
return {
@@ -110,7 +114,7 @@ export default Vue.extend({
isRenote(): boolean {
return (this.note.renote &&
this.note.text == null &&
- this.note.mediaIds.length == 0 &&
+ this.note.fileIds.length == 0 &&
this.note.poll == null);
},
@@ -120,9 +124,7 @@ export default Vue.extend({
reactionsCount(): number {
return this.p.reactionCounts
- ? Object.keys(this.p.reactionCounts)
- .map(key => this.p.reactionCounts[key])
- .reduce((a, b) => a + b)
+ ? sum(Object.values(this.p.reactionCounts))
: 0;
},
@@ -399,19 +401,6 @@ root(isDark)
> .text
margin-right 8px
- > .toggle
- display inline-block
- padding 4px 8px
- font-size 0.7em
- color isDark ? #393f4f : #fff
- background isDark ? #687390 : #b1b9c1
- border-radius 2px
- cursor pointer
- user-select none
-
- &:hover
- background isDark ? #707b97 : #bbc4ce
-
> .content
> .text
@@ -470,7 +459,7 @@ root(isDark)
> .renote
margin 8px 0
- > .mk-note-preview
+ > *
padding 16px
border dashed 1px isDark ? #4e945e : #c0dac6
border-radius 8px
diff --git a/src/client/app/desktop/views/components/notes.vue b/src/client/app/desktop/views/components/notes.vue
index a1c1207a7b..26aa0c8dea 100644
--- a/src/client/app/desktop/views/components/notes.vue
+++ b/src/client/app/desktop/views/components/notes.vue
@@ -10,8 +10,7 @@
</div>
<!-- トランジションを有効にするとなぜかメモリリークする -->
- <!--<transition-group name="mk-notes" class="transition">-->
- <div class="notes">
+ <transition-group name="mk-notes" class="notes transition" tag="div">
<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">
@@ -19,8 +18,7 @@
<span>%fa:angle-down%{{ _notes[i + 1]._datetext }}</span>
</p>
</template>
- </div>
- <!--</transition-group>-->
+ </transition-group>
<footer v-if="more">
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
@@ -122,7 +120,7 @@ export default Vue.extend({
prepend(note, silent = false) {
//#region 弾く
const isMyNote = note.userId == this.$store.state.i.id;
- const isPureRenote = note.renoteId != null && note.text == null && note.mediaIds.length == 0 && note.poll == null;
+ const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null;
if (this.$store.state.settings.showMyRenotes === false) {
if (isMyNote && isPureRenote) {
diff --git a/src/client/app/desktop/views/components/notifications.vue b/src/client/app/desktop/views/components/notifications.vue
index bfe71903e4..9d0e73adef 100644
--- a/src/client/app/desktop/views/components/notifications.vue
+++ b/src/client/app/desktop/views/components/notifications.vue
@@ -2,8 +2,7 @@
<div class="mk-notifications">
<div class="notifications" v-if="notifications.length != 0">
<!-- トランジションを有効にするとなぜかメモリリークする -->
- <!-- <transition-group name="mk-notifications" class="transition"> -->
- <div>
+ <transition-group name="mk-notifications" class="transition" tag="div">
<template v-for="(notification, i) in _notifications">
<div class="notification" :class="notification.type" :key="notification.id">
<mk-time :time="notification.createdAt"/>
@@ -97,8 +96,7 @@
<span>%fa:angle-down%{{ _notifications[i + 1]._datetext }}</span>
</p>
</template>
- </div>
- <!-- </transition-group> -->
+ </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%' }}
diff --git a/src/client/app/desktop/views/components/post-form-window.vue b/src/client/app/desktop/views/components/post-form-window.vue
index 51a416e281..a88c96d1bf 100644
--- a/src/client/app/desktop/views/components/post-form-window.vue
+++ b/src/client/app/desktop/views/components/post-form-window.vue
@@ -4,7 +4,7 @@
<span class="icon" v-if="geo">%fa:map-marker-alt%</span>
<span v-if="!reply">%i18n:@note%</span>
<span v-if="reply">%i18n:@reply%</span>
- <span class="count" v-if="media.length != 0">{{ '%i18n:@attaches%'.replace('{}', media.length) }}</span>
+ <span class="count" v-if="files.length != 0">{{ '%i18n:@attaches%'.replace('{}', files.length) }}</span>
<span class="count" v-if="uploadings.length != 0">{{ '%i18n:@uploading-media%'.replace('{}', uploadings.length) }}<mk-ellipsis/></span>
</span>
@@ -14,7 +14,7 @@
:reply="reply"
@posted="onPosted"
@change-uploadings="onChangeUploadings"
- @change-attached-media="onChangeMedia"
+ @change-attached-files="onChangeFiles"
@geo-attached="onGeoAttached"
@geo-dettached="onGeoDettached"/>
</div>
@@ -29,7 +29,7 @@ export default Vue.extend({
data() {
return {
uploadings: [],
- media: [],
+ files: [],
geo: null
};
},
@@ -42,8 +42,8 @@ export default Vue.extend({
onChangeUploadings(files) {
this.uploadings = files;
},
- onChangeMedia(media) {
- this.media = media;
+ onChangeFiles(files) {
+ this.files = files;
},
onGeoAttached(geo) {
this.geo = geo;
diff --git a/src/client/app/desktop/views/components/post-form.vue b/src/client/app/desktop/views/components/post-form.vue
index 2ca5484610..c371940aa3 100644
--- a/src/client/app/desktop/views/components/post-form.vue
+++ b/src/client/app/desktop/views/components/post-form.vue
@@ -20,7 +20,7 @@
@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
v-autocomplete="'text'"
></textarea>
- <div class="medias" :class="{ with: poll }" v-show="files.length != 0">
+ <div class="files" :class="{ with: poll }" v-show="files.length != 0">
<x-draggable :list="files" :options="{ animation: 150 }">
<div v-for="file in files" :key="file.id">
<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
@@ -45,7 +45,7 @@
<span v-if="visibility === 'specified'">%fa:envelope%</span>
<span v-if="visibility === 'private'">%fa:lock%</span>
</button>
- <p class="text-count" :class="{ over: text.length > 1000 }">{{ 1000 - text.length }}</p>
+ <p class="text-count" :class="{ over: this.trimmedLength(text) > 1000 }">{{ 1000 - this.trimmedLength(text) }}</p>
<button :class="{ posting }" class="submit" :disabled="!canPost" @click="post">
{{ posting ? '%i18n:@posting%' : submitText }}<mk-ellipsis v-if="posting"/>
</button>
@@ -62,6 +62,9 @@ import getFace from '../../../common/scripts/get-face';
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
import parse from '../../../../../mfm/parse';
import { host } from '../../../config';
+import { erase, unique } from '../../../../../prelude/array';
+import { length } from 'stringz';
+import parseAcct from '../../../../../misc/acct/parse';
export default Vue.extend({
components: {
@@ -99,7 +102,7 @@ export default Vue.extend({
useCw: false,
cw: null,
geo: null,
- visibility: this.$store.state.device.visibility || 'public',
+ visibility: this.$store.state.settings.rememberNoteVisibility ? (this.$store.state.device.visibility || this.$store.state.settings.defaultNoteVisibility) : this.$store.state.settings.defaultNoteVisibility,
visibleUsers: [],
autocomplete: null,
draghover: false,
@@ -145,7 +148,7 @@ export default Vue.extend({
canPost(): boolean {
return !this.posting &&
(1 <= this.text.length || 1 <= this.files.length || this.poll || this.renote) &&
- (this.text.trim().length <= 1000);
+ (length(this.text.trim()) <= 1000);
}
},
@@ -188,7 +191,7 @@ export default Vue.extend({
(this.$refs.poll as any).set(draft.data.poll);
});
}
- this.$emit('change-attached-media', this.files);
+ this.$emit('change-attached-files', this.files);
}
}
@@ -197,6 +200,10 @@ export default Vue.extend({
},
methods: {
+ trimmedLength(text: string) {
+ return length(text.trim());
+ },
+
addTag(tag: string) {
insertTextAtCursor(this.$refs.text, ` #${tag} `);
},
@@ -225,12 +232,12 @@ export default Vue.extend({
attachMedia(driveFile) {
this.files.push(driveFile);
- this.$emit('change-attached-media', this.files);
+ this.$emit('change-attached-files', this.files);
},
detachMedia(id) {
this.files = this.files.filter(x => x.id != id);
- this.$emit('change-attached-media', this.files);
+ this.$emit('change-attached-files', this.files);
},
onChangeFile() {
@@ -249,7 +256,7 @@ export default Vue.extend({
this.text = '';
this.files = [];
this.poll = false;
- this.$emit('change-attached-media', this.files);
+ this.$emit('change-attached-files', this.files);
},
onKeydown(e) {
@@ -297,7 +304,7 @@ export default Vue.extend({
if (driveFile != null && driveFile != '') {
const file = JSON.parse(driveFile);
this.files.push(file);
- this.$emit('change-attached-media', this.files);
+ this.$emit('change-attached-files', this.files);
e.preventDefault();
}
//#endregion
@@ -336,17 +343,16 @@ export default Vue.extend({
addVisibleUser() {
(this as any).apis.input({
title: '%i18n:@enter-username%'
- }).then(username => {
- (this as any).api('users/show', {
- username
- }).then(user => {
+ }).then(acct => {
+ if (acct.startsWith('@')) acct = acct.substr(1);
+ (this as any).api('users/show', parseAcct(acct)).then(user => {
this.visibleUsers.push(user);
});
});
},
removeVisibleUser(user) {
- this.visibleUsers = this.visibleUsers.filter(u => u != user);
+ this.visibleUsers = erase(user, this.visibleUsers);
},
post() {
@@ -354,7 +360,7 @@ export default Vue.extend({
(this as any).api('notes/create', {
text: this.text == '' ? undefined : this.text,
- mediaIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
+ fileIds: 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,
@@ -391,7 +397,7 @@ export default Vue.extend({
if (this.text && this.text != '') {
const hashtags = parse(this.text).filter(x => x.type == 'hashtag').map(x => x.hashtag);
const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[];
- localStorage.setItem('hashtags', JSON.stringify(hashtags.concat(history).reduce((a, c) => a.includes(c) ? a : [...a, c], [])));
+ localStorage.setItem('hashtags', JSON.stringify(unique(hashtags.concat(history))));
}
},
@@ -514,7 +520,7 @@ root(isDark)
margin-right 8px
white-space nowrap
- > .medias
+ > .files
margin 0
padding 0
background isDark ? #181b23 : lighten($theme-color, 98%)
diff --git a/src/client/app/desktop/views/components/renote-form.vue b/src/client/app/desktop/views/components/renote-form.vue
index 38eab3362f..c5192ecaac 100644
--- a/src/client/app/desktop/views/components/renote-form.vue
+++ b/src/client/app/desktop/views/components/renote-form.vue
@@ -1,6 +1,6 @@
<template>
<div class="mk-renote-form">
- <mk-note-preview :note="note"/>
+ <mk-note-preview class="preview" :note="note"/>
<template v-if="!quote">
<footer>
<a class="quote" v-if="!quote" @click="onQuote">%i18n:@quote%</a>
@@ -61,7 +61,7 @@ export default Vue.extend({
root(isDark)
- > .mk-note-preview
+ > .preview
margin 16px 22px
> footer
diff --git a/src/client/app/desktop/views/components/settings.drive.vue b/src/client/app/desktop/views/components/settings.drive.vue
index e8a3cc9685..d254b27110 100644
--- a/src/client/app/desktop/views/components/settings.drive.vue
+++ b/src/client/app/desktop/views/components/settings.drive.vue
@@ -1,7 +1,6 @@
<template>
<div class="root">
<template v-if="!fetching">
- <el-progress :text-inside="true" :stroke-width="18" :percentage="Math.floor((usage / capacity) * 100)"/>
<p><b>{{ capacity | bytes }}</b>%i18n:max%<b>{{ usage | bytes }}</b>%i18n:in-use%</p>
</template>
</div>
diff --git a/src/client/app/desktop/views/components/settings.profile.vue b/src/client/app/desktop/views/components/settings.profile.vue
index 262583b640..d47b5b224b 100644
--- a/src/client/app/desktop/views/components/settings.profile.vue
+++ b/src/client/app/desktop/views/components/settings.profile.vue
@@ -19,7 +19,7 @@
</label>
<label class="ui from group">
<p>%i18n:@birthday%</p>
- <el-date-picker v-model="birthday" type="date" value-format="yyyy-MM-dd"/>
+ <input type="date" v-model="birthday"/>
</label>
<button class="ui primary" @click="save">%i18n:@save%</button>
<section>
@@ -30,6 +30,7 @@
<h2>%i18n:@other%</h2>
<mk-switch v-model="$store.state.i.isBot" @change="onChangeIsBot" text="%i18n:@is-bot%"/>
<mk-switch v-model="$store.state.i.isCat" @change="onChangeIsCat" text="%i18n:@is-cat%"/>
+ <mk-switch v-model="alwaysMarkNsfw" text="%i18n:common.always-mark-nsfw%"/>
</section>
</div>
</template>
@@ -46,6 +47,12 @@ export default Vue.extend({
birthday: null,
};
},
+ computed: {
+ alwaysMarkNsfw: {
+ get() { return this.$store.state.i.settings.alwaysMarkNsfw; },
+ set(value) { (this as any).api('i/update', { alwaysMarkNsfw: value }); }
+ },
+ },
created() {
this.name = this.$store.state.i.name || '';
this.location = this.$store.state.i.profile.location;
diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue
index 7d6f1d55fb..709715e598 100644
--- a/src/client/app/desktop/views/components/settings.vue
+++ b/src/client/app/desktop/views/components/settings.vue
@@ -20,12 +20,28 @@
<section class="web" v-show="page == 'web'">
<h1>%i18n:@behaviour%</h1>
- <mk-switch v-model="$store.state.settings.fetchOnScroll" @change="onChangeFetchOnScroll" text="%i18n:@fetch-on-scroll%">
+ <mk-switch v-model="fetchOnScroll" text="%i18n:@fetch-on-scroll%">
<span>%i18n:@fetch-on-scroll-desc%</span>
</mk-switch>
<mk-switch v-model="autoPopout" text="%i18n:@auto-popout%">
<span>%i18n:@auto-popout-desc%</span>
</mk-switch>
+
+ <section>
+ <header>%i18n:@note-visibility%</header>
+ <mk-switch v-model="rememberNoteVisibility" text="%i18n:@remember-note-visibility%"/>
+ <section>
+ <header>%i18n:@default-note-visibility%</header>
+ <ui-select v-model="defaultNoteVisibility">
+ <option value="public">%i18n:common.note-visibility.public%</option>
+ <option value="home">%i18n:common.note-visibility.home%</option>
+ <option value="followers">%i18n:common.note-visibility.followers%</option>
+ <option value="specified">%i18n:common.note-visibility.specified%</option>
+ <option value="private">%i18n:common.note-visibility.private%</option>
+ </ui-select>
+ </section>
+ </section>
+
<details>
<summary>%i18n:@advanced%</summary>
<mk-switch v-model="apiViaStream" text="%i18n:@api-via-stream%">
@@ -43,23 +59,26 @@
<button class="ui" @click="updateWallpaper">%i18n:@choose-wallpaper%</button>
<button class="ui" @click="deleteWallpaper">%i18n:@delete-wallpaper%</button>
<mk-switch v-model="darkmode" text="%i18n:@dark-mode%"/>
- <mk-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons" text="%i18n:@circle-icons%"/>
- <mk-switch v-model="$store.state.settings.gradientWindowHeader" @change="onChangeGradientWindowHeader" text="%i18n:@gradient-window-header%"/>
- <mk-switch v-model="$store.state.settings.iLikeSushi" @change="onChangeILikeSushi" text="%i18n:common.i-like-sushi%"/>
+ <mk-switch v-model="circleIcons" text="%i18n:@circle-icons%"/>
+ <mk-switch v-model="contrastedAcct" text="%i18n:@contrasted-acct%"/>
+ <mk-switch v-model="showFullAcct" text="%i18n:common.show-full-acct%"/>
+ <mk-switch v-model="gradientWindowHeader" text="%i18n:@gradient-window-header%"/>
+ <mk-switch v-model="iLikeSushi" text="%i18n:common.i-like-sushi%"/>
</div>
- <mk-switch v-model="$store.state.settings.showPostFormOnTopOfTl" @change="onChangeShowPostFormOnTopOfTl" text="%i18n:@post-form-on-timeline%"/>
- <mk-switch v-model="$store.state.settings.suggestRecentHashtags" @change="onChangeSuggestRecentHashtags" text="%i18n:@suggest-recent-hashtags%"/>
- <mk-switch v-model="$store.state.settings.showClockOnHeader" @change="onChangeShowClockOnHeader" text="%i18n:@show-clock-on-header%"/>
- <mk-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget" text="%i18n:@show-reply-target%"/>
- <mk-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes" text="%i18n:@show-my-renotes%"/>
- <mk-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes" text="%i18n:@show-renoted-my-notes%"/>
- <mk-switch v-model="$store.state.settings.showLocalRenotes" @change="onChangeShowLocalRenotes" text="%i18n:@show-local-renotes%"/>
- <mk-switch v-model="$store.state.settings.showMaps" @change="onChangeShowMaps" text="%i18n:@show-maps%">
+ <mk-switch v-model="showPostFormOnTopOfTl" text="%i18n:@post-form-on-timeline%"/>
+ <mk-switch v-model="suggestRecentHashtags" text="%i18n:@suggest-recent-hashtags%"/>
+ <mk-switch v-model="showClockOnHeader" text="%i18n:@show-clock-on-header%"/>
+ <mk-switch v-model="alwaysShowNsfw" text="%i18n:common.always-show-nsfw%"/>
+ <mk-switch v-model="showReplyTarget" text="%i18n:@show-reply-target%"/>
+ <mk-switch v-model="showMyRenotes" text="%i18n:@show-my-renotes%"/>
+ <mk-switch v-model="showRenotedMyNotes" text="%i18n:@show-renoted-my-notes%"/>
+ <mk-switch v-model="showLocalRenotes" text="%i18n:@show-local-renotes%"/>
+ <mk-switch v-model="showMaps" text="%i18n:@show-maps%">
<span>%i18n:@show-maps-desc%</span>
</mk-switch>
- <mk-switch v-model="$store.state.settings.disableAnimatedMfm" @change="onChangeDisableAnimatedMfm" text="%i18n:common.disable-animated-mfm%"/>
- <mk-switch v-model="$store.state.settings.games.reversi.showBoardLabels" @change="onChangeReversiBoardLabels" text="%i18n:common.show-reversi-board-labels%"/>
- <mk-switch v-model="$store.state.settings.games.reversi.useContrastStones" @change="onChangeUseContrastReversiStones" text="%i18n:common.use-contrast-reversi-stones%"/>
+ <mk-switch v-model="disableAnimatedMfm" text="%i18n:common.disable-animated-mfm%"/>
+ <mk-switch v-model="games_reversi_showBoardLabels" text="%i18n:common.show-reversi-board-labels%"/>
+ <mk-switch v-model="games_reversi_useContrastStones" text="%i18n:common.use-contrast-reversi-stones%"/>
</section>
<section class="web" v-show="page == 'web'">
@@ -68,32 +87,31 @@
<span>%i18n:@enable-sounds-desc%</span>
</mk-switch>
<label>%i18n:@volume%</label>
- <el-slider
+ <input type="range"
v-model="soundVolume"
- :show-input="true"
- :format-tooltip="v => `${v * 100}%`"
:disabled="!enableSounds"
- :max="1"
- :step="0.1"
+ max="1"
+ step="0.1"
/>
<button class="ui button" @click="soundTest">%fa:volume-up% %i18n:@test%</button>
</section>
<section class="web" v-show="page == 'web'">
<h1>%i18n:@mobile%</h1>
- <mk-switch v-model="$store.state.settings.disableViaMobile" @change="onChangeDisableViaMobile" text="%i18n:@disable-via-mobile%"/>
+ <mk-switch v-model="disableViaMobile" text="%i18n:@disable-via-mobile%"/>
</section>
<section class="web" v-show="page == 'web'">
<h1>%i18n:@language%</h1>
- <el-select v-model="lang" placeholder="%i18n:@pick-language%">
- <el-option-group label="%i18n:@recommended%">
- <el-option label="%i18n:@auto%" :value="null"/>
- </el-option-group>
- <el-option-group label="%i18n:@specify-language%">
- <el-option v-for="x in langs" :label="x[1]" :value="x[0]" :key="x[0]"/>
- </el-option-group>
- </el-select>
+ <select v-model="lang" placeholder="%i18n:@pick-language%">
+ <optgroup label="%i18n:@recommended%">
+ <option value="">%i18n:@auto%</option>
+ </optgroup>
+
+ <optgroup label="%i18n:@specify-language%">
+ <option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option>
+ </optgroup>
+ </select>
<div class="none ui info">
<p>%fa:info-circle%%i18n:@language-desc%</p>
</div>
@@ -188,10 +206,6 @@
<mk-switch v-model="enableExperimentalFeatures" text="%i18n:@experimental%">
<span>%i18n:@experimental-desc%</span>
</mk-switch>
- <details v-if="debug">
- <summary>%i18n:@tools%</summary>
- <button class="ui button block" @click="taskmngr">%i18n:@task-manager%</button>
- </details>
</section>
</div>
</div>
@@ -209,7 +223,6 @@ import XSignins from './settings.signins.vue';
import XDrive from './settings.drive.vue';
import { url, langs, version } from '../../../config';
import checkForUpdate from '../../../common/scripts/check-for-update';
-import MkTaskManager from './taskmanager.vue';
export default Vue.extend({
components: {
@@ -276,7 +289,112 @@ export default Vue.extend({
enableExperimentalFeatures: {
get() { return this.$store.state.device.enableExperimentalFeatures; },
set(value) { this.$store.commit('device/set', { key: 'enableExperimentalFeatures', value }); }
- }
+ },
+
+ alwaysShowNsfw: {
+ get() { return this.$store.state.device.alwaysShowNsfw; },
+ set(value) { this.$store.commit('device/set', { key: 'alwaysShowNsfw', value }); }
+ },
+
+ fetchOnScroll: {
+ get() { return this.$store.state.settings.fetchOnScroll; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'fetchOnScroll', value }); }
+ },
+
+ rememberNoteVisibility: {
+ get() { return this.$store.state.settings.rememberNoteVisibility; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'rememberNoteVisibility', value }); }
+ },
+
+ defaultNoteVisibility: {
+ get() { return this.$store.state.settings.defaultNoteVisibility; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'defaultNoteVisibility', value }); }
+ },
+
+ showReplyTarget: {
+ get() { return this.$store.state.settings.showReplyTarget; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'showReplyTarget', value }); }
+ },
+
+ showMyRenotes: {
+ get() { return this.$store.state.settings.showMyRenotes; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'showMyRenotes', value }); }
+ },
+
+ showRenotedMyNotes: {
+ get() { return this.$store.state.settings.showRenotedMyNotes; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'showRenotedMyNotes', value }); }
+ },
+
+ showLocalRenotes: {
+ get() { return this.$store.state.settings.showLocalRenotes; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'showLocalRenotes', value }); }
+ },
+
+ showPostFormOnTopOfTl: {
+ get() { return this.$store.state.settings.showPostFormOnTopOfTl; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'showPostFormOnTopOfTl', value }); }
+ },
+
+ suggestRecentHashtags: {
+ get() { return this.$store.state.settings.suggestRecentHashtags; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'suggestRecentHashtags', value }); }
+ },
+
+ showClockOnHeader: {
+ get() { return this.$store.state.settings.showClockOnHeader; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'showClockOnHeader', value }); }
+ },
+
+ showMaps: {
+ get() { return this.$store.state.settings.showMaps; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'showMaps', value }); }
+ },
+
+ circleIcons: {
+ get() { return this.$store.state.settings.circleIcons; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'circleIcons', value }); }
+ },
+
+ contrastedAcct: {
+ get() { return this.$store.state.settings.contrastedAcct; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'contrastedAcct', value }); }
+ },
+
+ showFullAcct: {
+ get() { return this.$store.state.settings.showFullAcct; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'showFullAcct', value }); }
+ },
+
+ iLikeSushi: {
+ get() { return this.$store.state.settings.iLikeSushi; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'iLikeSushi', value }); }
+ },
+
+ games_reversi_showBoardLabels: {
+ get() { return this.$store.state.settings.games.reversi.showBoardLabels; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'games.reversi.showBoardLabels', value }); }
+ },
+
+ games_reversi_useContrastStones: {
+ get() { return this.$store.state.settings.games.reversi.useContrastStones; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'games.reversi.useContrastStones', value }); }
+ },
+
+ disableAnimatedMfm: {
+ get() { return this.$store.state.settings.disableAnimatedMfm; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'disableAnimatedMfm', value }); }
+ },
+
+ disableViaMobile: {
+ get() { return this.$store.state.settings.disableViaMobile; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'disableViaMobile', value }); }
+ },
+
+ gradientWindowHeader: {
+ get() { return this.$store.state.settings.gradientWindowHeader; },
+ set(value) { this.$store.dispatch('settings/set', { key: 'gradientWindowHeader', value }); }
+ },
},
created() {
(this as any).os.getMeta().then(meta => {
@@ -284,9 +402,6 @@ export default Vue.extend({
});
},
methods: {
- taskmngr() {
- (this as any).os.new(MkTaskManager);
- },
customizeHome() {
this.$router.push('/i/customize-home');
this.$emit('done');
@@ -305,113 +420,11 @@ export default Vue.extend({
wallpaperId: null
});
},
- onChangeFetchOnScroll(v) {
- this.$store.dispatch('settings/set', {
- key: 'fetchOnScroll',
- value: v
- });
- },
onChangeAutoWatch(v) {
(this as any).api('i/update', {
autoWatch: v
});
},
- onChangeDark(v) {
- this.$store.dispatch('settings/set', {
- key: 'dark',
- value: v
- });
- },
- onChangeShowPostFormOnTopOfTl(v) {
- this.$store.dispatch('settings/set', {
- key: 'showPostFormOnTopOfTl',
- value: v
- });
- },
- onChangeSuggestRecentHashtags(v) {
- this.$store.dispatch('settings/set', {
- key: 'suggestRecentHashtags',
- value: v
- });
- },
- onChangeShowClockOnHeader(v) {
- this.$store.dispatch('settings/set', {
- key: 'showClockOnHeader',
- value: v
- });
- },
- onChangeShowReplyTarget(v) {
- this.$store.dispatch('settings/set', {
- key: 'showReplyTarget',
- value: v
- });
- },
- onChangeShowMyRenotes(v) {
- this.$store.dispatch('settings/set', {
- key: 'showMyRenotes',
- value: v
- });
- },
- onChangeShowRenotedMyNotes(v) {
- this.$store.dispatch('settings/set', {
- key: 'showRenotedMyNotes',
- value: v
- });
- },
- onChangeShowLocalRenotes(v) {
- this.$store.dispatch('settings/set', {
- key: 'showLocalRenotes',
- value: v
- });
- },
- onChangeShowMaps(v) {
- this.$store.dispatch('settings/set', {
- key: 'showMaps',
- value: v
- });
- },
- onChangeCircleIcons(v) {
- this.$store.dispatch('settings/set', {
- key: 'circleIcons',
- value: v
- });
- },
- onChangeILikeSushi(v) {
- this.$store.dispatch('settings/set', {
- key: 'iLikeSushi',
- value: v
- });
- },
- onChangeReversiBoardLabels(v) {
- this.$store.dispatch('settings/set', {
- key: 'games.reversi.showBoardLabels',
- value: v
- });
- },
- onChangeUseContrastReversiStones(v) {
- this.$store.dispatch('settings/set', {
- key: 'games.reversi.useContrastStones',
- value: v
- });
- },
- onChangeDisableAnimatedMfm(v) {
- this.$store.dispatch('settings/set', {
- key: 'disableAnimatedMfm',
- value: v
- });
- },
- onChangeGradientWindowHeader(v) {
- this.$store.dispatch('settings/set', {
- key: 'gradientWindowHeader',
- value: v
- });
- },
- onChangeDisableViaMobile(v) {
- this.$store.dispatch('settings/set', {
- key: 'disableViaMobile',
- value: v
- });
- },
checkForUpdate() {
this.checkingForUpdate = true;
checkForUpdate((this as any).os, true, true).then(newer => {
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 cb0374b910..6889dc231e 100644
--- a/src/client/app/desktop/views/components/sub-note-content.vue
+++ b/src/client/app/desktop/views/components/sub-note-content.vue
@@ -7,9 +7,9 @@
<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">
- <summary>({{ '%i18n:@media-count%'.replace('{}', note.media.length) }})</summary>
- <mk-media-list :media-list="note.media"/>
+ <details v-if="note.files.length > 0">
+ <summary>({{ '%i18n:@media-count%'.replace('{}', note.files.length) }})</summary>
+ <mk-media-list :media-list="note.files"/>
</details>
<details v-if="note.poll">
<summary>%i18n:@poll%</summary>
diff --git a/src/client/app/desktop/views/components/taskmanager.vue b/src/client/app/desktop/views/components/taskmanager.vue
deleted file mode 100644
index 1f1385add8..0000000000
--- a/src/client/app/desktop/views/components/taskmanager.vue
+++ /dev/null
@@ -1,219 +0,0 @@
-<template>
-<mk-window ref="window" width="750px" height="500px" @closed="$destroy" name="TaskManager">
- <span slot="header" :class="$style.header">%fa:stethoscope%%i18n:@title%</span>
- <el-tabs :class="$style.content">
- <el-tab-pane label="Requests">
- <el-table
- :data="os.requests"
- style="width: 100%"
- :default-sort="{prop: 'date', order: 'descending'}"
- >
- <el-table-column type="expand">
- <template slot-scope="props">
- <pre>{{ props.row.data }}</pre>
- <pre>{{ props.row.res }}</pre>
- </template>
- </el-table-column>
-
- <el-table-column
- label="Requested at"
- prop="date"
- sortable
- >
- <template slot-scope="scope">
- <b style="margin-right: 8px">{{ scope.row.date.getTime() }}</b>
- <span>(<mk-time :time="scope.row.date"/>)</span>
- </template>
- </el-table-column>
-
- <el-table-column
- label="Name"
- >
- <template slot-scope="scope">
- <b>{{ scope.row.name }}</b>
- </template>
- </el-table-column>
-
- <el-table-column
- label="Status"
- >
- <template slot-scope="scope">
- <span>{{ scope.row.status || '(pending)' }}</span>
- </template>
- </el-table-column>
- </el-table>
- </el-tab-pane>
-
- <el-tab-pane label="Streams">
- <el-table
- :data="os.connections"
- style="width: 100%"
- >
- <el-table-column
- label="Uptime"
- >
- <template slot-scope="scope">
- <mk-timer v-if="scope.row.connectedAt" :time="scope.row.connectedAt"/>
- <span v-else>-</span>
- </template>
- </el-table-column>
-
- <el-table-column
- label="Name"
- >
- <template slot-scope="scope">
- <b>{{ scope.row.name == '' ? '[Home]' : scope.row.name }}</b>
- </template>
- </el-table-column>
-
- <el-table-column
- label="User"
- >
- <template slot-scope="scope">
- <span>{{ scope.row.user || '(anonymous)' }}</span>
- </template>
- </el-table-column>
-
- <el-table-column
- prop="state"
- label="State"
- />
-
- <el-table-column
- prop="in"
- label="In"
- />
-
- <el-table-column
- prop="out"
- label="Out"
- />
- </el-table>
- </el-tab-pane>
-
- <el-tab-pane label="Streams (Inspect)">
- <el-tabs type="card" style="height:50%">
- <el-tab-pane v-for="c in os.connections" :label="c.name == '' ? '[Home]' : c.name" :key="c.id" :name="c.id" ref="connectionsTab">
- <div style="padding: 12px 0 0 12px">
- <el-button size="mini" @click="send(c)">Send</el-button>
- <el-button size="mini" type="warning" @click="c.isSuspended = true" v-if="!c.isSuspended">Suspend</el-button>
- <el-button size="mini" type="success" @click="c.isSuspended = false" v-else>Resume</el-button>
- <el-button size="mini" type="danger" @click="c.close">Disconnect</el-button>
- </div>
-
- <el-table
- :data="c.inout"
- style="width: 100%"
- :default-sort="{prop: 'at', order: 'descending'}"
- >
- <el-table-column type="expand">
- <template slot-scope="props">
- <pre>{{ props.row.data }}</pre>
- </template>
- </el-table-column>
-
- <el-table-column
- label="Date"
- prop="at"
- sortable
- >
- <template slot-scope="scope">
- <b style="margin-right: 8px">{{ scope.row.at.getTime() }}</b>
- <span>(<mk-time :time="scope.row.at"/>)</span>
- </template>
- </el-table-column>
-
- <el-table-column
- label="Type"
- >
- <template slot-scope="scope">
- <span>{{ getMessageType(scope.row.data) }}</span>
- </template>
- </el-table-column>
-
- <el-table-column
- label="Incoming / Outgoing"
- prop="type"
- />
- </el-table>
- </el-tab-pane>
- </el-tabs>
- </el-tab-pane>
-
- <el-tab-pane label="Windows">
- <el-table
- :data="Array.from(os.windows.windows)"
- style="width: 100%"
- >
- <el-table-column
- label="Name"
- >
- <template slot-scope="scope">
- <b>{{ scope.row.name || '(unknown)' }}</b>
- </template>
- </el-table-column>
-
- <el-table-column
- label="Operations"
- >
- <template slot-scope="scope">
- <el-button size="mini" type="danger" @click="scope.row.close">Close</el-button>
- </template>
- </el-table-column>
- </el-table>
- </el-tab-pane>
- </el-tabs>
-</mk-window>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- mounted() {
- (this as any).os.windows.on('added', this.onWindowsChanged);
- (this as any).os.windows.on('removed', this.onWindowsChanged);
- },
- beforeDestroy() {
- (this as any).os.windows.off('added', this.onWindowsChanged);
- (this as any).os.windows.off('removed', this.onWindowsChanged);
- },
- methods: {
- getMessageType(data): string {
- return data.type ? data.type : '-';
- },
- onWindowsChanged() {
- this.$forceUpdate();
- },
- send(c) {
- (this as any).apis.input({
- title: 'Send a JSON message',
- allowEmpty: false
- }).then(json => {
- c.send(JSON.parse(json));
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" module>
-.header
- > [data-fa]
- margin-right 4px
-
-.content
- height 100%
- overflow auto
-
-</style>
-
-<style>
-.el-tabs__header {
- margin-bottom: 0 !important;
-}
-
-.el-tabs__item {
- padding: 0 20px !important;
-}
-</style>
diff --git a/src/client/app/desktop/views/components/timeline.vue b/src/client/app/desktop/views/components/timeline.vue
index 52a7753438..8d72016f22 100644
--- a/src/client/app/desktop/views/components/timeline.vue
+++ b/src/client/app/desktop/views/components/timeline.vue
@@ -2,8 +2,8 @@
<div class="mk-timeline">
<header>
<span :data-active="src == 'home'" @click="src = 'home'">%fa:home% %i18n:@home%</span>
- <span :data-active="src == 'local'" @click="src = 'local'">%fa:R comments% %i18n:@local%</span>
- <span :data-active="src == 'hybrid'" @click="src = 'hybrid'">%fa:share-alt% %i18n:@hybrid%</span>
+ <span :data-active="src == 'local'" @click="src = 'local'" v-if="enableLocalTimeline">%fa:R comments% %i18n:@local%</span>
+ <span :data-active="src == 'hybrid'" @click="src = 'hybrid'" v-if="enableLocalTimeline">%fa:share-alt% %i18n:@hybrid%</span>
<span :data-active="src == 'global'" @click="src = 'global'">%fa:globe% %i18n:@global%</span>
<span :data-active="src == 'list'" @click="src = 'list'" v-if="list">%fa:list% {{ list.title }}</span>
<button @click="chooseList" title="%i18n:@list%">%fa:list%</button>
@@ -29,7 +29,8 @@ export default Vue.extend({
data() {
return {
src: 'home',
- list: null
+ list: null,
+ enableLocalTimeline: false
};
},
@@ -44,6 +45,10 @@ export default Vue.extend({
},
created() {
+ (this as any).os.getMeta().then(meta => {
+ this.enableLocalTimeline = !meta.disableLocalTimeline;
+ });
+
if (this.$store.state.device.tl) {
this.src = this.$store.state.device.tl.src;
if (this.src == 'list') {
diff --git a/src/client/app/desktop/views/components/ui-notification.vue b/src/client/app/desktop/views/components/ui-notification.vue
index 68413914c0..7519124870 100644
--- a/src/client/app/desktop/views/components/ui-notification.vue
+++ b/src/client/app/desktop/views/components/ui-notification.vue
@@ -27,7 +27,7 @@ export default Vue.extend({
translateY: -64,
duration: 500,
easing: 'easeInElastic',
- complete: () => this.$destroy()
+ complete: () => this.destroyDom()
});
}, 6000);
});
diff --git a/src/client/app/desktop/views/components/user-preview.vue b/src/client/app/desktop/views/components/user-preview.vue
index 1e1755ec3c..f6d6d68a7f 100644
--- a/src/client/app/desktop/views/components/user-preview.vue
+++ b/src/client/app/desktop/views/components/user-preview.vue
@@ -75,7 +75,7 @@ export default Vue.extend({
'margin-top': '-8px',
duration: 200,
easing: 'easeOutQuad',
- complete: () => this.$destroy()
+ complete: () => this.destroyDom()
});
}
}
diff --git a/src/client/app/desktop/views/components/users-list.item.vue b/src/client/app/desktop/views/components/users-list.item.vue
index 262fd38cd1..f42d577fce 100644
--- a/src/client/app/desktop/views/components/users-list.item.vue
+++ b/src/client/app/desktop/views/components/users-list.item.vue
@@ -1,17 +1,16 @@
<template>
-<div class="root item">
- <mk-avatar class="avatar" :user="user"/>
- <div class="main">
- <header>
- <router-link class="name" :to="user | userPage" v-user-preview="user.id">{{ user | userName }}</router-link>
- <span class="username">@{{ user | acct }}</span>
- </header>
- <div class="body">
- <p class="followed" v-if="user.isFollowed">%i18n:@followed%</p>
- <div class="description">{{ user.description }}</div>
+<div class="zvdbznxvfixtmujpsigoccczftvpiwqh">
+ <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>
+ <p class="followed" v-if="user.isFollowed">%i18n:@followed%</p>
+ <mk-follow-button :user="user" :size="'big'"/>
</div>
- <mk-follow-button :user="user"/>
</div>
</template>
@@ -19,76 +18,69 @@
import Vue from 'vue';
export default Vue.extend({
- props: ['user']
+ props: ['user'],
+
+ computed: {
+ 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 })`
+ };
+ }
+ },
});
</script>
<style lang="stylus" scoped>
-.root.item
- padding 16px
+.zvdbznxvfixtmujpsigoccczftvpiwqh
+ $bg = #fff
+
+ margin 16px auto
+ max-width calc(100% - 32px)
font-size 16px
+ text-align center
+ background $bg
+ box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
- &:after
- content ""
- display block
- clear both
+ > .banner
+ height 100px
+ background-color #f9f4f4
+ background-position center
+ background-size cover
> .avatar
display block
- float left
- margin 0 16px 0 0
- width 58px
- height 58px
- border-radius 8px
-
- > .main
- float left
- width calc(100% - 74px)
-
- > header
- margin-bottom 2px
+ margin -40px auto 0 auto
+ width 80px
+ height 80px
+ border-radius 100%
+ border solid 4px $bg
- > .name
- display inline
- margin 0
- padding 0
- color #777
- font-size 1em
- font-weight 700
- text-align left
- text-decoration none
+ > .body
+ padding 4px 32px 32px 32px
- &:hover
- text-decoration underline
+ @media (max-width 400px)
+ padding 4px 16px 16px 16px
- > .username
- text-align left
- margin 0 0 0 8px
- color #ccc
+ > .name
+ font-size 20px
+ font-weight bold
- > .body
- > .followed
- display inline-block
- margin 0 0 4px 0
- padding 2px 8px
- vertical-align top
- font-size 10px
- color #71afc7
- background #eefaff
- border-radius 4px
+ > .username
+ display block
+ opacity 0.7
- > .description
- cursor default
- display block
- margin 0
- padding 0
- overflow-wrap break-word
- font-size 1.1em
- color #717171
+ > .description
+ margin 16px 0
- > .mk-follow-button
- position absolute
- top 16px
- right 16px
+ > .followed
+ margin 0 0 16px 0
+ padding 0
+ line-height 24px
+ font-size 0.8em
+ color #71afc7
+ background #eefaff
+ border-radius 4px
</style>
diff --git a/src/client/app/desktop/views/components/users-list.vue b/src/client/app/desktop/views/components/users-list.vue
index 0423db8ed7..05e2f4e5b3 100644
--- a/src/client/app/desktop/views/components/users-list.vue
+++ b/src/client/app/desktop/views/components/users-list.vue
@@ -33,7 +33,7 @@ export default Vue.extend({
props: ['fetch', 'count', 'youKnowCount'],
data() {
return {
- limit: 30,
+ limit: 20,
mode: 'all',
fetching: true,
moreFetching: false,
@@ -73,10 +73,14 @@ export default Vue.extend({
.mk-users-list
height 100%
- background #fff
+ overflow auto
+ background #eee
> nav
- z-index 1
+ z-index 10
+ position sticky
+ top 0
+ background #fff
box-shadow 0 1px 0 rgba(#000, 0.1)
> div
@@ -114,16 +118,14 @@ export default Vue.extend({
background #eee
border-radius 20px
- > .users
- height calc(100% - 54px)
- overflow auto
-
- > *
- border-bottom solid 1px rgba(#000, 0.05)
+ > button
+ display block
+ width calc(100% - 32px)
+ margin 16px
+ padding 16px
- > *
- max-width 600px
- margin 0 auto
+ &:hover
+ background rgba(#000, 0.1)
> .no
margin 0
diff --git a/src/client/app/desktop/views/components/window.vue b/src/client/app/desktop/views/components/window.vue
index ec044ad27e..30f0ec558f 100644
--- a/src/client/app/desktop/views/components/window.vue
+++ b/src/client/app/desktop/views/components/window.vue
@@ -106,7 +106,7 @@ export default Vue.extend({
mounted() {
if (this.preventMount) {
- this.$destroy();
+ this.destroyDom();
return;
}
@@ -190,7 +190,7 @@ export default Vue.extend({
});
setTimeout(() => {
- this.$destroy();
+ this.destroyDom();
this.$emit('closed');
}, 300);
},
diff --git a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
index ebb54d782e..c86c30db17 100644
--- a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
@@ -1,22 +1,34 @@
<template>
<div class="obdskegsannmntldydackcpzezagxqfy mk-admin-card">
<header>%i18n:@dashboard%</header>
+
<div v-if="stats" class="stats">
<div><b>%fa:user% {{ stats.originalUsersCount | number }}</b><span>%i18n:@original-users%</span></div>
<div><span>%fa:user% {{ stats.usersCount | number }}</span><span>%i18n:@all-users%</span></div>
<div><b>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</b><span>%i18n:@original-notes%</span></div>
<div><span>%fa:pencil-alt% {{ stats.notesCount | number }}</span><span>%i18n:@all-notes%</span></div>
</div>
+
<div class="cpu-memory">
<x-cpu-memory :connection="connection"/>
</div>
- <div>
- <label>
- <input type="checkbox" v-model="disableRegistration" @change="updateMeta">
- <span>disableRegistration</span>
- </label>
- <button class="ui" @click="invite">%i18n:@invite%</button>
- <p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p>
+
+ <div class="form">
+ <div>
+ <label>
+ <input type="checkbox" v-model="disableRegistration" @change="updateMeta">
+ <span>%i18n:@disableRegistration%</span>
+ </label>
+ <button class="ui" @click="invite">%i18n:@invite%</button>
+ <p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p>
+ </div>
+
+ <div>
+ <label>
+ <input type="checkbox" v-model="disableLocalTimeline" @change="updateMeta">
+ <span>%i18n:@disableLocalTimeline%</span>
+ </label>
+ </div>
</div>
</div>
</template>
@@ -33,6 +45,7 @@ export default Vue.extend({
return {
stats: null,
disableRegistration: false,
+ disableLocalTimeline: false,
inviteCode: null,
connection: null,
connectionId: null
@@ -44,6 +57,7 @@ export default Vue.extend({
(this as any).os.getMeta().then(meta => {
this.disableRegistration = meta.disableRegistration;
+ this.disableLocalTimeline = meta.disableLocalTimeline;
});
(this as any).api('stats').then(stats => {
@@ -61,7 +75,8 @@ export default Vue.extend({
},
updateMeta() {
(this as any).api('admin/update-meta', {
- disableRegistration: this.disableRegistration
+ disableRegistration: this.disableRegistration,
+ disableLocalTimeline: this.disableLocalTimeline
});
}
}
@@ -97,4 +112,8 @@ export default Vue.extend({
border solid 1px #eee
border-radius: 8px
+ > .form
+ > div
+ border-bottom solid 1px #eee
+
</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.hashtags.vue b/src/client/app/desktop/views/pages/admin/admin.hashtags.vue
new file mode 100644
index 0000000000..c6bf20361f
--- /dev/null
+++ b/src/client/app/desktop/views/pages/admin/admin.hashtags.vue
@@ -0,0 +1,41 @@
+<template>
+<div class="jdnqwkzlnxcfftthoybjxrebyolvoucw mk-admin-card">
+ <header>%i18n:@hided-tags%</header>
+ <textarea v-model="hidedTags"></textarea>
+ <button class="ui" @click="save">%i18n:@save%</button>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from "vue";
+
+export default Vue.extend({
+ data() {
+ return {
+ hidedTags: '',
+ };
+ },
+ created() {
+ (this as any).os.getMeta().then(meta => {
+ this.hidedTags = meta.hidedTags.join('\n');
+ });
+ },
+ methods: {
+ save() {
+ (this as any).api('admin/update-meta', {
+ hidedTags: this.hidedTags.split('\n')
+ });
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.jdnqwkzlnxcfftthoybjxrebyolvoucw
+ textarea
+ width 100%
+ min-height 300px
+
+</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.vue b/src/client/app/desktop/views/pages/admin/admin.vue
index a71059c378..510252b447 100644
--- a/src/client/app/desktop/views/pages/admin/admin.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.vue
@@ -5,6 +5,8 @@
<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%fa:chalkboard .fw%%i18n:@dashboard%</li>
<li @click="nav('users')" :class="{ active: page == 'users' }">%fa:users .fw%%i18n:@users%</li>
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }">%fa:broadcast-tower .fw%%i18n:@announcements%</li>
+ <li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }">%fa:hashtag .fw%%i18n:@hashtags%</li>
+
<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }">%fa:cloud .fw%%i18n:@drive%</li> -->
<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li> -->
</ul>
@@ -17,6 +19,9 @@
<div v-show="page == 'announcements'">
<x-announcements/>
</div>
+ <div v-show="page == 'hashtags'">
+ <x-hashtags/>
+ </div>
<div v-if="page == 'users'">
<x-suspend-user/>
<x-unsuspend-user/>
@@ -33,6 +38,7 @@
import Vue from "vue";
import XDashboard from "./admin.dashboard.vue";
import XAnnouncements from "./admin.announcements.vue";
+import XHashtags from "./admin.hashtags.vue";
import XSuspendUser from "./admin.suspend-user.vue";
import XUnsuspendUser from "./admin.unsuspend-user.vue";
import XVerifyUser from "./admin.verify-user.vue";
@@ -43,6 +49,7 @@ export default Vue.extend({
components: {
XDashboard,
XAnnouncements,
+ XHashtags,
XSuspendUser,
XUnsuspendUser,
XVerifyUser,
diff --git a/src/client/app/desktop/views/pages/deck/deck.column.vue b/src/client/app/desktop/views/pages/deck/deck.column.vue
index 239b1b0447..abb09775fb 100644
--- a/src/client/app/desktop/views/pages/deck/deck.column.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.column.vue
@@ -28,6 +28,7 @@
import Vue from 'vue';
import Menu from '../../../../common/views/components/menu.vue';
import contextmenu from '../../../api/contextmenu';
+import { countIf } from '../../../../../../prelude/array';
export default Vue.extend({
props: {
@@ -117,7 +118,7 @@ export default Vue.extend({
toggleActive() {
if (!this.isStacked) return;
const vms = this.$store.state.settings.deck.layout.find(ids => ids.indexOf(this.column.id) != -1).map(id => this.getColumnVm(id));
- if (this.active && vms.filter(vm => vm.$el.classList.contains('active')).length == 1) return;
+ if (this.active && countIf(vm => vm.$el.classList.contains('active'), vms) == 1) return;
this.active = !this.active;
},
diff --git a/src/client/app/desktop/views/pages/deck/deck.list-tl.vue b/src/client/app/desktop/views/pages/deck/deck.list-tl.vue
index 70048f99e3..e82e76e4d0 100644
--- a/src/client/app/desktop/views/pages/deck/deck.list-tl.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.list-tl.vue
@@ -68,7 +68,7 @@ export default Vue.extend({
(this as any).api('notes/user-list-timeline', {
listId: this.list.id,
limit: fetchLimit + 1,
- mediaOnly: this.mediaOnly,
+ withFiles: this.mediaOnly,
includeMyRenotes: this.$store.state.settings.showMyRenotes,
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
includeLocalRenotes: this.$store.state.settings.showLocalRenotes
@@ -90,7 +90,7 @@ export default Vue.extend({
listId: this.list.id,
limit: fetchLimit + 1,
untilId: (this.$refs.timeline as any).tail().id,
- mediaOnly: this.mediaOnly,
+ withFiles: this.mediaOnly,
includeMyRenotes: this.$store.state.settings.showMyRenotes,
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
includeLocalRenotes: this.$store.state.settings.showLocalRenotes
@@ -109,7 +109,7 @@ export default Vue.extend({
return promise;
},
onNote(note) {
- if (this.mediaOnly && note.media.length == 0) return;
+ if (this.mediaOnly && note.files.length == 0) return;
// Prepend a note
(this.$refs.timeline as any).prepend(note);
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 2615c0d090..980fb03136 100644
--- a/src/client/app/desktop/views/pages/deck/deck.note.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.note.vue
@@ -18,7 +18,7 @@
<div class="body">
<p v-if="p.cw != null" class="cw">
<span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
- <span class="toggle" @click="showContent = !showContent">{{ showContent ? '%i18n:@less%' : '%i18n:@more%' }}</span>
+ <mk-cw-button v-model="showContent"/>
</p>
<div class="content" v-show="p.cw == null || showContent">
<div class="text">
@@ -28,8 +28,8 @@
<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 class="files" v-if="p.files.length > 0">
+ <mk-media-list :media-list="p.files"/>
</div>
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
<a class="location" v-if="p.geo" :href="`https://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
@@ -54,11 +54,11 @@
</article>
</div>
<div v-else class="srwrkujossgfuhrbnvqkybtzxpblgchi">
- <div v-if="note.media.length > 0">
- <mk-media-list :media-list="note.media"/>
+ <div v-if="note.files.length > 0">
+ <mk-media-list :media-list="note.files"/>
</div>
- <div v-if="note.renote && note.renote.media.length > 0">
- <mk-media-list :media-list="note.renote.media"/>
+ <div v-if="note.renote && note.renote.files.length > 0">
+ <mk-media-list :media-list="note.renote.files"/>
</div>
</div>
</template>
@@ -100,7 +100,7 @@ export default Vue.extend({
isRenote(): boolean {
return (this.note.renote &&
this.note.text == null &&
- this.note.mediaIds.length == 0 &&
+ this.note.fileIds.length == 0 &&
this.note.poll == null);
},
@@ -371,7 +371,7 @@ root(isDark)
.mk-url-preview
margin-top 8px
- > .media
+ > .files
> img
display block
max-width 100%
@@ -394,7 +394,7 @@ root(isDark)
> .renote
margin 8px 0
- > .mk-note-preview
+ > *
padding 16px
border dashed 1px isDark ? #4e945e : #c0dac6
border-radius 8px
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 f7fca5de92..2e7e30f12a 100644
--- a/src/client/app/desktop/views/pages/deck/deck.notes.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.notes.vue
@@ -127,7 +127,7 @@ export default Vue.extend({
prepend(note, silent = false) {
//#region 弾く
const isMyNote = note.userId == this.$store.state.i.id;
- const isPureRenote = note.renoteId != null && note.text == null && note.mediaIds.length == 0 && note.poll == null;
+ const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null;
if (this.$store.state.settings.showMyRenotes === false) {
if (isMyNote && isPureRenote) {
diff --git a/src/client/app/desktop/views/pages/deck/deck.tl.vue b/src/client/app/desktop/views/pages/deck/deck.tl.vue
index a9e4d489c3..120ceb7fc2 100644
--- a/src/client/app/desktop/views/pages/deck/deck.tl.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.tl.vue
@@ -96,7 +96,7 @@ export default Vue.extend({
(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
(this as any).api(this.endpoint, {
limit: fetchLimit + 1,
- mediaOnly: this.mediaOnly,
+ withFiles: this.mediaOnly,
includeMyRenotes: this.$store.state.settings.showMyRenotes,
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
includeLocalRenotes: this.$store.state.settings.showLocalRenotes
@@ -117,7 +117,7 @@ export default Vue.extend({
const promise = (this as any).api(this.endpoint, {
limit: fetchLimit + 1,
- mediaOnly: this.mediaOnly,
+ withFiles: this.mediaOnly,
untilId: (this.$refs.timeline as any).tail().id,
includeMyRenotes: this.$store.state.settings.showMyRenotes,
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
@@ -138,7 +138,7 @@ export default Vue.extend({
},
onNote(note) {
- if (this.mediaOnly && note.media.length == 0) return;
+ if (this.mediaOnly && note.files.length == 0) return;
// Prepend a note
(this.$refs.timeline as any).prepend(note);
diff --git a/src/client/app/desktop/views/pages/deck/deck.vue b/src/client/app/desktop/views/pages/deck/deck.vue
index 26b989656e..5e7a07ea6b 100644
--- a/src/client/app/desktop/views/pages/deck/deck.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.vue
@@ -85,6 +85,7 @@ export default Vue.extend({
},
mounted() {
+ document.title = (this as any).os.instanceName;
document.documentElement.style.overflow = 'hidden';
},
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 d8f4656ed0..4b434ec219 100644
--- a/src/client/app/desktop/views/pages/user/user.header.vue
+++ b/src/client/app/desktop/views/pages/user/user.header.vue
@@ -6,7 +6,7 @@
<div class="title">
<p class="name">{{ user | userName }}</p>
<div>
- <span class="username"><mk-acct :user="user"/></span>
+ <span class="username"><mk-acct :user="user" :detail="true" /></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>
diff --git a/src/client/app/desktop/views/pages/user/user.photos.vue b/src/client/app/desktop/views/pages/user/user.photos.vue
index 64c537f1ed..c5cd9e24fe 100644
--- a/src/client/app/desktop/views/pages/user/user.photos.vue
+++ b/src/client/app/desktop/views/pages/user/user.photos.vue
@@ -24,12 +24,12 @@ export default Vue.extend({
mounted() {
(this as any).api('users/notes', {
userId: this.user.id,
- withMedia: true,
+ withFiles: true,
limit: 9
}).then(notes => {
notes.forEach(note => {
- note.media.forEach(media => {
- if (this.images.length < 9) this.images.push(media);
+ note.files.forEach(file => {
+ if (this.images.length < 9) this.images.push(file);
});
});
this.fetching = false;
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 67987fcb94..54221380a7 100644
--- a/src/client/app/desktop/views/pages/user/user.timeline.vue
+++ b/src/client/app/desktop/views/pages/user/user.timeline.vue
@@ -66,7 +66,7 @@ export default Vue.extend({
limit: fetchLimit + 1,
untilDate: this.date ? this.date.getTime() : undefined,
includeReplies: this.mode == 'with-replies',
- withMedia: this.mode == 'with-media'
+ withFiles: this.mode == 'with-media'
}).then(notes => {
if (notes.length == fetchLimit + 1) {
notes.pop();
@@ -86,7 +86,7 @@ export default Vue.extend({
userId: this.user.id,
limit: fetchLimit + 1,
includeReplies: this.mode == 'with-replies',
- withMedia: this.mode == 'with-media',
+ withFiles: this.mode == 'with-media',
untilId: (this.$refs.timeline as any).tail().id
});
diff --git a/src/client/app/desktop/views/pages/welcome.vue b/src/client/app/desktop/views/pages/welcome.vue
index 0bc5c256e0..ea1734f8c7 100644
--- a/src/client/app/desktop/views/pages/welcome.vue
+++ b/src/client/app/desktop/views/pages/welcome.vue
@@ -7,45 +7,130 @@
<mk-forkit class="forkit"/>
- <div class="body">
- <div class="main block">
- <h1 v-if="name != 'Misskey'">{{ name }}</h1>
- <h1 v-else><img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" :alt="name"></h1>
+ <main>
+ <div class="body">
+ <div class="main block">
+ <div>
+ <h1 v-if="name != 'Misskey'">{{ name }}</h1>
+ <h1 v-else><img :src="$store.state.device.darkmode ? 'assets/title.dark.svg' : 'assets/title.light.svg'" :alt="name"></h1>
- <div class="info">
- <span><b>{{ host }}</b> - <span v-html="'%i18n:@powered-by-misskey%'"></span></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="info">
+ <span><b>{{ host }}</b> - <span v-html="'%i18n:@powered-by-misskey%'"></span></span>
+ <span class="stats" v-if="stats">
+ <span>%fa:user% {{ stats.originalUsersCount | number }}</span>
+ <span>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</span>
+ </span>
+ </div>
+
+ <div class="desc">
+ <span class="desc" v-html="description || '%i18n:common.about%'"></span>
+ <a class="about" @click="about">%i18n:@about%</a>
+ </div>
+
+ <p class="sign">
+ <span class="signup" @click="signup">%i18n:@signup%</span>
+ <span class="divider">|</span>
+ <span class="signin" @click="signin">%i18n:@signin%</span>
+ </p>
+
+ <img src="/assets/ai.png" alt="" title="藍" class="char">
+ </div>
</div>
- <p class="desc" v-html="description || '%i18n:common.about%'"></p>
+ <div class="announcements block">
+ <header>%fa:broadcast-tower% %i18n:@announcements%</header>
+ <div v-if="announcements && announcements.length > 0">
+ <div v-for="announcement in announcements">
+ <h1 v-html="announcement.title"></h1>
+ <div v-html="announcement.text"></div>
+ </div>
+ </div>
+ </div>
- <p class="sign">
- <span class="signup" @click="signup">%i18n:@signup%</span>
- <span class="divider">|</span>
- <span class="signin" @click="signin">%i18n:@signin%</span>
- </p>
- </div>
+ <div class="photos block">
+ <header>%fa:images% %i18n:@photos%</header>
+ <div>
+ <div v-for="photo in photos" :style="`background-image: url(${photo.thumbnailUrl})`"></div>
+ </div>
+ </div>
- <div class="broadcasts block">
- <div v-for="broadcast in broadcasts">
- <h1 v-html="broadcast.title"></h1>
- <div v-html="broadcast.text"></div>
+ <div class="tag-cloud block">
+ <div>
+ <mk-tag-cloud/>
+ </div>
</div>
- </div>
- <div class="nav block">
- <mk-nav class="nav"/>
- </div>
+ <div class="nav block">
+ <div>
+ <mk-nav class="nav"/>
+ </div>
+ </div>
+
+ <div class="side">
+ <div class="trends block">
+ <div>
+ <mk-trends/>
+ </div>
+ </div>
- <div class="side">
- <mk-trends class="trends block"/>
+ <div class="tl block">
+ <header>%fa:comment-alt R% %i18n:@timeline%</header>
+ <div>
+ <mk-welcome-timeline class="tl" :max="20"/>
+ </div>
+ </div>
- <mk-welcome-timeline class="tl block" :max="20"/>
+ <div class="info block">
+ <header>%fa:info-circle% %i18n:@info%</header>
+ <div>
+ <div v-if="meta" class="body">
+ <p>Version: <b>{{ meta.version }}</b></p>
+ <p>Maintainer: <b><a :href="meta.maintainer.url" target="_blank">{{ meta.maintainer.name }}</a></b></p>
+ </div>
+ </div>
+ </div>
+ </div>
</div>
- </div>
+ </main>
+
+ <modal name="about" :class="$store.state.device.darkmode ? ['about', 'modal-dark'] : ['about', 'modal-light']" width="800px" height="auto" scrollable>
+ <article class="fpdezooorhntlzyeszemrsqdlgbysvxq">
+ <h1>%i18n:common.intro.title%</h1>
+ <p v-html="'%i18n:common.intro.about%'"></p>
+ <section>
+ <h2>%i18n:common.intro.features%</h2>
+ <section>
+ <div class="body">
+ <h3>%i18n:common.intro.rich-contents%</h3>
+ <p v-html="'%i18n:common.intro.rich-contents-desc%'"></p>
+ </div>
+ <div class="image"><img src="/assets/about/post.png" alt=""></div>
+ </section>
+ <section>
+ <div class="body">
+ <h3>%i18n:common.intro.reaction%</h3>
+ <p v-html="'%i18n:common.intro.reaction-desc%'"></p>
+ </div>
+ <div class="image"><img src="/assets/about/reaction.png" alt=""></div>
+ </section>
+ <section>
+ <div class="body">
+ <h3>%i18n:common.intro.ui%</h3>
+ <p v-html="'%i18n:common.intro.ui-desc%'"></p>
+ </div>
+ <div class="image"><img src="/assets/about/ui.png" alt=""></div>
+ </section>
+ <section>
+ <div class="body">
+ <h3>%i18n:common.intro.drive%</h3>
+ <p v-html="'%i18n:common.intro.drive-desc%'"></p>
+ </div>
+ <div class="image"><img src="/assets/about/drive.png" alt=""></div>
+ </section>
+ </section>
+ <p v-html="'%i18n:common.intro.outro%'"></p>
+ </article>
+ </modal>
<modal name="signup" :class="$store.state.device.darkmode ? 'modal-dark' : 'modal-light'" width="450px" height="auto" scrollable>
<header class="formHeader">%i18n:@signup%</header>
@@ -62,37 +147,62 @@
<script lang="ts">
import Vue from 'vue';
import { host, copyright } from '../../../config';
+import { concat } from '../../../../../prelude/array';
export default Vue.extend({
data() {
return {
+ meta: null,
stats: null,
copyright,
host,
name: 'Misskey',
description: '',
- broadcasts: []
+ announcements: [],
+ photos: []
};
},
+
created() {
(this as any).os.getMeta().then(meta => {
+ this.meta = meta;
this.name = meta.name;
this.description = meta.description;
- this.broadcasts = meta.broadcasts;
+ this.announcements = meta.broadcasts;
});
(this as any).api('stats').then(stats => {
this.stats = stats;
});
+ const image = [
+ 'image/jpeg',
+ 'image/png',
+ 'image/gif'
+ ];
+
+ (this as any).api('notes/local-timeline', {
+ fileType: image,
+ limit: 6
+ }).then((notes: any[]) => {
+ const files = concat(notes.map((n: any): any[] => n.files));
+ this.photos = files.filter(f => image.includes(f.type)).slice(0, 6);
+ });
},
+
methods: {
+ about() {
+ this.$modal.show('about');
+ },
+
signup() {
this.$modal.show('signup');
},
+
signin() {
this.$modal.show('signin');
},
+
dark() {
this.$store.commit('device/set', {
key: 'darkmode',
@@ -137,6 +247,54 @@ export default Vue.extend({
margin 0 48px
font-size 1.5em
+.v--modal-overlay.about
+ .v--modal-box.v--modal
+ margin 32px 0
+
+.fpdezooorhntlzyeszemrsqdlgbysvxq
+ padding 64px
+
+ > p:last-child
+ margin-bottom 0
+
+ > h1
+ margin-top 0
+
+ > section
+ > h2
+ border-bottom 1px solid isDark ? rgba(#000, 0.2) : rgba(#000, 0.05)
+
+ > section
+ display grid
+ grid-template-rows 1fr
+ grid-template-columns 180px 1fr
+ gap 32px
+ margin-bottom 32px
+ padding-bottom 32px
+ border-bottom 1px solid isDark ? rgba(#000, 0.2) : rgba(#000, 0.05)
+
+ &:nth-child(odd)
+ grid-template-columns 1fr 180px
+
+ > .body
+ grid-column 1
+
+ > .image
+ grid-column 2
+
+ > .body
+ grid-row 1
+ grid-column 2
+
+ > .image
+ grid-row 1
+ grid-column 1
+
+ > img
+ display block
+ width 100%
+ height 100%
+ object-fit cover
</style>
<style lang="stylus" scoped>
@@ -164,116 +322,176 @@ root(isDark)
font-size 18px
color isDark ? #fff : #444
- > .body
- display grid
- grid-template-rows 0.5fr 0.5fr 64px
- grid-template-columns 1fr 350px
- gap 16px
- width 100%
- max-width 1200px
- height 100vh
- min-height 800px
+ > main
margin 0 auto
padding 64px
+ width 100%
+ max-width 1200px
.block
color isDark ? #fff : #444
- background isDark ? #313543 : #fff
+ background isDark ? #282C37 : #fff
box-shadow 0 3px 8px rgba(0, 0, 0, 0.2)
//border-radius 8px
overflow auto
- > .main
- grid-row 1
- grid-column 1
- padding 32px
- border-top solid 5px $theme-color
+ > header
+ z-index 1
+ padding 0 16px
+ line-height 48px
+ background isDark ? #313543 : #fff
- > h1
- margin 0
+ if !isDark
+ box-shadow 0 1px 0px rgba(0, 0, 0, 0.1)
- > img
- margin -8px 0 0 -16px
- max-width 280px
+ & + div
+ max-height calc(100% - 48px)
- > .info
- margin 0 auto 16px auto
- width $width
- font-size 14px
+ > div
+ overflow auto
- > .stats
- margin-left 16px
- padding-left 16px
- border-left solid 1px isDark ? #fff : #444
+ > .body
+ display grid
+ grid-template-rows 390px 1fr 256px 64px
+ grid-template-columns 1fr 1fr 350px
+ gap 16px
+ height 1150px
- > *
- margin-right 16px
+ > .main
+ grid-row 1
+ grid-column 1 / 3
+ border-top solid 5px $theme-color
- > .sign
- font-size 120%
+ > div
+ padding 32px
+ min-height 100%
- > .divider
- margin 0 16px
+ > h1
+ margin 0
- > .signin
- > .signup
- cursor pointer
+ > img
+ margin -8px 0 0 -16px
+ max-width 280px
- &:hover
- color $theme-color
+ > .info
+ margin 0 auto 16px auto
+ width $width
+ font-size 14px
- > .hashtags
- margin 16px auto
- width $width
- font-size 14px
- background rgba(#000, 0.3)
- border-radius 8px
+ > .stats
+ margin-left 16px
+ padding-left 16px
+ border-left solid 1px isDark ? #fff : #444
- > *
- display inline-block
- margin 14px
+ > *
+ margin-right 16px
- > .broadcasts
- grid-row 2
- grid-column 1
- padding 32px
+ > .desc
+ max-width calc(100% - 150px)
- > div
- padding 0 0 16px 0
- margin 0 0 16px 0
- border-bottom 1px solid isDark ? rgba(#000, 0.2) : rgba(#000, 0.05)
+ > .sign
+ font-size 120%
+ margin-bottom 0
- > h1
- margin 0
- font-size 1.5em
+ > .divider
+ margin 0 16px
- > .nav
- display flex
- justify-content center
- align-items center
- grid-row 3
- grid-column 1
- font-size 14px
+ > .signin
+ > .signup
+ cursor pointer
- > .side
- display grid
- grid-row 1 / 4
- grid-column 2
- grid-template-rows 1fr 350px
- grid-template-columns 1fr
- gap 16px
+ &:hover
+ color $theme-color
- > .tl
- grid-row 1
- grid-column 1
- text-align left
- max-height 100%
- overflow auto
+ > .char
+ display block
+ position absolute
+ right 16px
+ bottom 0
+ height 320px
+ opacity 0.7
- > .trends
+ > *:not(.char)
+ z-index 1
+
+ > .announcements
grid-row 2
grid-column 1
- padding 8px
+
+ > div
+ padding 32px
+
+ > div
+ padding 0 0 16px 0
+ margin 0 0 16px 0
+ border-bottom 1px solid isDark ? rgba(#000, 0.2) : rgba(#000, 0.05)
+
+ > h1
+ margin 0
+ font-size 1.25em
+
+ > .photos
+ grid-row 2
+ grid-column 2
+
+ > div
+ display grid
+ grid-template-rows 1fr 1fr 1fr
+ grid-template-columns 1fr 1fr
+ gap 8px
+ height 100%
+ padding 16px
+
+ > div
+ //border-radius 4px
+ background-position center center
+ background-size cover
+
+ > .tag-cloud
+ grid-row 3
+ grid-column 1 / 3
+
+ > div
+ height 256px
+ padding 32px
+
+ > .nav
+ display flex
+ justify-content center
+ align-items center
+ grid-row 4
+ grid-column 1 / 3
+ font-size 14px
+
+ > .side
+ display grid
+ grid-row 1 / 5
+ grid-column 3
+ grid-template-rows 1fr 350px
+ grid-template-columns 1fr
+ gap 16px
+
+ > .tl
+ grid-row 1
+ grid-column 1
+ overflow auto
+
+ > .trends
+ grid-row 2
+ grid-column 1
+ padding 8px
+
+ > .info
+ grid-row 3
+ grid-column 1
+
+ > div
+ padding 16px
+
+ > .body
+ > p
+ display block
+ margin 0
.mk-welcome[data-darkmode]
root(true)
diff --git a/src/client/app/desktop/views/widgets/trends.vue b/src/client/app/desktop/views/widgets/trends.vue
index c33bf2f2f2..aeaab63ac4 100644
--- a/src/client/app/desktop/views/widgets/trends.vue
+++ b/src/client/app/desktop/views/widgets/trends.vue
@@ -49,7 +49,7 @@ export default define({
offset: this.offset,
renote: false,
reply: false,
- media: false,
+ file: false,
poll: false
}).then(notes => {
const note = notes ? notes[0] : null;