summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2019-02-18 09:17:55 +0900
committerGitHub <noreply@github.com>2019-02-18 09:17:55 +0900
commitba1492f9773abab84277d62e957668ef2950f77a (patch)
treea5eb4c841b04e263e16bfee0ccba9785012afb78 /src/client
parentResolve #4305 (diff)
downloadmisskey-ba1492f9773abab84277d62e957668ef2950f77a.tar.gz
misskey-ba1492f9773abab84277d62e957668ef2950f77a.tar.bz2
misskey-ba1492f9773abab84277d62e957668ef2950f77a.zip
Refactor client (#4307)
* wip * wip * wip * wip * wip * wip * wip * Fix bug * :art: * :art: * :art:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/app/desktop/views/components/notes.vue83
-rw-r--r--src/client/app/desktop/views/components/user-list-timeline.vue84
-rw-r--r--src/client/app/desktop/views/deck/deck.direct.vue82
-rw-r--r--src/client/app/desktop/views/deck/deck.favorites-column.vue64
-rw-r--r--src/client/app/desktop/views/deck/deck.featured-column.vue30
-rw-r--r--src/client/app/desktop/views/deck/deck.hashtag-tl.vue86
-rw-r--r--src/client/app/desktop/views/deck/deck.list-tl.vue90
-rw-r--r--src/client/app/desktop/views/deck/deck.mentions.vue80
-rw-r--r--src/client/app/desktop/views/deck/deck.notes.vue76
-rw-r--r--src/client/app/desktop/views/deck/deck.search-column.vue82
-rw-r--r--src/client/app/desktop/views/deck/deck.tl.vue85
-rw-r--r--src/client/app/desktop/views/deck/deck.user-column.home.vue78
-rw-r--r--src/client/app/desktop/views/home/search.vue120
-rw-r--r--src/client/app/desktop/views/home/tag.vue113
-rw-r--r--src/client/app/desktop/views/home/timeline.core.vue106
-rw-r--r--src/client/app/desktop/views/home/timeline.vue164
-rw-r--r--src/client/app/desktop/views/home/user/user.home.vue5
-rw-r--r--src/client/app/desktop/views/home/user/user.timeline.vue174
-rw-r--r--src/client/app/mobile/style.styl12
-rw-r--r--src/client/app/mobile/views/components/notes.vue82
-rw-r--r--src/client/app/mobile/views/components/user-list-timeline.vue91
-rw-r--r--src/client/app/mobile/views/components/user-timeline.vue74
-rw-r--r--src/client/app/mobile/views/pages/explore.vue15
-rw-r--r--src/client/app/mobile/views/pages/favorites.vue10
-rw-r--r--src/client/app/mobile/views/pages/featured.vue10
-rw-r--r--src/client/app/mobile/views/pages/home.timeline.vue80
-rw-r--r--src/client/app/mobile/views/pages/home.vue11
-rw-r--r--src/client/app/mobile/views/pages/note.vue12
-rw-r--r--src/client/app/mobile/views/pages/notifications.vue15
-rw-r--r--src/client/app/mobile/views/pages/received-follow-requests.vue11
-rw-r--r--src/client/app/mobile/views/pages/search.vue91
-rw-r--r--src/client/app/mobile/views/pages/settings.vue3
-rw-r--r--src/client/app/mobile/views/pages/tag.vue78
-rw-r--r--src/client/app/mobile/views/pages/user-list.vue15
-rw-r--r--src/client/app/mobile/views/pages/user-lists.vue17
-rw-r--r--src/client/app/mobile/views/pages/user/home.vue1
-rw-r--r--src/client/app/mobile/views/pages/user/index.vue24
37 files changed, 733 insertions, 1521 deletions
diff --git a/src/client/app/desktop/views/components/notes.vue b/src/client/app/desktop/views/components/notes.vue
index 5cf51d9cc4..d1bf6dcc04 100644
--- a/src/client/app/desktop/views/components/notes.vue
+++ b/src/client/app/desktop/views/components/notes.vue
@@ -1,10 +1,12 @@
<template>
<div class="mk-notes">
+ <slot name="header"></slot>
+
<div class="newer-indicator" :style="{ top: $store.state.uiHeaderHeight + 'px' }" v-show="queue.length > 0"></div>
- <slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
+ <slot name="empty" v-if="notes.length == 0 && !fetching && inited"></slot>
- <mk-error v-if="!fetching && requestInitPromise != null" @retry="resolveInitPromise"/>
+ <mk-error v-if="!fetching && !inited" @retry="init()"/>
<div class="placeholder" v-if="fetching">
<template v-for="i in 10">
@@ -23,8 +25,8 @@
</template>
</component>
- <footer v-if="more">
- <button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
+ <footer v-if="cursor != null">
+ <button @click="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<template v-if="!moreFetching">{{ $t('@.load-more') }}</template>
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>
</button>
@@ -43,24 +45,25 @@ const displayLimit = 30;
export default Vue.extend({
i18n: i18n(),
+
components: {
XNote
},
props: {
- more: {
- type: Function,
- required: false
+ makePromise: {
+ required: true
}
},
data() {
return {
- requestInitPromise: null as () => Promise<any[]>,
notes: [],
queue: [],
fetching: true,
- moreFetching: false
+ moreFetching: false,
+ inited: false,
+ cursor: null
};
},
@@ -76,6 +79,10 @@ export default Vue.extend({
}
},
+ created() {
+ this.init();
+ },
+
mounted() {
window.addEventListener('scroll', this.onScroll, { passive: true });
},
@@ -97,27 +104,41 @@ export default Vue.extend({
Vue.set((this as any).notes, i, note);
},
- init(promiseGenerator: () => Promise<any[]>) {
- this.requestInitPromise = promiseGenerator;
- this.resolveInitPromise();
- },
-
- resolveInitPromise() {
+ reload() {
this.queue = [];
this.notes = [];
- this.fetching = true;
-
- const promise = this.requestInitPromise();
+ this.init();
+ },
- promise.then(notes => {
- this.notes = notes;
- this.requestInitPromise = null;
+ init() {
+ this.fetching = true;
+ this.makePromise().then(x => {
+ if (Array.isArray(x)) {
+ this.notes = x;
+ } else {
+ this.notes = x.notes;
+ this.cursor = x.cursor;
+ }
+ this.inited = true;
this.fetching = false;
+ this.$emit('inited');
}, e => {
this.fetching = false;
});
},
+ more() {
+ if (this.cursor == null || this.moreFetching) return;
+ this.moreFetching = true;
+ this.makePromise(this.cursor).then(x => {
+ this.notes = this.notes.concat(x.notes);
+ this.cursor = x.cursor;
+ this.moreFetching = false;
+ }, e => {
+ this.moreFetching = false;
+ });
+ },
+
prepend(note, silent = false) {
// 弾く
if (shouldMuteNote(this.$store.state.i, this.$store.state.settings, note)) return;
@@ -151,10 +172,6 @@ export default Vue.extend({
this.notes.push(note);
},
- tail() {
- return this.notes[this.notes.length - 1];
- },
-
releaseQueue() {
for (const n of this.queue) {
this.prepend(n, true);
@@ -162,15 +179,6 @@ export default Vue.extend({
this.queue = [];
},
- async loadMore() {
- if (this.more == null) return;
- if (this.moreFetching) return;
-
- this.moreFetching = true;
- await this.more();
- this.moreFetching = false;
- },
-
onScroll() {
if (this.isScrollTop()) {
this.releaseQueue();
@@ -178,7 +186,7 @@ export default Vue.extend({
if (this.$store.state.settings.fetchOnScroll !== false) {
const current = window.scrollY + window.innerHeight;
- if (current > document.body.offsetHeight - 8) this.loadMore();
+ if (current > document.body.offsetHeight - 8) this.more();
}
}
}
@@ -187,6 +195,11 @@ export default Vue.extend({
<style lang="stylus" scoped>
.mk-notes
+ background var(--face)
+ box-shadow var(--shadow)
+ border-radius var(--round)
+ overflow hidden
+
.transition
.mk-notes-enter
.mk-notes-leave-to
diff --git a/src/client/app/desktop/views/components/user-list-timeline.vue b/src/client/app/desktop/views/components/user-list-timeline.vue
index 8afd95a68e..d61de06eed 100644
--- a/src/client/app/desktop/views/components/user-list-timeline.vue
+++ b/src/client/app/desktop/views/components/user-list-timeline.vue
@@ -1,6 +1,10 @@
<template>
<div>
- <mk-notes ref="timeline" :more="existMore ? more : null"/>
+ <mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')">
+ <template slot="header">
+ <slot></slot>
+ </template>
+ </mk-notes>
</div>
</template>
@@ -13,10 +17,28 @@ export default Vue.extend({
props: ['list'],
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
- connection: null
+ connection: null,
+ makePromise: cursor => this.$root.api('notes/user-list-timeline', {
+ listId: this.list.id,
+ limit: fetchLimit + 1,
+ untilId: cursor ? cursor : undefined,
+ includeMyRenotes: this.$store.state.settings.showMyRenotes,
+ includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+ includeLocalRenotes: this.$store.state.settings.showLocalRenotes
+ }).then(notes => {
+ if (notes.length == fetchLimit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: notes[notes.length - 1].id
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ })
};
},
watch: {
@@ -37,63 +59,15 @@ export default Vue.extend({
this.connection.on('note', this.onNote);
this.connection.on('userAdded', this.onUserAdded);
this.connection.on('userRemoved', this.onUserRemoved);
-
- this.fetch();
- },
- fetch() {
- this.fetching = true;
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('notes/user-list-timeline', {
- listId: this.list.id,
- limit: fetchLimit + 1,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes
- }).then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- this.$emit('loaded');
- }, rej);
- }));
- },
- more() {
- this.moreFetching = true;
-
- const promise = this.$root.api('notes/user-list-timeline', {
- listId: this.list.id,
- limit: fetchLimit + 1,
- untilId: (this.$refs.timeline as any).tail().id,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes
- });
-
- promise.then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) (this.$refs.timeline as any).append(n);
- this.moreFetching = false;
- });
-
- return promise;
},
onNote(note) {
- // Prepend a note
(this.$refs.timeline as any).prepend(note);
},
onUserAdded() {
- this.fetch();
+ (this.$refs.timeline as any).reload();
},
onUserRemoved() {
- this.fetch();
+ (this.$refs.timeline as any).reload();
}
}
});
diff --git a/src/client/app/desktop/views/deck/deck.direct.vue b/src/client/app/desktop/views/deck/deck.direct.vue
index c6c2b99233..2618363b14 100644
--- a/src/client/app/desktop/views/deck/deck.direct.vue
+++ b/src/client/app/desktop/views/deck/deck.direct.vue
@@ -1,5 +1,5 @@
<template>
-<x-notes ref="timeline" :more="existMore ? more : null"/>
+<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
</template>
<script lang="ts">
@@ -13,23 +13,36 @@ export default Vue.extend({
XNotes
},
- props: {
- },
-
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
- connection: null
+ connection: null,
+ makePromise: cursor => this.$root.api('notes/mentions', {
+ limit: fetchLimit + 1,
+ untilId: cursor ? cursor : undefined,
+ includeMyRenotes: this.$store.state.settings.showMyRenotes,
+ includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+ includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
+ visibility: 'specified'
+ }).then(notes => {
+ if (notes.length == fetchLimit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: notes[notes.length - 1].id
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ })
};
},
mounted() {
this.connection = this.$root.stream.useSharedConnection('main');
this.connection.on('mention', this.onNote);
-
- this.fetch();
},
beforeDestroy() {
@@ -37,55 +50,6 @@ export default Vue.extend({
},
methods: {
- fetch() {
- this.fetching = true;
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('notes/mentions', {
- limit: fetchLimit + 1,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
- visibility: 'specified'
- }).then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- this.$emit('loaded');
- }, rej);
- }));
- },
-
- more() {
- this.moreFetching = true;
-
- const promise = this.$root.api('notes/mentions', {
- limit: fetchLimit + 1,
- untilId: (this.$refs.timeline as any).tail().id,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
- visibility: 'specified'
- });
-
- promise.then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
- },
-
onNote(note) {
// Prepend a note
if (note.visibility == 'specified') {
diff --git a/src/client/app/desktop/views/deck/deck.favorites-column.vue b/src/client/app/desktop/views/deck/deck.favorites-column.vue
index 3c2b50dee8..e0d5f8a339 100644
--- a/src/client/app/desktop/views/deck/deck.favorites-column.vue
+++ b/src/client/app/desktop/views/deck/deck.favorites-column.vue
@@ -5,7 +5,7 @@
</span>
<div>
- <x-notes ref="timeline" :more="existMore ? more : null"/>
+ <x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
</div>
</x-column>
</template>
@@ -28,58 +28,28 @@ export default Vue.extend({
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
- };
- },
-
- mounted() {
- this.fetch();
- },
-
- methods: {
- fetch() {
- this.fetching = true;
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('i/favorites', {
- limit: fetchLimit + 1,
- }).then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes.map(x => x.note));
- this.fetching = false;
- this.$emit('loaded');
- }, rej);
- }));
- },
-
- more() {
- this.moreFetching = true;
-
- const promise = this.$root.api('i/favorites', {
+ makePromise: cursor => this.$root.api('i/favorites', {
limit: fetchLimit + 1,
- untilId: (this.$refs.timeline as any).tail().id,
- });
-
- promise.then(notes => {
+ untilId: cursor ? cursor : undefined,
+ }).then(notes => {
+ notes = notes.map(x => x.note);
if (notes.length == fetchLimit + 1) {
notes.pop();
+ return {
+ notes: notes,
+ cursor: notes[notes.length - 1].id
+ };
} else {
- this.existMore = false;
+ return {
+ notes: notes,
+ cursor: null
+ };
}
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
- },
+ })
+ };
+ },
+ methods: {
focus() {
this.$refs.timeline.focus();
}
diff --git a/src/client/app/desktop/views/deck/deck.featured-column.vue b/src/client/app/desktop/views/deck/deck.featured-column.vue
index 78a5a7e3f5..e654c1eaae 100644
--- a/src/client/app/desktop/views/deck/deck.featured-column.vue
+++ b/src/client/app/desktop/views/deck/deck.featured-column.vue
@@ -5,7 +5,7 @@
</span>
<div>
- <x-notes ref="timeline" :more="null"/>
+ <x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
</div>
</x-column>
</template>
@@ -27,31 +27,17 @@ export default Vue.extend({
data() {
return {
- fetching: true,
- faNewspaper
+ faNewspaper,
+ makePromise: cursor => this.$root.api('notes/featured', {
+ limit: 20,
+ }).then(notes => {
+ notes.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
+ return notes;
+ })
};
},
- mounted() {
- this.fetch();
- },
-
methods: {
- fetch() {
- this.fetching = true;
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('notes/featured', {
- limit: 20,
- }).then(notes => {
- notes.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
- res(notes);
- this.fetching = false;
- this.$emit('loaded');
- }, rej);
- }));
- },
-
focus() {
this.$refs.timeline.focus();
}
diff --git a/src/client/app/desktop/views/deck/deck.hashtag-tl.vue b/src/client/app/desktop/views/deck/deck.hashtag-tl.vue
index 9a70733fda..e19c134849 100644
--- a/src/client/app/desktop/views/deck/deck.hashtag-tl.vue
+++ b/src/client/app/desktop/views/deck/deck.hashtag-tl.vue
@@ -1,5 +1,5 @@
<template>
-<x-notes ref="timeline" :more="existMore ? more : null" :media-view="mediaView"/>
+<x-notes ref="timeline" :make-promise="makePromise" :media-view="mediaView" @inited="() => $emit('loaded')"/>
</template>
<script lang="ts">
@@ -32,16 +32,35 @@ export default Vue.extend({
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
- connection: null
+ connection: null,
+ makePromise: cursor => this.$root.api('notes/search_by_tag', {
+ limit: fetchLimit + 1,
+ untilId: cursor ? cursor : undefined,
+ withFiles: this.mediaOnly,
+ includeMyRenotes: this.$store.state.settings.showMyRenotes,
+ includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+ includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
+ query: this.tagTl.query
+ }).then(notes => {
+ if (notes.length == fetchLimit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: notes[notes.length - 1].id
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ })
};
},
watch: {
mediaOnly() {
- this.fetch();
+ this.$refs.timeline.reload();
}
},
@@ -51,8 +70,6 @@ export default Vue.extend({
q: this.tagTl.query
});
this.connection.on('note', this.onNote);
-
- this.fetch();
},
beforeDestroy() {
@@ -60,61 +77,8 @@ export default Vue.extend({
},
methods: {
- fetch() {
- this.fetching = true;
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('notes/search_by_tag', {
- limit: fetchLimit + 1,
- withFiles: this.mediaOnly,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
- query: this.tagTl.query
- }).then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- this.$emit('loaded');
- }, rej);
- }));
- },
-
- more() {
- this.moreFetching = true;
-
- const promise = this.$root.api('notes/search_by_tag', {
- limit: fetchLimit + 1,
- untilId: (this.$refs.timeline as any).tail().id,
- withFiles: this.mediaOnly,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
- query: this.tagTl.query
- });
-
- promise.then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
- },
-
onNote(note) {
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/deck/deck.list-tl.vue b/src/client/app/desktop/views/deck/deck.list-tl.vue
index 68fbbb3ff9..7166263295 100644
--- a/src/client/app/desktop/views/deck/deck.list-tl.vue
+++ b/src/client/app/desktop/views/deck/deck.list-tl.vue
@@ -1,5 +1,5 @@
<template>
-<x-notes ref="timeline" :more="existMore ? more : null" :media-view="mediaView"/>
+<x-notes ref="timeline" :make-promise="makePromise" :media-view="mediaView" @inited="() => $emit('loaded')"/>
</template>
<script lang="ts">
@@ -32,16 +32,35 @@ export default Vue.extend({
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
- connection: null
+ connection: null,
+ makePromise: cursor => this.$root.api('notes/user-list-timeline', {
+ listId: this.list.id,
+ limit: fetchLimit + 1,
+ untilId: cursor ? cursor : undefined,
+ withFiles: this.mediaOnly,
+ includeMyRenotes: this.$store.state.settings.showMyRenotes,
+ includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+ includeLocalRenotes: this.$store.state.settings.showLocalRenotes
+ }).then(notes => {
+ if (notes.length == fetchLimit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: notes[notes.length - 1].id
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ })
};
},
watch: {
mediaOnly() {
- this.fetch();
+ this.$refs.timeline.reload();
}
},
@@ -53,8 +72,6 @@ export default Vue.extend({
this.connection.on('note', this.onNote);
this.connection.on('userAdded', this.onUserAdded);
this.connection.on('userRemoved', this.onUserRemoved);
-
- this.fetch();
},
beforeDestroy() {
@@ -62,70 +79,17 @@ export default Vue.extend({
},
methods: {
- fetch() {
- this.fetching = true;
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('notes/user-list-timeline', {
- listId: this.list.id,
- limit: fetchLimit + 1,
- withFiles: this.mediaOnly,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes
- }).then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- this.$emit('loaded');
- }, rej);
- }));
- },
-
- more() {
- this.moreFetching = true;
-
- const promise = this.$root.api('notes/user-list-timeline', {
- listId: this.list.id,
- limit: fetchLimit + 1,
- untilId: (this.$refs.timeline as any).tail().id,
- withFiles: this.mediaOnly,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes
- });
-
- promise.then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
- },
-
onNote(note) {
if (this.mediaOnly && note.files.length == 0) return;
-
- // Prepend a note
(this.$refs.timeline as any).prepend(note);
},
onUserAdded() {
- this.fetch();
+ this.$refs.timeline.reload();
},
onUserRemoved() {
- this.fetch();
+ this.$refs.timeline.reload();
},
focus() {
diff --git a/src/client/app/desktop/views/deck/deck.mentions.vue b/src/client/app/desktop/views/deck/deck.mentions.vue
index 5fcabde5d6..1efd778226 100644
--- a/src/client/app/desktop/views/deck/deck.mentions.vue
+++ b/src/client/app/desktop/views/deck/deck.mentions.vue
@@ -1,5 +1,5 @@
<template>
-<x-notes ref="timeline" :more="existMore ? more : null"/>
+<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
</template>
<script lang="ts">
@@ -13,23 +13,35 @@ export default Vue.extend({
XNotes
},
- props: {
- },
-
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
- connection: null
+ connection: null,
+ makePromise: cursor => this.$root.api('notes/mentions', {
+ limit: fetchLimit + 1,
+ untilId: cursor ? cursor : undefined,
+ includeMyRenotes: this.$store.state.settings.showMyRenotes,
+ includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+ includeLocalRenotes: this.$store.state.settings.showLocalRenotes
+ }).then(notes => {
+ if (notes.length == fetchLimit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: notes[notes.length - 1].id
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ })
};
},
mounted() {
this.connection = this.$root.stream.useSharedConnection('main');
this.connection.on('mention', this.onNote);
-
- this.fetch();
},
beforeDestroy() {
@@ -37,55 +49,7 @@ export default Vue.extend({
},
methods: {
- fetch() {
- this.fetching = true;
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('notes/mentions', {
- limit: fetchLimit + 1,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes
- }).then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- this.$emit('loaded');
- }, rej);
- }));
- },
-
- more() {
- this.moreFetching = true;
-
- const promise = this.$root.api('notes/mentions', {
- limit: fetchLimit + 1,
- untilId: (this.$refs.timeline as any).tail().id,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes
- });
-
- promise.then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
- },
-
onNote(note) {
- // Prepend a note
(this.$refs.timeline as any).prepend(note);
},
diff --git a/src/client/app/desktop/views/deck/deck.notes.vue b/src/client/app/desktop/views/deck/deck.notes.vue
index 260d75a884..e7fa9fd52a 100644
--- a/src/client/app/desktop/views/deck/deck.notes.vue
+++ b/src/client/app/desktop/views/deck/deck.notes.vue
@@ -1,6 +1,8 @@
<template>
<div class="eamppglmnmimdhrlzhplwpvyeaqmmhxu">
- <slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
+ <slot name="empty" v-if="notes.length == 0 && !fetching && inited"></slot>
+
+ <mk-error v-if="!fetching && !inited" @retry="init()"/>
<div class="placeholder" v-if="fetching">
<template v-for="i in 10">
@@ -8,8 +10,6 @@
</template>
</div>
- <mk-error v-if="!fetching && requestInitPromise != null" @retry="resolveInitPromise"/>
-
<!-- トランジションを有効にするとなぜかメモリリークする -->
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="transition notes" ref="notes" tag="div">
<template v-for="(note, i) in _notes">
@@ -27,8 +27,8 @@
</template>
</component>
- <footer v-if="more">
- <button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
+ <footer v-if="cursor != null">
+ <button @click="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<template v-if="!moreFetching">{{ $t('@.load-more') }}</template>
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>
</button>
@@ -40,13 +40,13 @@
import Vue from 'vue';
import i18n from '../../../i18n';
import shouldMuteNote from '../../../common/scripts/should-mute-note';
-
import XNote from '../components/note.vue';
const displayLimit = 20;
export default Vue.extend({
i18n: i18n(),
+
components: {
XNote
},
@@ -54,9 +54,8 @@ export default Vue.extend({
inject: ['column', 'isScrollTop', 'count'],
props: {
- more: {
- type: Function,
- required: false
+ makePromise: {
+ required: true
},
mediaView: {
type: Boolean,
@@ -68,11 +67,12 @@ export default Vue.extend({
data() {
return {
rootEl: null,
- requestInitPromise: null as () => Promise<any[]>,
notes: [],
queue: [],
fetching: true,
- moreFetching: false
+ moreFetching: false,
+ inited: false,
+ cursor: null
};
},
@@ -97,6 +97,7 @@ export default Vue.extend({
created() {
this.column.$on('top', this.onTop);
this.column.$on('bottom', this.onBottom);
+ this.init();
},
beforeDestroy() {
@@ -113,27 +114,41 @@ export default Vue.extend({
Vue.set((this as any).notes, i, note);
},
- init(promiseGenerator: () => Promise<any[]>) {
- this.requestInitPromise = promiseGenerator;
- this.resolveInitPromise();
- },
-
- resolveInitPromise() {
+ reload() {
this.queue = [];
this.notes = [];
- this.fetching = true;
-
- const promise = this.requestInitPromise();
+ this.init();
+ },
- promise.then(notes => {
- this.notes = notes;
- this.requestInitPromise = null;
+ init() {
+ this.fetching = true;
+ this.makePromise().then(x => {
+ if (Array.isArray(x)) {
+ this.notes = x;
+ } else {
+ this.notes = x.notes;
+ this.cursor = x.cursor;
+ }
+ this.inited = true;
this.fetching = false;
+ this.$emit('inited');
}, e => {
this.fetching = false;
});
},
+ more() {
+ if (this.cursor == null || this.moreFetching) return;
+ this.moreFetching = true;
+ this.makePromise(this.cursor).then(x => {
+ this.notes = this.notes.concat(x.notes);
+ this.cursor = x.cursor;
+ this.moreFetching = false;
+ }, e => {
+ this.moreFetching = false;
+ });
+ },
+
prepend(note, silent = false) {
// 弾く
if (shouldMuteNote(this.$store.state.i, this.$store.state.settings, note)) return;
@@ -160,10 +175,6 @@ export default Vue.extend({
this.notes.push(note);
},
- tail() {
- return this.notes[this.notes.length - 1];
- },
-
releaseQueue() {
for (const n of this.queue) {
this.prepend(n, true);
@@ -171,21 +182,12 @@ export default Vue.extend({
this.queue = [];
},
- async loadMore() {
- if (this.more == null) return;
- if (this.moreFetching) return;
-
- this.moreFetching = true;
- await this.more();
- this.moreFetching = false;
- },
-
onTop() {
this.releaseQueue();
},
onBottom() {
- this.loadMore();
+ this.more();
}
}
});
diff --git a/src/client/app/desktop/views/deck/deck.search-column.vue b/src/client/app/desktop/views/deck/deck.search-column.vue
index d732f524da..cc719bdf5c 100644
--- a/src/client/app/desktop/views/deck/deck.search-column.vue
+++ b/src/client/app/desktop/views/deck/deck.search-column.vue
@@ -5,7 +5,7 @@
</span>
<div>
- <x-notes ref="timeline" :more="existMore ? more : null"/>
+ <x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
</div>
</x-column>
</template>
@@ -25,12 +25,24 @@ export default Vue.extend({
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
- offset: 0,
- empty: false,
- notAvailable: false
+ makePromise: cursor => this.$root.api('notes/search', {
+ limit: limit + 1,
+ offset: cursor ? cursor : undefined,
+ query: this.q
+ }).then(notes => {
+ if (notes.length == limit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: cursor ? cursor + limit : limit
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ })
};
},
@@ -41,59 +53,9 @@ export default Vue.extend({
},
watch: {
- $route: 'fetch'
- },
-
- created() {
- this.fetch();
- },
-
- methods: {
- fetch() {
- this.fetching = true;
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('notes/search', {
- limit: limit + 1,
- offset: this.offset,
- query: this.q
- }).then(notes => {
- if (notes.length == 0) this.empty = true;
- if (notes.length == limit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- }, (e: string) => {
- this.fetching = false;
- if (e === 'searching not available') this.notAvailable = true;
- });
- }));
- },
- more() {
- this.offset += limit;
-
- const promise = this.$root.api('notes/search', {
- limit: limit + 1,
- offset: this.offset,
- query: this.q
- });
-
- promise.then(notes => {
- if (notes.length == limit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
+ $route() {
+ this.$refs.timeline.reload();
}
- }
+ },
});
</script>
diff --git a/src/client/app/desktop/views/deck/deck.tl.vue b/src/client/app/desktop/views/deck/deck.tl.vue
index 16f268f2c1..263c2a0820 100644
--- a/src/client/app/desktop/views/deck/deck.tl.vue
+++ b/src/client/app/desktop/views/deck/deck.tl.vue
@@ -6,7 +6,7 @@
</p>
<p class="desc">{{ $t('disabled-timeline.description') }}</p>
</div>
-<x-notes v-else ref="timeline" :more="existMore ? more : null" :media-view="mediaView"/>
+<x-notes v-else ref="timeline" :make-promise="makePromise" :media-view="mediaView" @inited="() => $emit('loaded')"/>
</template>
<script lang="ts">
@@ -44,12 +44,10 @@ export default Vue.extend({
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
connection: null,
disabled: false,
- faMinusCircle
+ faMinusCircle,
+ makePromise: null
};
},
@@ -79,6 +77,28 @@ export default Vue.extend({
}
},
+ created() {
+ this.makePromise = cursor => this.$root.api(this.endpoint, {
+ limit: fetchLimit + 1,
+ untilDate: cursor ? undefined : (this.date ? this.date.getTime() : undefined),
+ untilId: cursor ? cursor : undefined,
+ ...this.baseQuery, ...this.query
+ }).then(notes => {
+ if (notes.length == fetchLimit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: notes[notes.length - 1].id
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ });
+ },
+
mounted() {
this.connection = this.stream;
@@ -93,8 +113,6 @@ export default Vue.extend({
meta.disableLocalTimeline && ['local', 'hybrid'].includes(this.src) ||
meta.disableGlobalTimeline && ['global'].includes(this.src));
});
-
- this.fetch();
},
beforeDestroy() {
@@ -102,64 +120,13 @@ export default Vue.extend({
},
methods: {
- fetch() {
- this.fetching = true;
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api(this.endpoint, {
- limit: fetchLimit + 1,
- withFiles: this.mediaOnly,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes
- }).then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- this.$emit('loaded');
- }, rej);
- }));
- },
-
- more() {
- this.moreFetching = true;
-
- const promise = this.$root.api(this.endpoint, {
- limit: fetchLimit + 1,
- withFiles: this.mediaOnly,
- untilId: (this.$refs.timeline as any).tail().id,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes
- });
-
- promise.then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
- },
-
onNote(note) {
if (this.mediaOnly && note.files.length == 0) return;
-
- // Prepend a note
(this.$refs.timeline as any).prepend(note);
},
onChangeFollowing() {
- this.fetch();
+ (this.$refs.timeline as any).reload();
},
focus() {
diff --git a/src/client/app/desktop/views/deck/deck.user-column.home.vue b/src/client/app/desktop/views/deck/deck.user-column.home.vue
index 966c5bdb1b..ff13bc3124 100644
--- a/src/client/app/desktop/views/deck/deck.user-column.home.vue
+++ b/src/client/app/desktop/views/deck/deck.user-column.home.vue
@@ -26,7 +26,7 @@
<ui-container>
<span slot="header"><fa :icon="['far', 'comment-alt']"/> {{ $t('timeline') }}</span>
<div>
- <x-notes ref="timeline" :more="existMore ? fetchMoreNotes : null"/>
+ <x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
</div>
</ui-container>
</div>
@@ -35,7 +35,6 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
-import parseAcct from '../../../../../misc/acct/parse';
import XNotes from './deck.notes.vue';
import XNote from '../components/note.vue';
import { concat } from '../../../../../prelude/array';
@@ -45,6 +44,7 @@ const fetchLimit = 10;
export default Vue.extend({
i18n: i18n('deck/deck.user-column.vue'),
+
components: {
XNotes,
XNote
@@ -59,10 +59,30 @@ export default Vue.extend({
data() {
return {
- existMore: false,
- moreFetching: false,
withFiles: false,
images: [],
+ makePromise: cursor => this.$root.api('users/notes', {
+ userId: this.user.id,
+ limit: fetchLimit + 1,
+ untilId: cursor ? cursor : undefined,
+ withFiles: this.withFiles,
+ includeMyRenotes: this.$store.state.settings.showMyRenotes,
+ includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+ includeLocalRenotes: this.$store.state.settings.showLocalRenotes
+ }).then(notes => {
+ if (notes.length == fetchLimit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: notes[notes.length - 1].id
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ })
};
},
@@ -72,10 +92,6 @@ export default Vue.extend({
methods: {
fetch() {
- this.$nextTick(() => {
- (this.$refs.timeline as any).init(() => this.initTl());
- });
-
const image = [
'image/jpeg',
'image/png',
@@ -177,52 +193,6 @@ export default Vue.extend({
chart.render();
});
},
-
- initTl() {
- return new Promise((res, rej) => {
- this.$root.api('users/notes', {
- userId: this.user.id,
- limit: fetchLimit + 1,
- untilDate: new Date().getTime() + 1000 * 86400 * 365,
- withFiles: this.withFiles,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes
- }).then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- }, rej);
- });
- },
-
- fetchMoreNotes() {
- this.moreFetching = true;
-
- const promise = this.$root.api('users/notes', {
- userId: this.user.id,
- limit: fetchLimit + 1,
- untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime(),
- withFiles: this.withFiles,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes
- });
-
- promise.then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) (this.$refs.timeline as any).append(n);
- this.moreFetching = false;
- });
-
- return promise;
- }
}
});
</script>
diff --git a/src/client/app/desktop/views/home/search.vue b/src/client/app/desktop/views/home/search.vue
index 993467b4bf..cd21bd5b2a 100644
--- a/src/client/app/desktop/views/home/search.vue
+++ b/src/client/app/desktop/views/home/search.vue
@@ -1,13 +1,10 @@
<template>
-<div class="oxgbmvii">
- <div class="notes">
- <header>
+<div>
+ <mk-notes ref="timeline" :make-promise="makePromise" @inited="inited">
+ <header slot="header" class="oxgbmvii">
<span><fa icon="search"/> {{ q }}</span>
</header>
- <p v-if="!fetching && notAvailable">{{ $t('not-available') }}</p>
- <p v-if="!fetching && empty"><fa icon="search"/> {{ $t('not-found', { q }) }}</p>
- <mk-notes ref="timeline" :more="existMore ? more : null"/>
- </div>
+ </mk-notes>
</div>
</template>
@@ -22,27 +19,40 @@ export default Vue.extend({
i18n: i18n('desktop/views/pages/search.vue'),
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
- offset: 0,
- empty: false,
- notAvailable: false
+ makePromise: cursor => this.$root.api('notes/search', {
+ limit: limit + 1,
+ offset: cursor ? cursor : undefined,
+ query: this.q
+ }).then(notes => {
+ if (notes.length == limit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: cursor ? cursor + limit : limit
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ })
};
},
- watch: {
- $route: 'fetch'
- },
computed: {
q(): string {
return this.$route.query.q;
}
},
+ watch: {
+ $route() {
+ this.$refs.timeline.reload();
+ }
+ },
mounted() {
document.addEventListener('keydown', this.onDocumentKeydown);
window.addEventListener('scroll', this.onScroll, { passive: true });
-
- this.fetch();
+ Progress.start();
},
beforeDestroy() {
document.removeEventListener('keydown', this.onDocumentKeydown);
@@ -56,75 +66,23 @@ export default Vue.extend({
}
}
},
- fetch() {
- this.fetching = true;
- Progress.start();
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('notes/search', {
- limit: limit + 1,
- offset: this.offset,
- query: this.q
- }).then(notes => {
- if (notes.length == 0) this.empty = true;
- if (notes.length == limit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- Progress.done();
- }, (e: string) => {
- this.fetching = false;
- Progress.done();
- if (e === 'searching not available') this.notAvailable = true;
- });
- }));
+ inited() {
+ Progress.done();
},
- more() {
- this.offset += limit;
-
- const promise = this.$root.api('notes/search', {
- limit: limit + 1,
- offset: this.offset,
- query: this.q
- });
-
- promise.then(notes => {
- if (notes.length == limit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
- }
}
});
</script>
<style lang="stylus" scoped>
.oxgbmvii
- > .notes
- background var(--face)
- box-shadow var(--shadow)
- border-radius var(--round)
- overflow hidden
-
- > header
- padding 0 8px
- z-index 10
- background var(--faceHeader)
- box-shadow 0 var(--lineWidth) var(--desktopTimelineHeaderShadow)
+ padding 0 8px
+ z-index 10
+ background var(--faceHeader)
+ box-shadow 0 var(--lineWidth) var(--desktopTimelineHeaderShadow)
- > span
- padding 0 8px
- font-size 0.9em
- line-height 42px
- color var(--text)
+ > span
+ padding 0 8px
+ font-size 0.9em
+ line-height 42px
+ color var(--text)
</style>
diff --git a/src/client/app/desktop/views/home/tag.vue b/src/client/app/desktop/views/home/tag.vue
index 182c8f3512..2f9854c074 100644
--- a/src/client/app/desktop/views/home/tag.vue
+++ b/src/client/app/desktop/views/home/tag.vue
@@ -1,7 +1,10 @@
<template>
<div>
- <p :class="$style.empty" v-if="!fetching && empty"><fa icon="search"/> {{ $t('no-posts-found', { q: $route.params.tag }) }}</p>
- <mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/>
+ <mk-notes ref="timeline" :make-promise="makePromise" @inited="inited">
+ <header class="wqraeznr" slot="header">
+ <span><fa icon="hashtag"/> {{ $route.params.tag }}</span>
+ </header>
+ </mk-notes>
</div>
</template>
@@ -16,21 +19,35 @@ export default Vue.extend({
i18n: i18n('desktop/views/pages/tag.vue'),
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
- offset: 0,
- empty: false
+ makePromise: cursor => this.$root.api('notes/search_by_tag', {
+ limit: limit + 1,
+ offset: cursor ? cursor : undefined,
+ tag: this.$route.params.tag
+ }).then(notes => {
+ if (notes.length == limit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: cursor ? cursor + limit : limit
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ })
};
},
watch: {
- $route: 'fetch'
+ $route() {
+ this.$refs.timeline.reload();
+ }
},
mounted() {
document.addEventListener('keydown', this.onDocumentKeydown);
window.addEventListener('scroll', this.onScroll, { passive: true });
-
- this.fetch();
+ Progress.start();
},
beforeDestroy() {
document.removeEventListener('keydown', this.onDocumentKeydown);
@@ -44,73 +61,23 @@ export default Vue.extend({
}
}
},
- fetch() {
- this.fetching = true;
- Progress.start();
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('notes/search_by_tag', {
- limit: limit + 1,
- offset: this.offset,
- tag: this.$route.params.tag
- }).then(notes => {
- if (notes.length == 0) this.empty = true;
- if (notes.length == limit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- Progress.done();
- }, rej);
- }));
+ inited() {
+ Progress.done();
},
- more() {
- this.offset += limit;
-
- const promise = this.$root.api('notes/search_by_tag', {
- limit: limit + 1,
- offset: this.offset,
- tag: this.$route.params.tag
- });
-
- promise.then(notes => {
- if (notes.length == limit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
- }
}
});
</script>
-<style lang="stylus" module>
-.notes
- background var(--face)
- box-shadow var(--shadow)
- border-radius var(--round)
- overflow hidden
-
-.empty
- display block
- margin 0 auto
- padding 32px
- max-width 400px
- text-align center
- color #999
-
- > [data-icon]
- display block
- margin-bottom 16px
- font-size 3em
- color #ccc
+<style lang="stylus" scoped>
+.wqraeznr
+ padding 0 8px
+ z-index 10
+ background var(--faceHeader)
+ box-shadow 0 var(--lineWidth) var(--desktopTimelineHeaderShadow)
+ > span
+ padding 0 8px
+ font-size 0.9em
+ line-height 42px
+ color var(--text)
</style>
diff --git a/src/client/app/desktop/views/home/timeline.core.vue b/src/client/app/desktop/views/home/timeline.core.vue
index 704ca48ae4..efffc0b4de 100644
--- a/src/client/app/desktop/views/home/timeline.core.vue
+++ b/src/client/app/desktop/views/home/timeline.core.vue
@@ -5,8 +5,11 @@
<router-link to="/explore">{{ $t('@.empty-timeline-info.explore') }}</router-link>
</div>
- <mk-notes ref="timeline" :more="existMore ? more : null">
- <p :class="$style.empty" slot="empty">
+ <mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')">
+ <template slot="header">
+ <slot></slot>
+ </template>
+ <p slot="empty">
<fa :icon="['far', 'comments']"/>{{ $t('empty') }}
</p>
</mk-notes>
@@ -21,6 +24,7 @@ const fetchLimit = 10;
export default Vue.extend({
i18n: i18n('desktop/views/components/timeline.core.vue'),
+
props: {
src: {
type: String,
@@ -33,9 +37,6 @@ export default Vue.extend({
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
connection: null,
date: null,
baseQuery: {
@@ -44,21 +45,18 @@ export default Vue.extend({
includeLocalRenotes: this.$store.state.settings.showLocalRenotes
},
query: {},
- endpoint: null
+ endpoint: null,
+ makePromise: null
};
},
computed: {
alone(): boolean {
return this.$store.state.i.followingCount == 0;
- },
-
- canFetchMore(): boolean {
- return !this.moreFetching && !this.fetching && this.existMore;
}
},
- mounted() {
+ created() {
const prepend = note => {
(this.$refs.timeline as any).prepend(note);
};
@@ -109,7 +107,25 @@ export default Vue.extend({
this.connection.on('mention', onNote);
}
- this.fetch();
+ this.makePromise = cursor => this.$root.api(this.endpoint, {
+ limit: fetchLimit + 1,
+ untilDate: cursor ? undefined : (this.date ? this.date.getTime() : undefined),
+ untilId: cursor ? cursor : undefined,
+ ...this.baseQuery, ...this.query
+ }).then(notes => {
+ if (notes.length == fetchLimit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: notes[notes.length - 1].id
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ });
},
beforeDestroy() {
@@ -117,57 +133,8 @@ export default Vue.extend({
},
methods: {
- fetch() {
- this.fetching = true;
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api(this.endpoint, Object.assign({
- limit: fetchLimit + 1,
- untilDate: this.date ? this.date.getTime() : undefined
- }, this.baseQuery, this.query)).then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- this.$emit('loaded');
- }, rej);
- }));
- },
-
- more() {
- if (!this.canFetchMore) return;
-
- this.moreFetching = true;
-
- const promise = this.$root.api(this.endpoint, Object.assign({
- limit: fetchLimit + 1,
- untilId: (this.$refs.timeline as any).tail().id
- }, this.baseQuery, this.query));
-
- promise.then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
- },
-
focus() {
(this.$refs.timeline as any).focus();
- },
-
- warp(date) {
- this.date = date;
- this.fetch();
}
}
});
@@ -186,20 +153,3 @@ export default Vue.extend({
margin 0 0 8px 0
</style>
-
-<style lang="stylus" module>
-.empty
- display block
- margin 0 auto
- padding 32px
- max-width 400px
- text-align center
- color #999
-
- > [data-icon]
- display block
- margin-bottom 16px
- font-size 3em
- color #ccc
-
-</style>
diff --git a/src/client/app/desktop/views/home/timeline.vue b/src/client/app/desktop/views/home/timeline.vue
index 2f42b9723f..573cc95a9e 100644
--- a/src/client/app/desktop/views/home/timeline.vue
+++ b/src/client/app/desktop/views/home/timeline.vue
@@ -1,29 +1,23 @@
<template>
-<div class="mk-timeline">
+<div class="pwbzawku">
<mk-post-form class="form" v-if="$store.state.settings.showPostFormOnTopOfTl"/>
<div class="main">
- <header>
- <span :data-active="src == 'home'" @click="src = 'home'"><fa icon="home"/> {{ $t('home') }}</span>
- <span :data-active="src == 'local'" @click="src = 'local'" v-if="enableLocalTimeline"><fa :icon="['far', 'comments']"/> {{ $t('local') }}</span>
- <span :data-active="src == 'hybrid'" @click="src = 'hybrid'" v-if="enableLocalTimeline"><fa icon="share-alt"/> {{ $t('hybrid') }}</span>
- <span :data-active="src == 'global'" @click="src = 'global'" v-if="enableGlobalTimeline"><fa icon="globe"/> {{ $t('global') }}</span>
- <span :data-active="src == 'tag'" @click="src = 'tag'" v-if="tagTl"><fa icon="hashtag"/> {{ tagTl.title }}</span>
- <span :data-active="src == 'list'" @click="src = 'list'" v-if="list"><fa icon="list"/> {{ list.title }}</span>
- <div class="buttons">
- <button :data-active="src == 'mentions'" @click="src = 'mentions'" :title="$t('mentions')"><fa icon="at"/><i class="badge" v-if="$store.state.i.hasUnreadMentions"><fa icon="circle"/></i></button>
- <button :data-active="src == 'messages'" @click="src = 'messages'" :title="$t('messages')"><fa :icon="['far', 'envelope']"/><i class="badge" v-if="$store.state.i.hasUnreadSpecifiedNotes"><fa icon="circle"/></i></button>
- <button @click="chooseTag" :title="$t('hashtag')" ref="tagButton"><fa icon="hashtag"/></button>
- <button @click="chooseList" :title="$t('list')" ref="listButton"><fa icon="list"/></button>
- </div>
- </header>
- <x-core v-if="src == 'home'" ref="tl" key="home" src="home"/>
- <x-core v-if="src == 'local'" ref="tl" key="local" src="local"/>
- <x-core v-if="src == 'hybrid'" ref="tl" key="hybrid" src="hybrid"/>
- <x-core v-if="src == 'global'" ref="tl" key="global" src="global"/>
- <x-core v-if="src == 'mentions'" ref="tl" key="mentions" src="mentions"/>
- <x-core v-if="src == 'messages'" ref="tl" key="messages" src="messages"/>
- <x-core v-if="src == 'tag'" ref="tl" key="tag" src="tag" :tag-tl="tagTl"/>
- <mk-user-list-timeline v-if="src == 'list'" ref="tl" :key="list.id" :list="list"/>
+ <component :is="src == 'list' ? 'mk-user-list-timeline' : 'x-core'" ref="tl" v-bind="options">
+ <header class="zahtxcqi">
+ <span :data-active="src == 'home'" @click="src = 'home'"><fa icon="home"/> {{ $t('home') }}</span>
+ <span :data-active="src == 'local'" @click="src = 'local'" v-if="enableLocalTimeline"><fa :icon="['far', 'comments']"/> {{ $t('local') }}</span>
+ <span :data-active="src == 'hybrid'" @click="src = 'hybrid'" v-if="enableLocalTimeline"><fa icon="share-alt"/> {{ $t('hybrid') }}</span>
+ <span :data-active="src == 'global'" @click="src = 'global'" v-if="enableGlobalTimeline"><fa icon="globe"/> {{ $t('global') }}</span>
+ <span :data-active="src == 'tag'" @click="src = 'tag'" v-if="tagTl"><fa icon="hashtag"/> {{ tagTl.title }}</span>
+ <span :data-active="src == 'list'" @click="src = 'list'" v-if="list"><fa icon="list"/> {{ list.title }}</span>
+ <div class="buttons">
+ <button :data-active="src == 'mentions'" @click="src = 'mentions'" :title="$t('mentions')"><fa icon="at"/><i class="badge" v-if="$store.state.i.hasUnreadMentions"><fa icon="circle"/></i></button>
+ <button :data-active="src == 'messages'" @click="src = 'messages'" :title="$t('messages')"><fa :icon="['far', 'envelope']"/><i class="badge" v-if="$store.state.i.hasUnreadSpecifiedNotes"><fa icon="circle"/></i></button>
+ <button @click="chooseTag" :title="$t('hashtag')" ref="tagButton"><fa icon="hashtag"/></button>
+ <button @click="chooseList" :title="$t('list')" ref="listButton"><fa icon="list"/></button>
+ </div>
+ </header>
+ </component>
</div>
</div>
</template>
@@ -51,6 +45,16 @@ export default Vue.extend({
};
},
+ computed: {
+ options(): any {
+ return {
+ ...(this.src == 'list' ? { list: this.list } : { src: this.src }),
+ ...(this.src == 'tag' ? { tagTl: this.tagTl } : {}),
+ key: this.src == 'list' ? this.list.id : this.src
+ }
+ }
+ },
+
watch: {
src() {
this.saveSrc();
@@ -186,88 +190,82 @@ export default Vue.extend({
</script>
<style lang="stylus" scoped>
-.mk-timeline
+.pwbzawku
> .form
margin-bottom 16px
box-shadow var(--shadow)
border-radius var(--round)
- > .main
- background var(--face)
- box-shadow var(--shadow)
- border-radius var(--round)
- overflow hidden
-
- > header
- padding 0 8px
- z-index 10
- background var(--faceHeader)
- box-shadow 0 var(--lineWidth) var(--desktopTimelineHeaderShadow)
-
- > .buttons
- position absolute
- z-index 2
- top 0
- right 0
- padding-right 8px
+ .zahtxcqi
+ padding 0 8px
+ z-index 10
+ background var(--faceHeader)
+ box-shadow 0 var(--lineWidth) var(--desktopTimelineHeaderShadow)
- > button
- padding 0 8px
- font-size 0.9em
- line-height 42px
- color var(--faceTextButton)
+ > .buttons
+ position absolute
+ z-index 2
+ top 0
+ right 0
+ padding-right 8px
- > .badge
- position absolute
- top -4px
- right 4px
- font-size 10px
- color var(--notificationIndicator)
-
- &:hover
- color var(--faceTextButtonHover)
-
- &[data-active]
- color var(--primary)
- cursor default
+ > button
+ padding 0 8px
+ font-size 0.9em
+ line-height 42px
+ color var(--faceTextButton)
- &:before
- content ""
- display block
- position absolute
- bottom 0
- left 0
- width 100%
- height 2px
- background var(--primary)
+ > .badge
+ position absolute
+ top -4px
+ right 4px
+ font-size 10px
+ color var(--notificationIndicator)
- > span
- display inline-block
- padding 0 10px
- line-height 42px
- font-size 12px
- user-select none
+ &:hover
+ color var(--faceTextButtonHover)
&[data-active]
color var(--primary)
cursor default
- font-weight bold
&:before
content ""
display block
position absolute
bottom 0
- left -8px
- width calc(100% + 16px)
+ left 0
+ width 100%
height 2px
background var(--primary)
- &:not([data-active])
- color var(--desktopTimelineSrc)
- cursor pointer
+ > span
+ display inline-block
+ padding 0 10px
+ line-height 42px
+ font-size 12px
+ user-select none
+
+ &[data-active]
+ color var(--primary)
+ cursor default
+ font-weight bold
+
+ &:before
+ content ""
+ display block
+ position absolute
+ bottom 0
+ left -8px
+ width calc(100% + 16px)
+ height 2px
+ background var(--primary)
+
+ &:not([data-active])
+ color var(--desktopTimelineSrc)
+ cursor pointer
- &:hover
- color var(--desktopTimelineSrcHover)
+ &:hover
+ color var(--desktopTimelineSrcHover)
</style>
diff --git a/src/client/app/desktop/views/home/user/user.home.vue b/src/client/app/desktop/views/home/user/user.home.vue
index 3a999b5739..65aa5e1c8a 100644
--- a/src/client/app/desktop/views/home/user/user.home.vue
+++ b/src/client/app/desktop/views/home/user/user.home.vue
@@ -10,7 +10,7 @@
</ui-container>
</div>
<x-photos :user="user"/>
- <x-timeline class="timeline" ref="tl" :user="user"/>
+ <x-timeline ref="tl" :user="user"/>
</div>
</template>
@@ -51,7 +51,4 @@ export default Vue.extend({
> *
margin-bottom 16px
- > .timeline
- box-shadow var(--shadow)
-
</style>
diff --git a/src/client/app/desktop/views/home/user/user.timeline.vue b/src/client/app/desktop/views/home/user/user.timeline.vue
index 0571ce76f1..edbced8170 100644
--- a/src/client/app/desktop/views/home/user/user.timeline.vue
+++ b/src/client/app/desktop/views/home/user/user.timeline.vue
@@ -1,12 +1,12 @@
<template>
-<div class="oh5y2r7l5lx8j6jj791ykeiwgihheguk">
- <header>
- <span :data-active="mode == 'default'" @click="mode = 'default'"><fa :icon="['far', 'comment-alt']"/> {{ $t('default') }}</span>
- <span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'"><fa icon="comments"/> {{ $t('with-replies') }}</span>
- <span :data-active="mode == 'with-media'" @click="mode = 'with-media'"><fa :icon="['far', 'images']"/> {{ $t('with-media') }}</span>
- <span :data-active="mode == 'my-posts'" @click="mode = 'my-posts'"><fa icon="user"/> {{ $t('my-posts') }}</span>
- </header>
- <mk-notes ref="timeline" :more="existMore ? more : null">
+<div>
+ <mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')">
+ <header slot="header" class="oh5y2r7l5lx8j6jj791ykeiwgihheguk">
+ <span :data-active="mode == 'default'" @click="mode = 'default'"><fa :icon="['far', 'comment-alt']"/> {{ $t('default') }}</span>
+ <span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'"><fa icon="comments"/> {{ $t('with-replies') }}</span>
+ <span :data-active="mode == 'with-media'" @click="mode = 'with-media'"><fa :icon="['far', 'images']"/> {{ $t('with-media') }}</span>
+ <span :data-active="mode == 'my-posts'" @click="mode = 'my-posts'"><fa icon="user"/> {{ $t('my-posts') }}</span>
+ </header>
<p class="empty" slot="empty"><fa :icon="['far', 'comments']"/>{{ $t('empty') }}</p>
</mk-notes>
</div>
@@ -20,29 +20,47 @@ const fetchLimit = 10;
export default Vue.extend({
i18n: i18n('desktop/views/pages/user/user.timeline.vue'),
+
props: ['user'],
data() {
return {
fetching: true,
- moreFetching: false,
- existMore: false,
mode: 'default',
unreadCount: 0,
- date: null
+ date: null,
+ makePromise: cursor => this.$root.api('users/notes', {
+ userId: this.user.id,
+ limit: fetchLimit + 1,
+ includeReplies: this.mode == 'with-replies',
+ includeMyRenotes: this.mode != 'my-posts',
+ withFiles: this.mode == 'with-media',
+ untilId: cursor ? cursor : undefined
+ }).then(notes => {
+ if (notes.length == fetchLimit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: notes[notes.length - 1].id
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ })
};
},
watch: {
mode() {
- this.fetch();
+ (this.$refs.timeline as any).reload();
}
},
mounted() {
document.addEventListener('keydown', this.onDocumentKeydown);
-
- this.fetch(() => this.$emit('loaded'));
},
beforeDestroy() {
@@ -58,58 +76,9 @@ export default Vue.extend({
}
},
- fetch(cb?) {
- this.fetching = true;
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('users/notes', {
- userId: this.user.id,
- limit: fetchLimit + 1,
- untilDate: this.date ? this.date.getTime() : new Date().getTime() + 1000 * 86400 * 365,
- includeReplies: this.mode == 'with-replies',
- includeMyRenotes: this.mode != 'my-posts',
- withFiles: this.mode == 'with-media'
- }).then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- if (cb) cb();
- }, rej);
- }));
- },
-
- more() {
- this.moreFetching = true;
-
- const promise = this.$root.api('users/notes', {
- userId: this.user.id,
- limit: fetchLimit + 1,
- includeReplies: this.mode == 'with-replies',
- includeMyRenotes: this.mode != 'my-posts',
- withFiles: this.mode == 'with-media',
- untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime()
- });
-
- promise.then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
- },
-
warp(date) {
this.date = date;
- this.fetch();
+ (this.$refs.timeline as any).reload();
}
}
});
@@ -117,59 +86,38 @@ export default Vue.extend({
<style lang="stylus" scoped>
.oh5y2r7l5lx8j6jj791ykeiwgihheguk
- background var(--face)
- border-radius var(--round)
- overflow hidden
-
- > header
- padding 0 8px
- z-index 10
- background var(--faceHeader)
- box-shadow 0 1px var(--desktopTimelineHeaderShadow)
-
- > span
- display inline-block
- padding 0 10px
- line-height 42px
- font-size 12px
- user-select none
+ padding 0 8px
+ z-index 10
+ background var(--faceHeader)
+ box-shadow 0 1px var(--desktopTimelineHeaderShadow)
- &[data-active]
- color var(--primary)
- cursor default
- font-weight bold
+ > span
+ display inline-block
+ padding 0 10px
+ line-height 42px
+ font-size 12px
+ user-select none
- &:before
- content ""
- display block
- position absolute
- bottom 0
- left -8px
- width calc(100% + 16px)
- height 2px
- background var(--primary)
+ &[data-active]
+ color var(--primary)
+ cursor default
+ font-weight bold
- &:not([data-active])
- color var(--desktopTimelineSrc)
- cursor pointer
-
- &:hover
- color var(--desktopTimelineSrcHover)
-
- > .mk-notes
+ &:before
+ content ""
+ display block
+ position absolute
+ bottom 0
+ left -8px
+ width calc(100% + 16px)
+ height 2px
+ background var(--primary)
- > .empty
- display block
- margin 0 auto
- padding 32px
- max-width 400px
- text-align center
- color var(--text)
+ &:not([data-active])
+ color var(--desktopTimelineSrc)
+ cursor pointer
- > [data-icon]
- display block
- margin-bottom 16px
- font-size 3em
- color var(--faceHeaderText);
+ &:hover
+ color var(--desktopTimelineSrcHover)
</style>
diff --git a/src/client/app/mobile/style.styl b/src/client/app/mobile/style.styl
index 095e5266fd..3a4fc9c0c6 100644
--- a/src/client/app/mobile/style.styl
+++ b/src/client/app/mobile/style.styl
@@ -9,3 +9,15 @@
html
height 100%
background var(--bg)
+
+main
+ width 100%
+ max-width 680px
+ margin 0 auto
+ padding 8px
+
+ @media (min-width 500px)
+ padding 16px
+
+ @media (min-width 600px)
+ padding 32px
diff --git a/src/client/app/mobile/views/components/notes.vue b/src/client/app/mobile/views/components/notes.vue
index 1d0375cfa9..9b4e7a3895 100644
--- a/src/client/app/mobile/views/components/notes.vue
+++ b/src/client/app/mobile/views/components/notes.vue
@@ -1,8 +1,8 @@
<template>
-<div class="mk-notes">
- <slot name="head"></slot>
+<div class="ivaojijs">
+ <slot name="empty" v-if="notes.length == 0 && !fetching && inited"></slot>
- <slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
+ <mk-error v-if="!fetching && !inited" @retry="init()"/>
<div class="placeholder" v-if="fetching">
<template v-for="i in 10">
@@ -10,8 +10,6 @@
</template>
</div>
- <mk-error v-if="!fetching && requestInitPromise != null" @retry="resolveInitPromise"/>
-
<!-- トランジションを有効にするとなぜかメモリリークする -->
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="transition" tag="div">
<template v-for="(note, i) in _notes">
@@ -23,8 +21,8 @@
</template>
</component>
- <footer v-if="more">
- <button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
+ <footer v-if="cursor != null">
+ <button @click="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<template v-if="!moreFetching">{{ $t('@.load-more') }}</template>
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>
</button>
@@ -41,20 +39,21 @@ const displayLimit = 30;
export default Vue.extend({
i18n: i18n(),
+
props: {
- more: {
- type: Function,
- required: false
+ makePromise: {
+ required: true
}
},
data() {
return {
- requestInitPromise: null as () => Promise<any[]>,
notes: [],
queue: [],
fetching: true,
- moreFetching: false
+ moreFetching: false,
+ inited: false,
+ cursor: null
};
},
@@ -80,6 +79,10 @@ export default Vue.extend({
}
},
+ created() {
+ this.init();
+ },
+
mounted() {
window.addEventListener('scroll', this.onScroll, { passive: true });
},
@@ -97,27 +100,41 @@ export default Vue.extend({
Vue.set((this as any).notes, i, note);
},
- init(promiseGenerator: () => Promise<any[]>) {
- this.requestInitPromise = promiseGenerator;
- this.resolveInitPromise();
- },
-
- resolveInitPromise() {
+ reload() {
this.queue = [];
this.notes = [];
- this.fetching = true;
-
- const promise = this.requestInitPromise();
+ this.init();
+ },
- promise.then(notes => {
- this.notes = notes;
- this.requestInitPromise = null;
+ init() {
+ this.fetching = true;
+ this.makePromise().then(x => {
+ if (Array.isArray(x)) {
+ this.notes = x;
+ } else {
+ this.notes = x.notes;
+ this.cursor = x.cursor;
+ }
+ this.inited = true;
this.fetching = false;
+ this.$emit('inited');
}, e => {
this.fetching = false;
});
},
+ more() {
+ if (this.cursor == null || this.moreFetching) return;
+ this.moreFetching = true;
+ this.makePromise(this.cursor).then(x => {
+ this.notes = this.notes.concat(x.notes);
+ this.cursor = x.cursor;
+ this.moreFetching = false;
+ }, e => {
+ this.moreFetching = false;
+ });
+ },
+
prepend(note, silent = false) {
// 弾く
if (shouldMuteNote(this.$store.state.i, this.$store.state.settings, note)) return;
@@ -144,10 +161,6 @@ export default Vue.extend({
this.notes.push(note);
},
- tail() {
- return this.notes[this.notes.length - 1];
- },
-
releaseQueue() {
for (const n of this.queue) {
this.prepend(n, true);
@@ -155,15 +168,6 @@ export default Vue.extend({
this.queue = [];
},
- async loadMore() {
- if (this.more == null) return;
- if (this.moreFetching) return;
-
- this.moreFetching = true;
- await this.more();
- this.moreFetching = false;
- },
-
onScroll() {
if (this.isScrollTop()) {
this.releaseQueue();
@@ -176,7 +180,7 @@ export default Vue.extend({
if (this.$el.offsetHeight == 0) return;
const current = window.scrollY + window.innerHeight;
- if (current > document.body.offsetHeight - 8) this.loadMore();
+ if (current > document.body.offsetHeight - 8) this.more();
}
}
}
@@ -184,7 +188,7 @@ export default Vue.extend({
</script>
<style lang="stylus" scoped>
-.mk-notes
+.ivaojijs
overflow hidden
background var(--face)
border-radius 8px
diff --git a/src/client/app/mobile/views/components/user-list-timeline.vue b/src/client/app/mobile/views/components/user-list-timeline.vue
index d90051710b..e67d7931f7 100644
--- a/src/client/app/mobile/views/components/user-list-timeline.vue
+++ b/src/client/app/mobile/views/components/user-list-timeline.vue
@@ -1,6 +1,6 @@
<template>
<div>
- <mk-notes ref="timeline" :more="existMore ? more : null"/>
+ <mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
</div>
</template>
@@ -14,19 +14,31 @@ export default Vue.extend({
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
- connection: null
+ connection: null,
+ makePromise: cursor => this.$root.api('notes/user-list-timeline', {
+ listId: this.list.id,
+ limit: fetchLimit + 1,
+ untilId: cursor ? cursor : undefined,
+ includeMyRenotes: this.$store.state.settings.showMyRenotes,
+ includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+ includeLocalRenotes: this.$store.state.settings.showLocalRenotes
+ }).then(notes => {
+ if (notes.length == fetchLimit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: notes[notes.length - 1].id
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ })
};
},
- computed: {
- canFetchMore(): boolean {
- return !this.moreFetching && !this.fetching && this.existMore;
- }
- },
-
watch: {
$route: 'init'
},
@@ -48,59 +60,6 @@ export default Vue.extend({
this.connection.on('note', this.onNote);
this.connection.on('userAdded', this.onUserAdded);
this.connection.on('userRemoved', this.onUserRemoved);
-
- this.fetch();
- },
-
- fetch() {
- this.fetching = true;
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('notes/user-list-timeline', {
- listId: this.list.id,
- limit: fetchLimit + 1,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes
- }).then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- this.$emit('loaded');
- }, rej);
- }));
- },
-
- more() {
- if (!this.canFetchMore) return;
-
- this.moreFetching = true;
-
- const promise = this.$root.api('notes/user-list-timeline', {
- listId: this.list.id,
- limit: fetchLimit + 1,
- untilId: (this.$refs.timeline as any).tail().id,
- includeMyRenotes: this.$store.state.settings.showMyRenotes,
- includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
- includeLocalRenotes: this.$store.state.settings.showLocalRenotes
- });
-
- promise.then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
},
onNote(note) {
@@ -109,11 +68,11 @@ export default Vue.extend({
},
onUserAdded() {
- this.fetch();
+ (this.$refs.timeline as any).reload();
},
onUserRemoved() {
- this.fetch();
+ (this.$refs.timeline as any).reload();
}
}
});
diff --git a/src/client/app/mobile/views/components/user-timeline.vue b/src/client/app/mobile/views/components/user-timeline.vue
index 0d0bbc4073..e85a0d177c 100644
--- a/src/client/app/mobile/views/components/user-timeline.vue
+++ b/src/client/app/mobile/views/components/user-timeline.vue
@@ -1,6 +1,6 @@
<template>
<div class="mk-user-timeline">
- <mk-notes ref="timeline" :more="existMore ? more : null">
+ <mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')">
<div slot="empty">
<fa :icon="['far', 'comments']"/>
{{ withMedia ? this.$t('no-notes-with-media') : this.$t('no-notes') }}
@@ -17,73 +17,31 @@ const fetchLimit = 10;
export default Vue.extend({
i18n: i18n('mobile/views/components/user-timeline.vue'),
+
props: ['user', 'withMedia'],
data() {
return {
- fetching: true,
- existMore: false,
- moreFetching: false
- };
- },
-
- computed: {
- canFetchMore(): boolean {
- return !this.moreFetching && !this.fetching && this.existMore;
- }
- },
-
- mounted() {
- this.fetch();
- },
-
- methods: {
- fetch() {
- this.fetching = true;
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('users/notes', {
- userId: this.user.id,
- withFiles: this.withMedia,
- limit: fetchLimit + 1,
- untilDate: new Date().getTime() + 1000 * 86400 * 365
- }).then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- this.$emit('loaded');
- }, rej);
- }));
- },
-
- more() {
- if (!this.canFetchMore) return;
-
- this.moreFetching = true;
-
- const promise = this.$root.api('users/notes', {
+ makePromise: cursor => this.$root.api('users/notes', {
userId: this.user.id,
- withFiles: this.withMedia,
limit: fetchLimit + 1,
- untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime()
- });
-
- promise.then(notes => {
+ withFiles: this.withMedia,
+ untilId: cursor ? cursor : undefined
+ }).then(notes => {
if (notes.length == fetchLimit + 1) {
notes.pop();
+ return {
+ notes: notes,
+ cursor: notes[notes.length - 1].id
+ };
} else {
- this.existMore = false;
+ return {
+ notes: notes,
+ cursor: null
+ };
}
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
- }
+ })
+ };
}
});
</script>
diff --git a/src/client/app/mobile/views/pages/explore.vue b/src/client/app/mobile/views/pages/explore.vue
index 2955c9a50b..c861f2dfc4 100644
--- a/src/client/app/mobile/views/pages/explore.vue
+++ b/src/client/app/mobile/views/pages/explore.vue
@@ -26,18 +26,3 @@ export default Vue.extend({
},
});
</script>
-
-<style lang="stylus" scoped>
-main
- width 100%
- max-width 680px
- margin 0 auto
- padding 8px
-
- @media (min-width 500px)
- padding 16px
-
- @media (min-width 600px)
- padding 32px
-
-</style>
diff --git a/src/client/app/mobile/views/pages/favorites.vue b/src/client/app/mobile/views/pages/favorites.vue
index 61dd1526ba..9fcaf566e3 100644
--- a/src/client/app/mobile/views/pages/favorites.vue
+++ b/src/client/app/mobile/views/pages/favorites.vue
@@ -76,21 +76,11 @@ export default Vue.extend({
<style lang="stylus" scoped>
main
- width 100%
- max-width 680px
- margin 0 auto
- padding 8px
-
> * > .post
margin-bottom 8px
@media (min-width 500px)
- padding 16px
-
> * > .post
margin-bottom 16px
- @media (min-width 600px)
- padding 32px
-
</style>
diff --git a/src/client/app/mobile/views/pages/featured.vue b/src/client/app/mobile/views/pages/featured.vue
index 9122673be1..cab7b7243e 100644
--- a/src/client/app/mobile/views/pages/featured.vue
+++ b/src/client/app/mobile/views/pages/featured.vue
@@ -51,21 +51,11 @@ export default Vue.extend({
<style lang="stylus" scoped>
main
- width 100%
- max-width 680px
- margin 0 auto
- padding 8px
-
> * > .post
margin-bottom 8px
@media (min-width 500px)
- padding 16px
-
> * > .post
margin-bottom 16px
- @media (min-width 600px)
- padding 32px
-
</style>
diff --git a/src/client/app/mobile/views/pages/home.timeline.vue b/src/client/app/mobile/views/pages/home.timeline.vue
index b768a9ccc8..2fa155892e 100644
--- a/src/client/app/mobile/views/pages/home.timeline.vue
+++ b/src/client/app/mobile/views/pages/home.timeline.vue
@@ -7,7 +7,7 @@
</div>
</ui-container>
- <mk-notes ref="timeline" :more="existMore ? more : null">
+ <mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')">
<div slot="empty">
<fa :icon="['far', 'comments']"/>{{ $t('empty') }}
</div>
@@ -36,9 +36,6 @@ export default Vue.extend({
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
streamManager: null,
connection: null,
unreadCount: 0,
@@ -49,21 +46,18 @@ export default Vue.extend({
includeLocalRenotes: this.$store.state.settings.showLocalRenotes
},
query: {},
- endpoint: null
+ endpoint: null,
+ makePromise: null
};
},
computed: {
alone(): boolean {
return this.$store.state.i.followingCount == 0;
- },
-
- canFetchMore(): boolean {
- return !this.moreFetching && !this.fetching && this.existMore;
}
},
- mounted() {
+ created() {
const prepend = note => {
(this.$refs.timeline as any).prepend(note);
};
@@ -114,7 +108,25 @@ export default Vue.extend({
this.connection.on('mention', onNote);
}
- this.fetch();
+ this.makePromise = cursor => this.$root.api(this.endpoint, {
+ limit: fetchLimit + 1,
+ untilDate: cursor ? undefined : (this.date ? this.date.getTime() : undefined),
+ untilId: cursor ? cursor : undefined,
+ ...this.baseQuery, ...this.query
+ }).then(notes => {
+ if (notes.length == fetchLimit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: notes[notes.length - 1].id
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ });
},
beforeDestroy() {
@@ -122,57 +134,13 @@ export default Vue.extend({
},
methods: {
- fetch() {
- this.fetching = true;
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api(this.endpoint, Object.assign({
- limit: fetchLimit + 1,
- untilDate: this.date ? this.date.getTime() : undefined
- }, this.baseQuery, this.query)).then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- this.$emit('loaded');
- }, rej);
- }));
- },
-
- more() {
- if (!this.canFetchMore) return;
-
- this.moreFetching = true;
-
- const promise = this.$root.api(this.endpoint, Object.assign({
- limit: fetchLimit + 1,
- untilId: (this.$refs.timeline as any).tail().id
- }, this.baseQuery, this.query));
-
- promise.then(notes => {
- if (notes.length == fetchLimit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
- },
-
focus() {
(this.$refs.timeline as any).focus();
},
warp(date) {
this.date = date;
- this.fetch();
+ (this.$refs.timeline as any).reload();
}
}
});
diff --git a/src/client/app/mobile/views/pages/home.vue b/src/client/app/mobile/views/pages/home.vue
index a663c1da99..7f6a1b8238 100644
--- a/src/client/app/mobile/views/pages/home.vue
+++ b/src/client/app/mobile/views/pages/home.vue
@@ -233,17 +233,6 @@ main
font-size 10px
color var(--notificationIndicator)
- > .tl
- max-width 680px
- margin 0 auto
- padding 8px
-
- @media (min-width 500px)
- padding 16px
-
- @media (min-width 600px)
- padding 32px
-
</style>
<style lang="stylus" module>
diff --git a/src/client/app/mobile/views/pages/note.vue b/src/client/app/mobile/views/pages/note.vue
index 79757ea374..f22601a3f7 100644
--- a/src/client/app/mobile/views/pages/note.vue
+++ b/src/client/app/mobile/views/pages/note.vue
@@ -56,18 +56,6 @@ export default Vue.extend({
<style lang="stylus" scoped>
main
text-align center
- padding 8px
-
- @media (min-width 500px)
- padding 16px
-
- @media (min-width 600px)
- padding 32px
-
- > div
- margin 0 auto
- padding 0
- max-width 600px
> footer
margin-top 16px
diff --git a/src/client/app/mobile/views/pages/notifications.vue b/src/client/app/mobile/views/pages/notifications.vue
index c6e5b646f2..fd84a21c15 100644
--- a/src/client/app/mobile/views/pages/notifications.vue
+++ b/src/client/app/mobile/views/pages/notifications.vue
@@ -39,18 +39,3 @@ export default Vue.extend({
}
});
</script>
-
-<style lang="stylus" scoped>
-main
- width 100%
- max-width 680px
- margin 0 auto
- padding 8px
-
- @media (min-width 500px)
- padding 16px
-
- @media (min-width 600px)
- padding 32px
-
-</style>
diff --git a/src/client/app/mobile/views/pages/received-follow-requests.vue b/src/client/app/mobile/views/pages/received-follow-requests.vue
index 1b8323e834..df0cf109cd 100644
--- a/src/client/app/mobile/views/pages/received-follow-requests.vue
+++ b/src/client/app/mobile/views/pages/received-follow-requests.vue
@@ -57,17 +57,6 @@ export default Vue.extend({
<style lang="stylus" scoped>
main
- width 100%
- max-width 680px
- margin 0 auto
- padding 8px
-
- @media (min-width 500px)
- padding 16px
-
- @media (min-width 600px)
- padding 32px
-
> div
display flex
padding 16px
diff --git a/src/client/app/mobile/views/pages/search.vue b/src/client/app/mobile/views/pages/search.vue
index 669e0b740b..0bfc1c0384 100644
--- a/src/client/app/mobile/views/pages/search.vue
+++ b/src/client/app/mobile/views/pages/search.vue
@@ -3,8 +3,7 @@
<span slot="header"><fa icon="search"/> {{ q }}</span>
<main>
- <p :class="$style.empty" v-if="!fetching && empty"><fa icon="search"/> {{ $t('not-found', { q }) }}</p>
- <mk-notes ref="timeline" :more="existMore ? more : null"/>
+ <mk-notes ref="timeline" :make-promise="makePromise" @inited="inited"/>
</main>
</mk-ui>
</template>
@@ -20,15 +19,30 @@ export default Vue.extend({
i18n: i18n('mobile/views/pages/search.vue'),
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
- empty: false,
- offset: 0
+ makePromise: cursor => this.$root.api('notes/search', {
+ limit: limit + 1,
+ offset: cursor ? cursor : undefined,
+ query: this.q
+ }).then(notes => {
+ if (notes.length == limit + 1) {
+ notes.pop();
+ return {
+ notes: notes,
+ cursor: cursor ? cursor + limit : limit
+ };
+ } else {
+ return {
+ notes: notes,
+ cursor: null
+ };
+ }
+ })
};
},
watch: {
- $route: 'fetch'
+ $route() {
+ this.$refs.timeline.reload();
+ }
},
computed: {
q(): string {
@@ -37,68 +51,11 @@ export default Vue.extend({
},
mounted() {
document.title = `%i18n:@search%: ${this.q} | ${this.$root.instanceName}`;
-
- this.fetch();
},
methods: {
- fetch() {
- this.fetching = true;
- Progress.start();
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('notes/search', {
- limit: limit + 1,
- offset: this.offset,
- query: this.q
- }).then(notes => {
- if (notes.length == 0) this.empty = true;
- if (notes.length == limit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- Progress.done();
- }, rej);
- }));
+ inited() {
+ Progress.done();
},
- more() {
- this.offset += limit;
-
- const promise = this.$root.api('notes/search', {
- limit: limit + 1,
- offset: this.offset,
- query: this.q
- });
-
- promise.then(notes => {
- if (notes.length == limit + 1) {
- notes.pop();
- } else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
- }
- this.moreFetching = false;
- });
-
- return promise;
- }
}
});
</script>
-
-<style lang="stylus" module>
-.notes
- margin 8px auto
- max-width 500px
- width calc(100% - 16px)
- background #fff
- border-radius 8px
- box-shadow 0 0 0 1px rgba(#000, 0.2)
-
- @media (min-width 500px)
- margin 16px auto
- width calc(100% - 32px)
-</style>
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index f26b9af6f4..17f0c2f146 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -383,9 +383,6 @@ export default Vue.extend({
<style lang="stylus" scoped>
main
- margin 0 auto
- max-width 600px
- width 100%
> .signed-in-as
margin 16px
diff --git a/src/client/app/mobile/views/pages/tag.vue b/src/client/app/mobile/views/pages/tag.vue
index ecd523dab2..53129ed20b 100644
--- a/src/client/app/mobile/views/pages/tag.vue
+++ b/src/client/app/mobile/views/pages/tag.vue
@@ -3,8 +3,7 @@
<span slot="header"><span style="margin-right:4px;"><fa icon="hashtag"/></span>{{ $route.params.tag }}</span>
<main>
- <p v-if="!fetching && empty"><fa icon="search"/> {{ $t('no-posts-found', { q: $route.params.tag }) }}</p>
- <mk-notes ref="timeline" :more="existMore ? more : null"/>
+ <mk-notes ref="timeline" :make-promise="makePromise" @inited="inited"/>
</main>
</mk-ui>
</template>
@@ -20,66 +19,35 @@ export default Vue.extend({
i18n: i18n('mobile/views/pages/tag.vue'),
data() {
return {
- fetching: true,
- moreFetching: false,
- existMore: false,
- offset: 0,
- empty: false
- };
- },
- watch: {
- $route: 'fetch'
- },
- mounted() {
- this.$nextTick(() => {
- this.fetch();
- });
- },
- methods: {
- fetch() {
- this.fetching = true;
- Progress.start();
-
- (this.$refs.timeline as any).init(() => new Promise((res, rej) => {
- this.$root.api('notes/search_by_tag', {
- limit: limit + 1,
- offset: this.offset,
- tag: this.$route.params.tag
- }).then(notes => {
- if (notes.length == 0) this.empty = true;
- if (notes.length == limit + 1) {
- notes.pop();
- this.existMore = true;
- }
- res(notes);
- this.fetching = false;
- Progress.done();
- }, rej);
- }));
- },
- more() {
- this.offset += limit;
-
- const promise = this.$root.api('notes/search_by_tag', {
+ makePromise: cursor => this.$root.api('notes/search_by_tag', {
limit: limit + 1,
- offset: this.offset,
+ offset: cursor ? cursor : undefined,
tag: this.$route.params.tag
- });
-
- promise.then(notes => {
+ }).then(notes => {
if (notes.length == limit + 1) {
notes.pop();
+ return {
+ notes: notes,
+ cursor: cursor ? cursor + limit : limit
+ };
} else {
- this.existMore = false;
- }
- for (const n of notes) {
- (this.$refs.timeline as any).append(n);
+ return {
+ notes: notes,
+ cursor: null
+ };
}
- this.moreFetching = false;
- });
-
- return promise;
+ })
+ };
+ },
+ watch: {
+ $route() {
+ this.$refs.timeline.reload();
}
+ },
+ methods: {
+ inited() {
+ Progress.done();
+ },
}
});
</script>
diff --git a/src/client/app/mobile/views/pages/user-list.vue b/src/client/app/mobile/views/pages/user-list.vue
index cf2dd134fd..ad6e314767 100644
--- a/src/client/app/mobile/views/pages/user-list.vue
+++ b/src/client/app/mobile/views/pages/user-list.vue
@@ -46,18 +46,3 @@ export default Vue.extend({
}
});
</script>
-
-<style lang="stylus" scoped>
-main
- width 100%
- max-width 680px
- margin 0 auto
- padding 8px
-
- @media (min-width 500px)
- padding 16px
-
- @media (min-width 600px)
- padding 32px
-
-</style>
diff --git a/src/client/app/mobile/views/pages/user-lists.vue b/src/client/app/mobile/views/pages/user-lists.vue
index dc9d47de3c..530357576f 100644
--- a/src/client/app/mobile/views/pages/user-lists.vue
+++ b/src/client/app/mobile/views/pages/user-lists.vue
@@ -53,20 +53,3 @@ export default Vue.extend({
}
});
</script>
-
-<style lang="stylus" scoped>
-
-
-main
- width 100%
- max-width 680px
- margin 0 auto
- padding 8px
-
- @media (min-width 500px)
- padding 16px
-
- @media (min-width 600px)
- padding 32px
-
-</style>
diff --git a/src/client/app/mobile/views/pages/user/home.vue b/src/client/app/mobile/views/pages/user/home.vue
index 98b4f44476..f9b7e7f90a 100644
--- a/src/client/app/mobile/views/pages/user/home.vue
+++ b/src/client/app/mobile/views/pages/user/home.vue
@@ -57,7 +57,6 @@ export default Vue.extend({
<style lang="stylus" scoped>
.root.home
- max-width 600px
margin 0 auto
> .mk-note-detail
diff --git a/src/client/app/mobile/views/pages/user/index.vue b/src/client/app/mobile/views/pages/user/index.vue
index d7fb3d4d58..a2360e7417 100644
--- a/src/client/app/mobile/views/pages/user/index.vue
+++ b/src/client/app/mobile/views/pages/user/index.vue
@@ -3,7 +3,7 @@
<template slot="header" v-if="!fetching"><img :src="avator" alt="">
<mk-user-name :user="user"/>
</template>
- <main v-if="!fetching">
+ <div class="wwtwuxyh" v-if="!fetching">
<div class="is-suspended" v-if="user.isSuspended"><p><fa icon="exclamation-triangle"/> {{ $t('@.user-suspended') }}</p></div>
<div class="is-remote" v-if="user.host != null"><p><fa icon="exclamation-triangle"/> {{ $t('@.is-remote-user') }}<a :href="user.url || user.uri" target="_blank">{{ $t('@.view-on-remote') }}</a></p></div>
<header>
@@ -65,15 +65,15 @@
<a :data-active="page == 'media'" @click="page = 'media'"><fa icon="image"/> {{ $t('media') }}</a>
</div>
</nav>
- <div class="body">
+ <main>
<template v-if="$route.name == 'user'">
<x-home v-if="page == 'home'" :user="user"/>
<mk-user-timeline v-if="page == 'notes'" :user="user" key="tl"/>
<mk-user-timeline v-if="page == 'media'" :user="user" :with-media="true" key="media"/>
</template>
<router-view :user="user"></router-view>
- </div>
- </main>
+ </main>
+ </div>
</mk-ui>
</template>
@@ -146,7 +146,7 @@ export default Vue.extend({
</script>
<style lang="stylus" scoped>
-main
+.wwtwuxyh
$bg = var(--face)
> .is-suspended
@@ -314,7 +314,7 @@ main
display flex
justify-content center
margin 0 auto
- max-width 600px
+ max-width 616px
> a
display block
@@ -335,16 +335,4 @@ main
color var(--primary)
border-color var(--primary)
- > .body
- max-width 680px
- margin 0 auto
- padding 8px
- color var(--text)
-
- @media (min-width 500px)
- padding 16px
-
- @media (min-width 600px)
- padding 32px
-
</style>