diff options
| author | こぴなたみぽ <syuilotan@yahoo.co.jp> | 2018-02-13 15:17:59 +0900 |
|---|---|---|
| committer | こぴなたみぽ <syuilotan@yahoo.co.jp> | 2018-02-13 15:17:59 +0900 |
| commit | cffda0bcd213b74da3e6463f50896b32aeba5211 (patch) | |
| tree | e7b22aa4c98b36e6023e51193c1204dd4d5912ff /src/web/app/common/views | |
| parent | wip (diff) | |
| download | misskey-cffda0bcd213b74da3e6463f50896b32aeba5211.tar.gz misskey-cffda0bcd213b74da3e6463f50896b32aeba5211.tar.bz2 misskey-cffda0bcd213b74da3e6463f50896b32aeba5211.zip | |
wip
Diffstat (limited to 'src/web/app/common/views')
| -rw-r--r-- | src/web/app/common/views/components/messaging-room.vue | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/src/web/app/common/views/components/messaging-room.vue b/src/web/app/common/views/components/messaging-room.vue new file mode 100644 index 0000000000..2fb6671b8c --- /dev/null +++ b/src/web/app/common/views/components/messaging-room.vue @@ -0,0 +1,314 @@ +<template> +<div class="mk-messaging-room"> + <div class="stream"> + <p class="init" v-if="init">%fa:spinner .spin%%i18n:common.loading%</p> + <p class="empty" v-if="!init && messages.length == 0">%fa:info-circle%%i18n:common.tags.mk-messaging-room.empty%</p> + <p class="no-history" v-if="!init && messages.length > 0 && !moreMessagesIsInStock">%fa:flag%%i18n:common.tags.mk-messaging-room.no-history%</p> + <button class="more" :class="{ fetching: fetchingMoreMessages }" v-if="moreMessagesIsInStock" @click="fetchMoreMessages" :disabled="fetchingMoreMessages"> + <template v-if="fetchingMoreMessages">%fa:spinner .pulse .fw%</template>{{ fetchingMoreMessages ? '%i18n:common.loading%' : '%i18n:common.tags.mk-messaging-room.more%' }} + </button> + <template v-for="(message, i) in messages"> + <mk-messaging-message :message="message" :key="message.id"/> + <p class="date" :key="message.id + '-time'" v-if="i != messages.length - 1 && _message._date != _messages[i + 1]._date"><span>{{ _messages[i + 1]._datetext }}</span></p> + </template> + </div> + <footer> + <div ref="notifications"></div> + <div class="grippie" title="%i18n:common.tags.mk-messaging-room.resize-form%"></div> + <mk-messaging-form :user="user"/> + </footer> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import MessagingStreamConnection from '../../scripts/streaming/messaging-stream'; + +export default Vue.extend({ + props: ['user', 'isNaked'], + data() { + return { + init: true, + fetchingMoreMessages: false, + messages: [], + existMoreMessages: false, + connection: null + }; + }, + computed: { + _messages(): any[] { + return (this.messages as any).map(message => { + const date = new Date(message.created_at).getDate(); + const month = new Date(message.created_at).getMonth() + 1; + message._date = date; + message._datetext = `${month}月 ${date}日`; + return message; + }); + } + }, + + mounted() { + this.connection = new MessagingStreamConnection(this.$root.$data.os.i, this.user.id); + + this.connection.on('message', this.onMessage); + this.connection.on('read', this.onRead); + + document.addEventListener('visibilitychange', this.onVisibilitychange); + + this.fetchMessages().then(() => { + this.init = false; + this.scrollToBottom(); + }); + }, + beforeDestroy() { + this.connection.off('message', this.onMessage); + this.connection.off('read', this.onRead); + this.connection.close(); + + document.removeEventListener('visibilitychange', this.onVisibilitychange); + }, + methods: { + fetchMessages() { + return new Promise((resolve, reject) => { + const max = this.existMoreMessages ? 20 : 10; + + this.$root.$data.os.api('messaging/messages', { + user_id: this.user.id, + limit: max + 1, + until_id: this.existMoreMessages ? this.messages[0].id : undefined + }).then(messages => { + if (messages.length == max + 1) { + this.existMoreMessages = true; + messages.pop(); + } else { + this.existMoreMessages = false; + } + + this.messages.unshift.apply(this.messages, messages.reverse()); + resolve(); + }); + }); + }, + fetchMoreMessages() { + this.fetchingMoreMessages = true; + this.fetchMessages().then(() => { + this.fetchingMoreMessages = false; + }); + }, + onMessage(message) { + const isBottom = this.isBottom(); + + this.messages.push(message); + if (message.user_id != this.$root.$data.os.i.id && !document.hidden) { + this.connection.send({ + type: 'read', + id: message.id + }); + } + + if (isBottom) { + // Scroll to bottom + this.scrollToBottom(); + } else if (message.user_id != this.$root.$data.os.i.id) { + // Notify + this.notify('%i18n:common.tags.mk-messaging-room.new-message%'); + } + }, + onRead(ids) { + if (!Array.isArray(ids)) ids = [ids]; + ids.forEach(id => { + if (this.messages.some(x => x.id == id)) { + const exist = this.messages.map(x => x.id).indexOf(id); + this.messages[exist].is_read = true; + } + }); + }, + isBottom() { + const asobi = 32; + const current = this.isNaked + ? window.scrollY + window.innerHeight + : this.$el.scrollTop + this.$el.offsetHeight; + const max = this.isNaked + ? document.body.offsetHeight + : this.$el.scrollHeight; + return current > (max - asobi); + }, + scrollToBottom() { + if (this.isNaked) { + window.scroll(0, document.body.offsetHeight); + } else { + this.$el.scrollTop = this.$el.scrollHeight; + } + }, + notify(message) { + const n = document.createElement('p') as any; + n.innerHTML = '%fa:arrow-circle-down%' + message; + n.onclick = () => { + this.scrollToBottom(); + n.parentNode.removeChild(n); + }; + (this.$refs.notifications as any).appendChild(n); + + setTimeout(() => { + n.style.opacity = 0; + setTimeout(() => n.parentNode.removeChild(n), 1000); + }, 4000); + }, + onVisibilitychange() { + if (document.hidden) return; + this.messages.forEach(message => { + if (message.user_id !== this.$root.$data.os.i.id && !message.is_read) { + this.connection.send({ + type: 'read', + id: message.id + }); + } + }); + } + } +}); +</script> + +<style lang="stylus" scoped> +.mk-messaging-room + > .stream + max-width 600px + margin 0 auto + + > .init + width 100% + margin 0 + padding 16px 8px 8px 8px + text-align center + font-size 0.8em + color rgba(0, 0, 0, 0.4) + + [data-fa] + margin-right 4px + + > .empty + width 100% + margin 0 + padding 16px 8px 8px 8px + text-align center + font-size 0.8em + color rgba(0, 0, 0, 0.4) + + [data-fa] + margin-right 4px + + > .no-history + display block + margin 0 + padding 16px + text-align center + font-size 0.8em + color rgba(0, 0, 0, 0.4) + + [data-fa] + margin-right 4px + + > .more + display block + margin 16px auto + padding 0 12px + line-height 24px + color #fff + background rgba(0, 0, 0, 0.3) + border-radius 12px + + &:hover + background rgba(0, 0, 0, 0.4) + + &:active + background rgba(0, 0, 0, 0.5) + + &.fetching + cursor wait + + > [data-fa] + margin-right 4px + + > .message + // something + + > .date + display block + margin 8px 0 + text-align center + + &:before + content '' + display block + position absolute + height 1px + width 90% + top 16px + left 0 + right 0 + margin 0 auto + background rgba(0, 0, 0, 0.1) + + > span + display inline-block + margin 0 + padding 0 16px + //font-weight bold + line-height 32px + color rgba(0, 0, 0, 0.3) + background #fff + + > footer + position -webkit-sticky + position sticky + z-index 2 + bottom 0 + width 100% + max-width 600px + margin 0 auto + padding 0 + background rgba(255, 255, 255, 0.95) + background-clip content-box + + > [ref='notifications'] + position absolute + top -48px + width 100% + padding 8px 0 + text-align center + + &:empty + display none + + > p + display inline-block + margin 0 + padding 0 12px 0 28px + cursor pointer + line-height 32px + font-size 12px + color $theme-color-foreground + background $theme-color + border-radius 16px + transition opacity 1s ease + + > [data-fa] + position absolute + top 0 + left 10px + line-height 32px + font-size 16px + + > .grippie + height 10px + margin-top -10px + background transparent + cursor ns-resize + + &:hover + //background rgba(0, 0, 0, 0.1) + + &:active + //background rgba(0, 0, 0, 0.2) + +</style> |