diff --git a/src/client/app/common/scripts/note-subscriber.ts b/src/client/app/common/scripts/note-subscriber.ts index d881fe01ce..5b31a9f9d0 100644 --- a/src/client/app/common/scripts/note-subscriber.ts +++ b/src/client/app/common/scripts/note-subscriber.ts @@ -144,8 +144,6 @@ export default prop => ({ break; } } - - this.$emit(`update:${prop}`, this.$_ns_note_); }, } }); diff --git a/src/client/app/common/scripts/paging.ts b/src/client/app/common/scripts/paging.ts new file mode 100644 index 0000000000..458ee7e5ca --- /dev/null +++ b/src/client/app/common/scripts/paging.ts @@ -0,0 +1,169 @@ +import Vue from 'vue'; + +export default (opts) => ({ + data() { + return { + items: [], + queue: [], + fetching: true, + moreFetching: false, + inited: false, + more: false + }; + }, + + computed: { + empty(): boolean { + return this.items.length == 0 && !this.fetching && this.inited; + }, + + error(): boolean { + return !this.fetching && !this.inited; + } + }, + + watch: { + queue(x) { + if (opts.onQueueChanged) opts.onQueueChanged(this, x); + }, + + pagination() { + this.init(); + } + }, + + created() { + opts.displayLimit = opts.displayLimit || 30; + this.init(); + }, + + mounted() { + if (opts.captureWindowScroll) { + this.isScrollTop = () => { + return window.scrollY <= 8; + }; + + window.addEventListener('scroll', this.onWindowScroll, { passive: true }); + } + }, + + beforeDestroy() { + if (opts.captureWindowScroll) { + window.removeEventListener('scroll', this.onWindowScroll); + } + }, + + methods: { + updateItem(i, item) { + Vue.set((this as any).items, i, item); + }, + + reload() { + this.queue = []; + this.items = []; + this.init(); + }, + + async init() { + this.fetching = true; + let params = typeof this.pagination.params === 'function' ? this.pagination.params(true) : this.pagination.params; + if (params && params.then) params = await params; + await this.$root.api(this.pagination.endpoint, { + limit: (this.pagination.limit || 10) + 1, + ...params + }).then(x => { + if (x.length == (this.pagination.limit || 10) + 1) { + x.pop(); + this.items = x; + this.more = true; + } else { + this.items = x; + this.more = false; + } + this.inited = true; + this.fetching = false; + }, e => { + this.fetching = false; + }); + }, + + async fetchMore() { + if (!this.more || this.moreFetching || this.items.length === 0) return; + this.moreFetching = true; + let params = typeof this.pagination.params === 'function' ? this.pagination.params(false) : this.pagination.params; + if (params && params.then) params = await params; + await this.$root.api(this.pagination.endpoint, { + limit: (this.pagination.limit || 10) + 1, + untilId: this.items[this.items.length - 1].id, + ...params + }).then(x => { + if (x.length == (this.pagination.limit || 10) + 1) { + x.pop(); + this.items = this.items.concat(x); + this.more = true; + } else { + this.items = this.items.concat(x); + this.more = false; + } + this.moreFetching = false; + }, e => { + this.moreFetching = false; + }); + }, + + prepend(item, silent = false) { + if (opts.onPrepend) { + const cancel = opts.onPrepend(this, item, silent); + if (cancel) return; + } + + if (this.isScrollTop()) { + // Prepend the item + this.items.unshift(item); + + // オーバーフローしたら古い投稿は捨てる + if (this.items.length >= opts.displayLimit) { + this.items = this.items.slice(0, opts.displayLimit); + this.more = true; + } + } else { + this.queue.push(item); + } + }, + + append(item) { + this.items.push(item); + }, + + releaseQueue() { + for (const n of this.queue) { + this.prepend(n, true); + } + this.queue = []; + }, + + onWindowScroll() { + if (this.isScrollTop()) { + this.onTop(); + } + + if (this.$store.state.settings.fetchOnScroll) { + // 親要素が display none だったら弾く + // https://github.com/syuilo/misskey/issues/1569 + // http://d.hatena.ne.jp/favril/20091105/1257403319 + if (this.$el.offsetHeight == 0) return; + + const current = window.scrollY + window.innerHeight; + if (current > document.body.offsetHeight - 8) this.onBottom(); + } + }, + + onTop() { + this.releaseQueue(); + }, + + onBottom() { + this.fetchMore(); + } + } +}); diff --git a/src/client/app/common/views/components/user-list.vue b/src/client/app/common/views/components/user-list.vue index 6761886b09..d6cf750016 100644 --- a/src/client/app/common/views/components/user-list.vue +++ b/src/client/app/common/views/components/user-list.vue @@ -2,13 +2,13 @@ - +
-
+

{{ $t('no-users') }}

-
+
@@ -21,8 +21,8 @@
-
@@ -31,60 +31,31 @@ diff --git a/src/client/app/common/views/deck/deck.featured-column.vue b/src/client/app/common/views/deck/deck.featured-column.vue deleted file mode 100644 index d2c44e74ce..0000000000 --- a/src/client/app/common/views/deck/deck.featured-column.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - diff --git a/src/client/app/common/views/deck/deck.hashtag-tl.vue b/src/client/app/common/views/deck/deck.hashtag-tl.vue index 6f89f6a23d..94d2efc430 100644 --- a/src/client/app/common/views/deck/deck.hashtag-tl.vue +++ b/src/client/app/common/views/deck/deck.hashtag-tl.vue @@ -1,13 +1,11 @@ diff --git a/src/client/app/common/views/deck/deck.notification.vue b/src/client/app/common/views/deck/deck.notification.vue index 3ced7b7e23..8a21bedb91 100644 --- a/src/client/app/common/views/deck/deck.notification.vue +++ b/src/client/app/common/views/deck/deck.notification.vue @@ -81,15 +81,15 @@
@@ -105,17 +105,6 @@ export default Vue.extend({ getNoteSummary }; }, - methods: { - onNoteUpdated(note) { - switch (this.notification.type) { - case 'quote': - case 'reply': - case 'mention': - Vue.set(this.notification, 'note', note); - break; - } - } - } }); diff --git a/src/client/app/common/views/deck/deck.search-column.vue b/src/client/app/common/views/deck/deck.search-column.vue index 17ee2ef454..a2d1142fbe 100644 --- a/src/client/app/common/views/deck/deck.search-column.vue +++ b/src/client/app/common/views/deck/deck.search-column.vue @@ -5,7 +5,7 @@
- +
@@ -16,8 +16,6 @@ import XColumn from './deck.column.vue'; import XNotes from './deck.notes.vue'; import { genSearchQuery } from '../../../common/scripts/gen-search-query'; -const limit = 20; - export default Vue.extend({ components: { XColumn, @@ -26,24 +24,11 @@ export default Vue.extend({ data() { return { - makePromise: async cursor => this.$root.api('notes/search', { - limit: limit + 1, - offset: cursor ? cursor : undefined, - ...(await genSearchQuery(this, this.q)) - }).then(notes => { - if (notes.length == limit + 1) { - notes.pop(); - return { - notes: notes, - cursor: cursor ? cursor + limit : limit - }; - } else { - return { - notes: notes, - more: false - }; - } - }) + pagination: { + endpoint: 'notes/search', + limit: 20, + params: () => genSearchQuery(this, this.q) + } }; }, diff --git a/src/client/app/common/views/deck/deck.tl.vue b/src/client/app/common/views/deck/deck.tl.vue index 9284f06ee1..e6c716070a 100644 --- a/src/client/app/common/views/deck/deck.tl.vue +++ b/src/client/app/common/views/deck/deck.tl.vue @@ -6,7 +6,7 @@

{{ $t('disabled-timeline.description') }}

- + diff --git a/src/client/app/common/views/pages/featured.vue b/src/client/app/common/views/pages/featured.vue new file mode 100644 index 0000000000..42c97e09fc --- /dev/null +++ b/src/client/app/common/views/pages/featured.vue @@ -0,0 +1,44 @@ + + + diff --git a/src/client/app/common/views/pages/followers.vue b/src/client/app/common/views/pages/followers.vue index 1d68d71e80..b546e69ae3 100644 --- a/src/client/app/common/views/pages/followers.vue +++ b/src/client/app/common/views/pages/followers.vue @@ -1,7 +1,5 @@ + + diff --git a/src/client/app/desktop/views/components/notes.vue b/src/client/app/desktop/views/components/notes.vue index b051ff51cf..cb8b9c3ce3 100644 --- a/src/client/app/desktop/views/components/notes.vue +++ b/src/client/app/desktop/views/components/notes.vue @@ -4,9 +4,9 @@
-
{{ $t('@.no-notes') }}
+
{{ $t('@.no-notes') }}
- +