summaryrefslogtreecommitdiff
path: root/src/client/scripts/paging.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/scripts/paging.ts')
-rw-r--r--src/client/scripts/paging.ts134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/client/scripts/paging.ts b/src/client/scripts/paging.ts
new file mode 100644
index 0000000000..b24d705f15
--- /dev/null
+++ b/src/client/scripts/paging.ts
@@ -0,0 +1,134 @@
+import Vue from 'vue';
+
+export default (opts) => ({
+ data() {
+ return {
+ items: [],
+ offset: 0,
+ 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: {
+ pagination() {
+ this.init();
+ }
+ },
+
+ created() {
+ opts.displayLimit = opts.displayLimit || 30;
+ this.init();
+ },
+
+ methods: {
+ isScrollTop() {
+ return window.scrollY <= 8;
+ },
+
+ updateItem(i, item) {
+ Vue.set((this as any).items, i, item);
+ },
+
+ reload() {
+ this.items = [];
+ this.init();
+ },
+
+ async init() {
+ this.fetching = true;
+ if (opts.before) opts.before(this);
+ let params = typeof this.pagination.params === 'function' ? this.pagination.params(true) : this.pagination.params;
+ if (params && params.then) params = await params;
+ const endpoint = typeof this.pagination.endpoint === 'function' ? this.pagination.endpoint() : this.pagination.endpoint;
+ await this.$root.api(endpoint, {
+ limit: this.pagination.noPaging ? (this.pagination.limit || 10) : (this.pagination.limit || 10) + 1,
+ ...params
+ }).then(x => {
+ if (!this.pagination.noPaging && (x.length === (this.pagination.limit || 10) + 1)) {
+ x.pop();
+ this.items = x;
+ this.more = true;
+ } else {
+ this.items = x;
+ this.more = false;
+ }
+ this.offset = x.length;
+ this.inited = true;
+ this.fetching = false;
+ if (opts.after) opts.after(this, null);
+ }, e => {
+ this.fetching = false;
+ if (opts.after) opts.after(this, e);
+ });
+ },
+
+ 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;
+ const endpoint = typeof this.pagination.endpoint === 'function' ? this.pagination.endpoint() : this.pagination.endpoint;
+ await this.$root.api(endpoint, {
+ limit: (this.pagination.limit || 10) + 1,
+ ...(this.pagination.offsetMode ? {
+ offset: this.offset,
+ } : {
+ 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.offset += x.length;
+ this.moreFetching = false;
+ }, e => {
+ this.moreFetching = false;
+ });
+ },
+
+ prepend(item, silent = false) {
+ if (opts.onPrepend) {
+ const cancel = opts.onPrepend(this, item, silent);
+ if (cancel) return;
+ }
+
+ // Prepend the item
+ this.items.unshift(item);
+
+ if (this.isScrollTop()) {
+ // オーバーフローしたら古い投稿は捨てる
+ if (this.items.length >= opts.displayLimit) {
+ this.items = this.items.slice(0, opts.displayLimit);
+ this.more = true;
+ }
+ }
+ },
+
+ append(item) {
+ this.items.push(item);
+ },
+
+ remove(find) {
+ this.items = this.items.filter(x => !find(x));
+ },
+ }
+});