summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAya Morisawa <AyaMorisawa4869@gmail.com>2019-08-31 03:04:36 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2019-08-31 03:04:36 +0900
commit749200d22b4ff3d2a06cec37ebf2a5be67efc2c9 (patch)
tree6662607586c408208327c194a94e770bffea8e17 /src
parentUpdate dependencies :rocket: (diff)
downloadsharkey-749200d22b4ff3d2a06cec37ebf2a5be67efc2c9.tar.gz
sharkey-749200d22b4ff3d2a06cec37ebf2a5be67efc2c9.tar.bz2
sharkey-749200d22b4ff3d2a06cec37ebf2a5be67efc2c9.zip
Show users who sent reaction on hover (#5362)
* Show users who sent reaction on hover * Support i18n * detail -> details * Extract methods * Update on change
Diffstat (limited to 'src')
-rw-r--r--src/client/app/common/views/components/reactions-viewer.details.vue103
-rw-r--r--src/client/app/common/views/components/reactions-viewer.reaction.vue40
2 files changed, 143 insertions, 0 deletions
diff --git a/src/client/app/common/views/components/reactions-viewer.details.vue b/src/client/app/common/views/components/reactions-viewer.details.vue
new file mode 100644
index 0000000000..27eb860d5b
--- /dev/null
+++ b/src/client/app/common/views/components/reactions-viewer.details.vue
@@ -0,0 +1,103 @@
+<template>
+ <transition name="zoom-in-top">
+ <div class="buebdbiu" ref="popover" v-if="show">
+ <i18n path="few-users" v-if="users.length <= 10">
+ <span slot="users">{{ users.join(', ') }}</span>
+ <mk-reaction-icon slot="reaction" :reaction="reaction" ref="icon" />
+ </i18n>
+ <i18n path="many-users" v-if="10 < users.length">
+ <span slot="users">{{ users.slice(0, 10).join(', ') }}</span>
+ <span slot="ommited">{{ users.length - 10 }}</span>
+ <mk-reaction-icon slot="reaction" :reaction="reaction" ref="icon" />
+ </i18n>
+ </div>
+ </transition>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import i18n from '../../../i18n';
+
+export default Vue.extend({
+ i18n: i18n('common/views/components/reactions-viewer.details.vue'),
+ props: {
+ reaction: {
+ type: String,
+ required: true,
+ },
+ users: {
+ type: Array,
+ required: true,
+ },
+ source: {
+ required: true,
+ }
+ },
+ data() {
+ return {
+ show: false
+ };
+ },
+ mounted() {
+ this.show = true;
+
+ this.$nextTick(() => {
+ const popover = this.$refs.popover as any;
+
+ const rect = this.source.getBoundingClientRect();
+
+ const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
+ const y = rect.top + window.pageYOffset + this.source.offsetHeight;
+ popover.style.left = (x - 28) + 'px';
+ popover.style.top = (y + 16) + 'px';
+ });
+ }
+ methods: {
+ close() {
+ this.show = false;
+ setTimeout(this.destroyDom, 300);
+ }
+ }
+})
+</script>
+
+<style lang="stylus" scoped>
+.buebdbiu
+ $bgcolor = var(--popupBg)
+ z-index 10000
+ display block
+ position absolute
+ min-width max-content
+ max-width 240px
+ font-size 0.8em
+ padding 5px 8px
+ background $bgcolor
+ text-align center
+ color var(--text)
+ border-radius 4px
+ box-shadow 0 var(--lineWidth) 4px rgba(#000, 0.25)
+
+ &:before
+ content ""
+ pointer-events none
+ display block
+ position absolute
+ top -28px
+ left 12px
+ border-top solid 14px transparent
+ border-right solid 14px transparent
+ border-bottom solid 14px rgba(#000, 0.1)
+ border-left solid 14px transparent
+
+ &:after
+ content ""
+ pointer-events none
+ display block
+ position absolute
+ top -27px
+ left 12px
+ border-top solid 14px transparent
+ border-right solid 14px transparent
+ border-bottom solid 14px $bgcolor
+ border-left solid 14px transparent
+</style>
diff --git a/src/client/app/common/views/components/reactions-viewer.reaction.vue b/src/client/app/common/views/components/reactions-viewer.reaction.vue
index 02ee87b093..5bb9a9522e 100644
--- a/src/client/app/common/views/components/reactions-viewer.reaction.vue
+++ b/src/client/app/common/views/components/reactions-viewer.reaction.vue
@@ -5,6 +5,9 @@
@click="toggleReaction(reaction)"
v-if="count > 0"
v-particle="!isMe"
+ @mouseover="onMouseover"
+ @mouseleave="onMouseleave"
+ ref="reaction"
>
<mk-reaction-icon :reaction="reaction" ref="icon"/>
<span>{{ count }}</span>
@@ -15,6 +18,7 @@
import Vue from 'vue';
import Icon from './reaction-icon.vue';
import anime from 'animejs';
+import XDetails from './reactions-viewer.details.vue';
export default Vue.extend({
props: {
@@ -40,6 +44,12 @@ export default Vue.extend({
default: true,
},
},
+ data() {
+ return {
+ details: null,
+ detailsTimeoutId: null
+ };
+ },
computed: {
isMe(): boolean {
return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId;
@@ -51,6 +61,7 @@ export default Vue.extend({
watch: {
count(newCount, oldCount) {
if (oldCount < newCount) this.anime();
+ if (this.details != null) this.openDetails();
},
},
methods: {
@@ -77,6 +88,35 @@ export default Vue.extend({
});
}
},
+ onMouseover() {
+ this.detailsTimeoutId = setTimeout(this.openDetails, 300);
+ },
+ onMouseleave() {
+ clearTimeout(this.detailsTimeoutId);
+ this.closeDetails();
+ },
+ openDetails() {
+ this.$root.api('notes/reactions', {
+ noteId: this.note.id
+ }).then((reactions: any[]) => {
+ const users = reactions.filter(x => x.type === this.reaction)
+ .sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
+ .map(x => x.user.username);
+
+ this.closeDetails();
+ this.details = this.$root.new(XDetails, {
+ reaction: this.reaction,
+ users,
+ source: this.$refs.reaction
+ });
+ });
+ },
+ closeDetails() {
+ if (this.details != null) {
+ this.details.close();
+ this.details = null;
+ }
+ },
anime() {
if (this.$store.state.device.reduceMotion) return;
if (document.hidden) return;