summaryrefslogtreecommitdiff
path: root/src/client/app/desktop/views/pages/deck/deck.notes.vue
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-06-05 21:36:21 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-06-05 21:36:21 +0900
commit9ce0f96de3ba32e25893f6d248f35badaa522479 (patch)
tree37a0d9702b810bfe5eba6a044d1190c0fb102553 /src/client/app/desktop/views/pages/deck/deck.notes.vue
parentUpdate README.md (diff)
downloadmisskey-9ce0f96de3ba32e25893f6d248f35badaa522479.tar.gz
misskey-9ce0f96de3ba32e25893f6d248f35badaa522479.tar.bz2
misskey-9ce0f96de3ba32e25893f6d248f35badaa522479.zip
wip
Diffstat (limited to 'src/client/app/desktop/views/pages/deck/deck.notes.vue')
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.notes.vue248
1 files changed, 248 insertions, 0 deletions
diff --git a/src/client/app/desktop/views/pages/deck/deck.notes.vue b/src/client/app/desktop/views/pages/deck/deck.notes.vue
new file mode 100644
index 0000000000..ff871b049d
--- /dev/null
+++ b/src/client/app/desktop/views/pages/deck/deck.notes.vue
@@ -0,0 +1,248 @@
+<template>
+<div class="eamppglmnmimdhrlzhplwpvyeaqmmhxu">
+ <div class="newer-indicator" v-show="queue.length > 0"></div>
+
+ <slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
+
+ <div v-if="!fetching && requestInitPromise != null">
+ <p>%i18n:@error%</p>
+ <button @click="resolveInitPromise">%i18n:@retry%</button>
+ </div>
+
+ <transition-group name="mk-notes" class="transition">
+ <template v-for="(note, i) in _notes">
+ <x-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)"/>
+ <p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
+ <span>%fa:angle-up%{{ note._datetext }}</span>
+ <span>%fa:angle-down%{{ _notes[i + 1]._datetext }}</span>
+ </p>
+ </template>
+ </transition-group>
+
+ <footer v-if="more">
+ <button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
+ <template v-if="!moreFetching">%i18n:@load-more%</template>
+ <template v-if="moreFetching">%fa:spinner .pulse .fw%</template>
+ </button>
+ </footer>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import { url } from '../../../config';
+import getNoteSummary from '../../../../../renderers/get-note-summary';
+
+import XNote from './deck.note.vue';
+
+const displayLimit = 30;
+
+export default Vue.extend({
+ components: {
+ XNote
+ },
+
+ props: {
+ more: {
+ type: Function,
+ required: false
+ }
+ },
+
+ data() {
+ return {
+ rootEl: null,
+ requestInitPromise: null as () => Promise<any[]>,
+ notes: [],
+ queue: [],
+ unreadCount: 0,
+ fetching: true,
+ moreFetching: false
+ };
+ },
+
+ computed: {
+ _notes(): any[] {
+ return (this.notes as any).map(note => {
+ const date = new Date(note.createdAt).getDate();
+ const month = new Date(note.createdAt).getMonth() + 1;
+ note._date = date;
+ note._datetext = `${month}月 ${date}日`;
+ return note;
+ });
+ }
+ },
+
+ beforeDestroy() {
+ this.root.removeEventListener('scroll', this.onScroll);
+ },
+
+ methods: {
+ mount(root) {
+ this.rootEl = root;
+ this.rootEl.addEventListener('scroll', this.onScroll);
+ },
+
+ isScrollTop() {
+ if (this.rootEl == null) return true;
+ return this.rootEl.scrollTop <= 8;
+ },
+
+ focus() {
+ (this.$el as any).children[0].focus();
+ },
+
+ onNoteUpdated(i, note) {
+ Vue.set((this as any).notes, i, note);
+ },
+
+ init(promiseGenerator: () => Promise<any[]>) {
+ this.requestInitPromise = promiseGenerator;
+ this.resolveInitPromise();
+ },
+
+ resolveInitPromise() {
+ this.queue = [];
+ this.notes = [];
+ this.fetching = true;
+
+ const promise = this.requestInitPromise();
+
+ promise.then(notes => {
+ this.notes = notes;
+ this.requestInitPromise = null;
+ this.fetching = false;
+ }, e => {
+ this.fetching = false;
+ });
+ },
+
+ prepend(note, silent = false) {
+ //#region 弾く
+ const isMyNote = note.userId == this.$store.state.i.id;
+ const isPureRenote = note.renoteId != null && note.text == null && note.mediaIds.length == 0 && note.poll == null;
+
+ if (this.$store.state.settings.showMyRenotes === false) {
+ if (isMyNote && isPureRenote) {
+ return;
+ }
+ }
+
+ if (this.$store.state.settings.showRenotedMyNotes === false) {
+ if (isPureRenote && (note.renote.userId == this.$store.state.i.id)) {
+ return;
+ }
+ }
+ //#endregion
+
+ if (this.isScrollTop()) {
+ // Prepend the note
+ this.notes.unshift(note);
+
+ // オーバーフローしたら古い投稿は捨てる
+ if (this.notes.length >= displayLimit) {
+ this.notes = this.notes.slice(0, displayLimit);
+ }
+ } else {
+ this.queue.push(note);
+ }
+ },
+
+ append(note) {
+ this.notes.push(note);
+ },
+
+ tail() {
+ return this.notes[this.notes.length - 1];
+ },
+
+ releaseQueue() {
+ this.queue.forEach(n => this.prepend(n, true));
+ 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();
+ }
+
+ if (this.rootEl && this.$store.state.settings.fetchOnScroll !== false) {
+ const current = this.rootEl.scrollTop + this.rootEl.clientHeight;
+ if (current > this.rootEl.scrollHeight - 8) this.loadMore();
+ }
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+root(isDark)
+ .transition
+ .mk-notes-enter
+ .mk-notes-leave-to
+ opacity 0
+ transform translateY(-30px)
+
+ > *
+ transition transform .3s ease, opacity .3s ease
+
+ > .date
+ display block
+ margin 0
+ line-height 32px
+ font-size 14px
+ text-align center
+ color isDark ? #666b79 : #aaa
+ background isDark ? #242731 : #fdfdfd
+ border-bottom solid 1px isDark ? #1c2023 : #eaeaea
+
+ span
+ margin 0 16px
+
+ [data-fa]
+ margin-right 8px
+
+ > .newer-indicator
+ position -webkit-sticky
+ position sticky
+ z-index 100
+ height 3px
+ background $theme-color
+
+ > footer
+ > button
+ display block
+ margin 0
+ padding 16px
+ width 100%
+ text-align center
+ color #ccc
+ background isDark ? #282C37 : #fff
+ border-top solid 1px isDark ? #1c2023 : #eaeaea
+ border-bottom-left-radius 6px
+ border-bottom-right-radius 6px
+
+ &:hover
+ background isDark ? #2e3440 : #f5f5f5
+
+ &:active
+ background isDark ? #21242b : #eee
+
+.eamppglmnmimdhrlzhplwpvyeaqmmhxu[data-darkmode]
+ root(true)
+
+.eamppglmnmimdhrlzhplwpvyeaqmmhxu:not([data-darkmode])
+ root(false)
+
+</style>