From d0075026290c90d8406c7ac81413259a8ae58ec7 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Fri, 15 Nov 2019 08:39:21 +0200 Subject: add fetching for emoji reactions, draft design --- src/components/status/status.vue | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'src/components/status/status.vue') diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 65778b2e..aae58a5e 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -354,6 +354,17 @@ +
+ +
+
Date: Fri, 15 Nov 2019 16:29:25 +0200 Subject: wip commit, add basic popover for emoji reaction select --- src/components/react_button/react_button.js | 50 ++++++++++++++++++ src/components/react_button/react_button.vue | 78 ++++++++++++++++++++++++++++ src/components/status/status.js | 2 + src/components/status/status.vue | 10 ++-- src/i18n/en.json | 1 + 5 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 src/components/react_button/react_button.js create mode 100644 src/components/react_button/react_button.vue (limited to 'src/components/status/status.vue') diff --git a/src/components/react_button/react_button.js b/src/components/react_button/react_button.js new file mode 100644 index 00000000..d1d15d93 --- /dev/null +++ b/src/components/react_button/react_button.js @@ -0,0 +1,50 @@ +import { mapGetters } from 'vuex' + +const ReactButton = { + props: ['status', 'loggedIn'], + data () { + return { + animated: false, + showTooltip: false, + popperOptions: { + modifiers: { + preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' } + } + } + } + }, + methods: { + openReactionSelect () { + console.log('test') + this.showTooltip = true + }, + closeReactionSelect () { + this.showTooltip = false + }, + favorite () { + if (!this.status.favorited) { + this.$store.dispatch('favorite', { id: this.status.id }) + } else { + this.$store.dispatch('unfavorite', { id: this.status.id }) + } + this.animated = true + setTimeout(() => { + this.animated = false + }, 500) + } + }, + computed: { + emojis () { + return this.$store.state.instance.emoji || [] + }, + classes () { + return { + 'icon-smile': true, + 'animate-spin': this.animated + } + }, + ...mapGetters(['mergedConfig']) + } +} + +export default ReactButton diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue new file mode 100644 index 00000000..93638770 --- /dev/null +++ b/src/components/react_button/react_button.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/src/components/status/status.js b/src/components/status/status.js index 8268e615..8c6fc0cf 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -1,5 +1,6 @@ import Attachment from '../attachment/attachment.vue' import FavoriteButton from '../favorite_button/favorite_button.vue' +import ReactButton from '../react_button/react_button.vue' import RetweetButton from '../retweet_button/retweet_button.vue' import Poll from '../poll/poll.vue' import ExtraButtons from '../extra_buttons/extra_buttons.vue' @@ -289,6 +290,7 @@ const Status = { components: { Attachment, FavoriteButton, + ReactButton, RetweetButton, ExtraButtons, PostStatusForm, diff --git a/src/components/status/status.vue b/src/components/status/status.vue index aae58a5e..d455ccf6 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -356,12 +356,12 @@
@@ -393,6 +393,10 @@ :logged-in="loggedIn" :status="status" /> + Date: Mon, 13 Jan 2020 00:33:04 +0200 Subject: poll slot renamed, lightBg customization implemented --- src/App.scss | 4 ++++ src/components/autosuggest/autosuggest.vue | 4 ++-- src/components/dialog_modal/dialog_modal.vue | 12 +++++----- src/components/emoji_input/emoji_input.vue | 4 ++++ src/components/nav_panel/nav_panel.vue | 10 ++++++++ src/components/selectable_list/selectable_list.vue | 4 ++++ src/components/side_drawer/side_drawer.vue | 5 ++++ src/components/status/status.vue | 7 ++++-- src/components/style_switcher/style_switcher.vue | 27 ++++++++++++++++++++-- src/services/style_setter/style_setter.js | 4 +++- src/services/theme_data/theme_data.service.js | 26 +++++++++++++++++++++ 11 files changed, 94 insertions(+), 13 deletions(-) (limited to 'src/components/status/status.vue') diff --git a/src/App.scss b/src/App.scss index 7c9c91af..7da3688a 100644 --- a/src/App.scss +++ b/src/App.scss @@ -495,6 +495,10 @@ main-router { color: $fallback--faint; color: var(--panelFaint, $fallback--faint); } + .faint-link { + color: $fallback--faint; + color: var(--faintLink, $fallback--faint); + } .alert { white-space: nowrap; diff --git a/src/components/autosuggest/autosuggest.vue b/src/components/autosuggest/autosuggest.vue index 1f86e996..f283ab82 100644 --- a/src/components/autosuggest/autosuggest.vue +++ b/src/components/autosuggest/autosuggest.vue @@ -40,8 +40,8 @@ top: 100%; right: 0; max-height: 400px; - background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); + background-color: $fallback--bg; + background-color: var(--bg, $fallback--bg); border-style: solid; border-width: 1px; border-color: $fallback--border; diff --git a/src/components/dialog_modal/dialog_modal.vue b/src/components/dialog_modal/dialog_modal.vue index 55d7a7d2..3241ce3e 100644 --- a/src/components/dialog_modal/dialog_modal.vue +++ b/src/components/dialog_modal/dialog_modal.vue @@ -75,18 +75,18 @@ .dialog-modal-content { margin: 0; padding: 1rem 1rem; - background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); + background-color: $fallback--bg; + background-color: var(--bg, $fallback--bg); white-space: normal; } .dialog-modal-footer { margin: 0; padding: .5em .5em; - background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); - border-top: 1px solid $fallback--bg; - border-top: 1px solid var(--bg, $fallback--bg); + background-color: $fallback--bg; + background-color: var(--bg, $fallback--bg); + border-top: 1px solid $fallback--border; + border-top: 1px solid var(--border, $fallback--border); display: flex; justify-content: flex-end; diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue index a7215670..dcf51ff9 100644 --- a/src/components/emoji_input/emoji_input.vue +++ b/src/components/emoji_input/emoji_input.vue @@ -158,6 +158,10 @@ &.highlighted { background-color: $fallback--fg; background-color: var(--lightBg, $fallback--fg); + color: var(--lightBgText, $fallback--text); + --faint: var(--lightBgFaintText, $fallback--faint); + --faintLink: var(--lightBgFaintLink, $fallback--faint); + --icon: var(--lightBgIcon, $fallback--icon); } } } diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue index 034259d9..0886bf8c 100644 --- a/src/components/nav_panel/nav_panel.vue +++ b/src/components/nav_panel/nav_panel.vue @@ -101,12 +101,22 @@ &:hover { background-color: $fallback--lightBg; background-color: var(--lightBg, $fallback--lightBg); + color: $fallback--link; + color: var(--lightBgText, $fallback--link); + --faint: var(--lightBgFaintText, $fallback--faint); + --faintLink: var(--lightBgFaintLink, $fallback--faint); + --icon: var(--lightBgIcon, $fallback--icon); } &.router-link-active { font-weight: bolder; background-color: $fallback--lightBg; background-color: var(--lightBg, $fallback--lightBg); + color: $fallback--text; + color: var(--lightBgText, $fallback--text); + --faint: var(--lightBgFaintText, $fallback--faint); + --faintLink: var(--lightBgFaintLink, $fallback--faint); + --icon: var(--lightBgIcon, $fallback--icon); &:hover { text-decoration: underline; diff --git a/src/components/selectable_list/selectable_list.vue b/src/components/selectable_list/selectable_list.vue index d9ec7ece..416c9b6a 100644 --- a/src/components/selectable_list/selectable_list.vue +++ b/src/components/selectable_list/selectable_list.vue @@ -69,6 +69,10 @@ &-item-selected-inner { background-color: $fallback--lightBg; background-color: var(--lightBg, $fallback--lightBg); + color: var(--lightBgText, $fallback--text); + --faint: var(--lightBgFaintText, $fallback--faint); + --faintLink: var(--lightBgFaintLink, $fallback--faint); + --icon: var(--lightBgIcon, $fallback--icon); } &-header { diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue index 3fba9058..6d75221f 100644 --- a/src/components/side_drawer/side_drawer.vue +++ b/src/components/side_drawer/side_drawer.vue @@ -290,6 +290,11 @@ &:hover { background-color: $fallback--lightBg; background-color: var(--lightBg, $fallback--lightBg); + color: $fallback--text; + color: var(--lightBgText, $fallback--text); + --faint: var(--lightBgFaintText, $fallback--faint); + --faintLink: var(--lightBgFaintLink, $fallback--faint); + --icon: var(--lightBgIcon, $fallback--icon); } } } diff --git a/src/components/status/status.vue b/src/components/status/status.vue index d291e762..72e9b25a 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -446,6 +446,11 @@ $status-margin: 0.75em; &_focused { background-color: $fallback--lightBg; background-color: var(--lightBg, $fallback--lightBg); + color: $fallback--text; + color: var(--lightBgText, $fallback--text); + --faint: var(--lightBgFaintText, $fallback--faint); + --faintLink: var(--lightBgFaintLink, $fallback--faint); + --icon: var(--lightBgIcon, $fallback--icon); } .timeline & { @@ -573,8 +578,6 @@ $status-margin: 0.75em; overflow: hidden; text-overflow: ellipsis; margin: 0 0.4em 0 0.2em; - color: $fallback--faint; - color: var(--faint, $fallback--faint); } .replies-separator { diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index e0894b6d..1381f1fb 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -414,7 +414,7 @@ /> @@ -423,11 +423,34 @@

{{ $t('settings.style.advanced_colors.icons') }}

+
+

{{ $t('settings.style.advanced_colors.lightBg') }}

+ + + + + +
{ return } if (k === 'faint') { - colors[k + 'Link'].a = v + colors['faintLink'].a = v colors['panelFaint'].a = v + colors['lightBgFaintText'].a = v + colors['lightBgFaintLink'].a = v } if (k === 'bg') { colors['lightBg'].a = v diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js index 21bab1a2..808f67d5 100644 --- a/src/services/theme_data/theme_data.service.js +++ b/src/services/theme_data/theme_data.service.js @@ -12,6 +12,7 @@ export const LAYERS = { badge: null, // no transparency support fg: null, bg: 'underlay', + lightBg: 'bg', panel: 'bg', btn: 'bg', btnPanel: 'panel', @@ -57,6 +58,31 @@ export const SLOT_INHERITANCE = { depends: ['bg'], color: (mod, bg) => brightness(5 * mod, bg).rgb }, + lightBgFaintText: { + depends: ['faint'], + layer: 'lightBg', + textColor: true + }, + lightBgFaintLink: { + depends: ['faintLink'], + layer: 'lightBg', + textColor: 'preserve' + }, + lightBgText: { + depends: ['text'], + layer: 'lightBg', + textColor: true + }, + lightBgLink: { + depends: ['link'], + layer: 'lightBg', + textColor: 'preserve' + }, + lightBgIcon: { + depends: ['lightBg', 'lightBgText'], + color: (mod, bg, text) => mixrgb(bg, text) + }, + lightText: { depends: ['text'], color: (mod, text) => brightness(20 * mod, text).rgb -- cgit v1.2.3-70-g09d2 From 8a7f3fc16a511e6c5917c0e25cba1f6163659264 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 13 Jan 2020 20:40:16 +0200 Subject: selectedPost and selectedMenu support --- src/components/emoji_input/emoji_input.vue | 10 ++--- src/components/nav_panel/nav_panel.vue | 20 ++++----- src/components/selectable_list/selectable_list.vue | 10 ++--- src/components/side_drawer/side_drawer.vue | 10 ++--- src/components/status/status.vue | 10 ++--- src/components/style_switcher/style_switcher.vue | 46 +++++++++++++++++++ src/services/theme_data/theme_data.service.js | 52 ++++++++++++++++++++++ 7 files changed, 128 insertions(+), 30 deletions(-) (limited to 'src/components/status/status.vue') diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue index dcf51ff9..46ed6f25 100644 --- a/src/components/emoji_input/emoji_input.vue +++ b/src/components/emoji_input/emoji_input.vue @@ -157,11 +157,11 @@ &.highlighted { background-color: $fallback--fg; - background-color: var(--lightBg, $fallback--fg); - color: var(--lightBgText, $fallback--text); - --faint: var(--lightBgFaintText, $fallback--faint); - --faintLink: var(--lightBgFaintLink, $fallback--faint); - --icon: var(--lightBgIcon, $fallback--icon); + background-color: var(--selectedMenu, $fallback--fg); + color: var(--selectedMenuText, $fallback--text); + --faint: var(--selectedMenuFaintText, $fallback--faint); + --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --icon: var(--selectedMenuIcon, $fallback--icon); } } } diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue index 0886bf8c..afc611ea 100644 --- a/src/components/nav_panel/nav_panel.vue +++ b/src/components/nav_panel/nav_panel.vue @@ -100,23 +100,23 @@ &:hover { background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); + background-color: var(--selectedMenu, $fallback--lightBg); color: $fallback--link; - color: var(--lightBgText, $fallback--link); - --faint: var(--lightBgFaintText, $fallback--faint); - --faintLink: var(--lightBgFaintLink, $fallback--faint); - --icon: var(--lightBgIcon, $fallback--icon); + color: var(--selectedMenuText, $fallback--link); + --faint: var(--selectedMenuFaintText, $fallback--faint); + --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --icon: var(--selectedMenuIcon, $fallback--icon); } &.router-link-active { font-weight: bolder; background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); + background-color: var(--selectedMenu, $fallback--lightBg); color: $fallback--text; - color: var(--lightBgText, $fallback--text); - --faint: var(--lightBgFaintText, $fallback--faint); - --faintLink: var(--lightBgFaintLink, $fallback--faint); - --icon: var(--lightBgIcon, $fallback--icon); + color: var(--selectedMenuText, $fallback--text); + --faint: var(--selectedMenuFaintText, $fallback--faint); + --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --icon: var(--selectedMenuIcon, $fallback--icon); &:hover { text-decoration: underline; diff --git a/src/components/selectable_list/selectable_list.vue b/src/components/selectable_list/selectable_list.vue index 416c9b6a..2d1e44a3 100644 --- a/src/components/selectable_list/selectable_list.vue +++ b/src/components/selectable_list/selectable_list.vue @@ -68,11 +68,11 @@ &-item-selected-inner { background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); - color: var(--lightBgText, $fallback--text); - --faint: var(--lightBgFaintText, $fallback--faint); - --faintLink: var(--lightBgFaintLink, $fallback--faint); - --icon: var(--lightBgIcon, $fallback--icon); + background-color: var(--selectedMenu, $fallback--lightBg); + color: var(--selectedMenuText, $fallback--text); + --faint: var(--selectedMenuFaintText, $fallback--faint); + --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --icon: var(--selectedMenuIcon, $fallback--icon); } &-header { diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue index 6d75221f..3188fd59 100644 --- a/src/components/side_drawer/side_drawer.vue +++ b/src/components/side_drawer/side_drawer.vue @@ -289,12 +289,12 @@ &:hover { background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); + background-color: var(--selectedMenu, $fallback--lightBg); color: $fallback--text; - color: var(--lightBgText, $fallback--text); - --faint: var(--lightBgFaintText, $fallback--faint); - --faintLink: var(--lightBgFaintLink, $fallback--faint); - --icon: var(--lightBgIcon, $fallback--icon); + color: var(--selectedMenuText, $fallback--text); + --faint: var(--selectedMenuFaintText, $fallback--faint); + --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --icon: var(--selectedMenuIcon, $fallback--icon); } } } diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 72e9b25a..38d091ed 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -445,12 +445,12 @@ $status-margin: 0.75em; &_focused { background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); + background-color: var(--selectedPost, $fallback--lightBg); color: $fallback--text; - color: var(--lightBgText, $fallback--text); - --faint: var(--lightBgFaintText, $fallback--faint); - --faintLink: var(--lightBgFaintLink, $fallback--faint); - --icon: var(--lightBgIcon, $fallback--icon); + color: var(--selectedPostText, $fallback--text); + --faint: var(--selectedPostFaintText, $fallback--faint); + --faintLink: var(--selectedPostFaintLink, $fallback--faint); + --icon: var(--selectedPostIcon, $fallback--icon); } .timeline & { diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index f2fdfea2..613abd1f 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -465,6 +465,52 @@ />
+
+

{{ $t('settings.style.advanced_colors.selectedPost') }}

+ + + + + +
+
+

{{ $t('settings.style.advanced_colors.selectedMenu') }}

+ + + + + +
mixrgb(bg, text) }, + selectedPost: '--lightBg', + selectedPostFaintText: { + depends: ['lightBgFaintText'], + layer: 'lightBg', + textColor: true + }, + selectedPostFaintLink: { + depends: ['lightBgFaintLink'], + layer: 'lightBg', + textColor: 'preserve' + }, + selectedPostText: { + depends: ['lightBgText'], + layer: 'lightBg', + textColor: true + }, + selectedPostLink: { + depends: ['lightBgLink'], + layer: 'lightBg', + textColor: 'preserve' + }, + selectedPostIcon: { + depends: ['selectedPost', 'selectedPostText'], + color: (mod, bg, text) => mixrgb(bg, text) + }, + + selectedMenu: '--lightBg', + selectedMenuFaintText: { + depends: ['lightBgFaintText'], + layer: 'lightBg', + textColor: true + }, + selectedMenuFaintLink: { + depends: ['lightBgFaintLink'], + layer: 'lightBg', + textColor: 'preserve' + }, + selectedMenuText: { + depends: ['lightBgText'], + layer: 'lightBg', + textColor: true + }, + selectedMenuLink: { + depends: ['lightBgLink'], + layer: 'lightBg', + textColor: 'preserve' + }, + selectedMenuIcon: { + depends: ['selectedMenu', 'selectedMenuText'], + color: (mod, bg, text) => mixrgb(bg, text) + }, + lightText: { depends: ['text'], color: (mod, text) => brightness(20 * mod, text).rgb -- cgit v1.2.3-70-g09d2 From 33abbed5a1e1d1cf99d21d481b2a22481d7533b2 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Mon, 13 Jan 2020 23:34:39 +0200 Subject: usable-but-buggy: picker, adding/removing reaction on click, search, styles --- src/components/react_button/react_button.js | 25 +++++---- src/components/react_button/react_button.vue | 37 +++++++++++-- src/components/status/status.js | 21 ++++++-- src/components/status/status.vue | 24 +++++++-- src/modules/statuses.js | 61 ++++++++++++++++++++-- src/services/api/api.service.js | 22 ++++++++ .../backend_interactor_service.js | 4 ++ 7 files changed, 166 insertions(+), 28 deletions(-) (limited to 'src/components/status/status.vue') diff --git a/src/components/react_button/react_button.js b/src/components/react_button/react_button.js index d1d15d93..76a49305 100644 --- a/src/components/react_button/react_button.js +++ b/src/components/react_button/react_button.js @@ -6,6 +6,7 @@ const ReactButton = { return { animated: false, showTooltip: false, + filterWord: '', popperOptions: { modifiers: { preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' } @@ -14,27 +15,25 @@ const ReactButton = { } }, methods: { - openReactionSelect () { - console.log('test') - this.showTooltip = true + toggleReactionSelect () { + this.showTooltip = !this.showTooltip }, closeReactionSelect () { this.showTooltip = false }, - favorite () { - if (!this.status.favorited) { - this.$store.dispatch('favorite', { id: this.status.id }) - } else { - this.$store.dispatch('unfavorite', { id: this.status.id }) - } - this.animated = true - setTimeout(() => { - this.animated = false - }, 500) + addReaction (event, emoji) { + this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) + this.closeReactionSelect() } }, computed: { + commonEmojis () { + return ['💖', '😠', '👀', '😂', '🔥'] + }, emojis () { + if (this.filterWord !== '') { + return this.$store.state.instance.emoji.filter(emoji => emoji.displayText.includes(this.filterWord)) + } return this.$store.state.instance.emoji || [] }, classes () { diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue index 93638770..f7015316 100644 --- a/src/components/react_button/react_button.vue +++ b/src/components/react_button/react_button.vue @@ -5,21 +5,37 @@ trigger="manual" placement="top" class="react-button-popover" - @close-group="closeReactionSelect" + @hide="closeReactionSelect" >
+
+ +
+ + {{ emoji }} + +
{{ emoji.replacement }}
-
+
@import '../../_variables.scss'; +.reaction-picker-filter { + padding: 0.5em; +} + +.reaction-picker-divider { + height: 1px; + width: 100%; + margin: 0.4em; + background-color: var(--border, $fallback--border); +} + .reaction-picker { width: 10em; - height: 8em; + height: 9em; font-size: 1.5em; overflow-y: scroll; display: flex; flex-wrap: wrap; padding: 0.5em; - text-align:center; + text-align: center; + align-content: flex-start; + user-select: none; mask: linear-gradient(to top, white 0, transparent 100%) bottom no-repeat, linear-gradient(to bottom, white 0, transparent 100%) top no-repeat, diff --git a/src/components/status/status.js b/src/components/status/status.js index 8c6fc0cf..cc0c9e06 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -280,10 +280,7 @@ const Status = { return this.mergedConfig.hidePostStats }, emojiReactions () { - return { - '🤔': [{ 'id': 'xyz..' }, { 'id': 'zyx...' }], - '🐻': [{ 'id': 'abc...' }] - } + return this.status.emojiReactions }, ...mapGetters(['mergedConfig']) }, @@ -385,6 +382,22 @@ const Status = { setMedia () { const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments return () => this.$store.dispatch('setMedia', attachments) + }, + reactedWith (emoji) { + return this.status.reactedWithEmoji.includes(emoji) + }, + reactWith (emoji) { + this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) + }, + unreact (emoji) { + this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) + }, + emojiOnClick (emoji, event) { + if (this.reactedWith(emoji)) { + this.unreact(emoji) + } else { + this.reactWith(emoji) + } } }, watch: { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index d455ccf6..503de98d 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -354,13 +354,15 @@
-
+
@@ -788,19 +790,33 @@ $status-margin: 0.75em; .emoji-reactions { display: flex; - margin-top: 0.75em; + margin-top: 0.25em; + flex-wrap: wrap; } .emoji-reaction { padding: 0 0.5em; margin-right: 0.5em; + margin-top: 0.5em; display: flex; align-items: center; justify-content: center; - + box-sizing: border-box; :first-child { margin-right: 0.25em; } + :last-child { + width: 1.5em; + } + &:focus { + outline: none; + } +} + +.picked-reaction { + border: 1px solid var(--link, $fallback--link); + margin-left: -1px; // offset the border, can't use inset shadows either + margin-right: calc(0.5em - 1px); } .button-icon.icon-reply { diff --git a/src/modules/statuses.js b/src/modules/statuses.js index c285b452..fcb6d1f3 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,4 +1,20 @@ -import { remove, slice, each, findIndex, find, maxBy, minBy, merge, first, last, isArray, omitBy, findKey } from 'lodash' +import { + remove, + slice, + each, + findIndex, + find, + maxBy, + minBy, + merge, + first, + last, + isArray, + omitBy, + flow, + filter, + keys +} from 'lodash' import { set } from 'vue' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' @@ -512,8 +528,29 @@ export const mutations = { }, addEmojiReactions (state, { id, emojiReactions, currentUser }) { const status = state.allStatusesObject[id] - status.emojiReactions = emojiReactions - status.reactedWithEmoji = findKey(emojiReactions, { id: currentUser.id }) + set(status, 'emojiReactions', emojiReactions) + const reactedWithEmoji = flow(keys, filter(reaction => find(reaction, { id: currentUser.id })))(emojiReactions) + set(status, 'reactedWithEmoji', reactedWithEmoji) + }, + addOwnReaction (state, { id, emoji, currentUser }) { + const status = state.allStatusesObject[id] + status.emojiReactions = status.emojiReactions || {} + const listOfUsers = (status.emojiReactions && status.emojiReactions[emoji]) || [] + const hasSelfAlready = !!find(listOfUsers, { id: currentUser.id }) + if (!hasSelfAlready) { + set(status.emojiReactions, emoji, listOfUsers.concat([{ id: currentUser.id }])) + set(status, 'reactedWithEmoji', emoji) + } + }, + removeOwnReaction (state, { id, emoji, currentUser }) { + const status = state.allStatusesObject[id] + const listOfUsers = status.emojiReactions[emoji] || [] + const hasSelfAlready = !!find(listOfUsers, { id: currentUser.id }) + if (hasSelfAlready) { + const newUsers = filter(listOfUsers, user => user.id !== currentUser.id) + set(status.emojiReactions, emoji, newUsers) + set(status, 'reactedWith', emoji) + } }, updateStatusWithPoll (state, { id, poll }) { const status = state.allStatusesObject[id] @@ -616,6 +653,24 @@ const statuses = { commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser }) }) }, + reactWithEmoji ({ rootState, dispatch, commit }, { id, emoji }) { + const currentUser = rootState.users.currentUser + commit('addOwnReaction', { id, emoji, currentUser }) + rootState.api.backendInteractor.reactWithEmoji(id, emoji).then( + status => { + dispatch('fetchEmojiReactions', id) + } + ) + }, + unreactWithEmoji ({ rootState, dispatch, commit }, { id, emoji }) { + const currentUser = rootState.users.currentUser + commit('removeOwnReaction', { id, emoji, currentUser }) + rootState.api.backendInteractor.unreactWithEmoji(id, emoji).then( + status => { + dispatch('fetchEmojiReactions', id) + } + ) + }, fetchEmojiReactions ({ rootState, commit }, id) { rootState.api.backendInteractor.fetchEmojiReactions(id).then( emojiReactions => { diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 7ef4b74a..2e96264a 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -72,6 +72,8 @@ const MASTODON_UNMUTE_CONVERSATION = id => `/api/v1/statuses/${id}/unmute` const MASTODON_SEARCH_2 = `/api/v2/search` const MASTODON_USER_SEARCH_URL = '/api/v1/accounts/search' const PLEROMA_EMOJI_REACTIONS_URL = id => `/api/v1/pleroma/statuses/${id}/emoji_reactions_by` +const PLEROMA_EMOJI_REACT_URL = id => `/api/v1/pleroma/statuses/${id}/react_with_emoji` +const PLEROMA_EMOJI_UNREACT_URL = id => `/api/v1/pleroma/statuses/${id}/unreact_with_emoji` const oldfetch = window.fetch @@ -869,6 +871,24 @@ const fetchEmojiReactions = ({ id }) => { return promisedRequest({ url: PLEROMA_EMOJI_REACTIONS_URL(id) }) } +const reactWithEmoji = ({ id, emoji, credentials }) => { + return promisedRequest({ + url: PLEROMA_EMOJI_REACT_URL(id), + method: 'POST', + credentials, + payload: { emoji } + }).then(status => parseStatus(status)) +} + +const unreactWithEmoji = ({ id, emoji, credentials }) => { + return promisedRequest({ + url: PLEROMA_EMOJI_UNREACT_URL(id), + method: 'POST', + credentials, + payload: { emoji } + }).then(parseStatus) +} + const reportUser = ({ credentials, userId, statusIds, comment, forward }) => { return promisedRequest({ url: MASTODON_REPORT_USER_URL, @@ -1003,6 +1023,8 @@ const apiService = { fetchFavoritedByUsers, fetchRebloggedByUsers, fetchEmojiReactions, + reactWithEmoji, + unreactWithEmoji, reportUser, updateNotificationSettings, search2, diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index 52234fcc..44233a24 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -144,6 +144,8 @@ const backendInteractorService = credentials => { const fetchFavoritedByUsers = (id) => apiService.fetchFavoritedByUsers({ id }) const fetchRebloggedByUsers = (id) => apiService.fetchRebloggedByUsers({ id }) const fetchEmojiReactions = (id) => apiService.fetchEmojiReactions({ id }) + const reactWithEmoji = (id, emoji) => apiService.reactWithEmoji({ id, emoji, credentials }) + const unreactWithEmoji = (id, emoji) => apiService.unreactWithEmoji({ id, emoji, credentials }) const reportUser = (params) => apiService.reportUser({ credentials, ...params }) const favorite = (id) => apiService.favorite({ id, credentials }) @@ -212,6 +214,8 @@ const backendInteractorService = credentials => { fetchFavoritedByUsers, fetchRebloggedByUsers, fetchEmojiReactions, + reactWithEmoji, + unreactWithEmoji, reportUser, favorite, unfavorite, -- cgit v1.2.3-70-g09d2 From b10b92a876eb185a88e751d028e69063c9117298 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Tue, 14 Jan 2020 10:06:14 +0200 Subject: clean up code, fix prediction bug --- src/components/emoji_reactions/emoji_reactions.js | 30 +++++++++++++ src/components/emoji_reactions/emoji_reactions.vue | 51 ++++++++++++++++++++++ src/components/react_button/react_button.js | 5 ++- src/components/react_button/react_button.vue | 42 ++++++++++-------- src/components/status/status.js | 23 ++-------- src/components/status/status.vue | 47 ++------------------ src/modules/statuses.js | 9 ++-- 7 files changed, 120 insertions(+), 87 deletions(-) create mode 100644 src/components/emoji_reactions/emoji_reactions.js create mode 100644 src/components/emoji_reactions/emoji_reactions.vue (limited to 'src/components/status/status.vue') diff --git a/src/components/emoji_reactions/emoji_reactions.js b/src/components/emoji_reactions/emoji_reactions.js new file mode 100644 index 00000000..e81e6e25 --- /dev/null +++ b/src/components/emoji_reactions/emoji_reactions.js @@ -0,0 +1,30 @@ + +const EmojiReactions = { + name: 'EmojiReactions', + props: ['status'], + computed: { + emojiReactions () { + return this.status.emojiReactions + } + }, + methods: { + reactedWith (emoji) { + return this.status.reactedWithEmoji.includes(emoji) + }, + reactWith (emoji) { + this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) + }, + unreact (emoji) { + this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) + }, + emojiOnClick (emoji, event) { + if (this.reactedWith(emoji)) { + this.unreact(emoji) + } else { + this.reactWith(emoji) + } + } + } +} + +export default EmojiReactions diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue new file mode 100644 index 00000000..d83f60b6 --- /dev/null +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -0,0 +1,51 @@ + + + + diff --git a/src/components/react_button/react_button.js b/src/components/react_button/react_button.js index 76a49305..d1a179bc 100644 --- a/src/components/react_button/react_button.js +++ b/src/components/react_button/react_button.js @@ -15,8 +15,9 @@ const ReactButton = { } }, methods: { - toggleReactionSelect () { - this.showTooltip = !this.showTooltip + openReactionSelect () { + this.showTooltip = true + this.filterWord = '' }, closeReactionSelect () { this.showTooltip = false diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue index f7015316..ae975dee 100644 --- a/src/components/react_button/react_button.vue +++ b/src/components/react_button/react_button.vue @@ -9,13 +9,16 @@ >
- +
{{ emoji }} @@ -24,7 +27,7 @@ {{ emoji.replacement }} @@ -34,11 +37,11 @@
{{ status.fave_num }} @@ -58,7 +61,7 @@ .reaction-picker-divider { height: 1px; width: 100%; - margin: 0.4em; + margin: 0.5em; background-color: var(--border, $fallback--border); } @@ -82,26 +85,27 @@ // Autoprefixed seem to ignore this one, and also syntax is different -webkit-mask-composite: xor; mask-composite: exclude; -} -.emoji-reaction-button { - flex-basis: 20%; - line-height: 1.5em; - align-content: center; + .emoji-button { + cursor: pointer; + + flex-basis: 20%; + line-height: 1.5em; + align-content: center; + + &:hover { + transform: scale(1.25); + } + } } -.fav-active { +.add-reaction-button { cursor: pointer; - animation-duration: 0.6s; &:hover { - color: $fallback--cOrange; - color: var(--cOrange, $fallback--cOrange); + color: $fallback--text; + color: var(--text, $fallback--text); } } -.favorite-button.icon-star { - color: $fallback--cOrange; - color: var(--cOrange, $fallback--cOrange); -} diff --git a/src/components/status/status.js b/src/components/status/status.js index 18617938..81b57667 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -12,6 +12,7 @@ import LinkPreview from '../link-preview/link-preview.vue' import AvatarList from '../avatar_list/avatar_list.vue' import Timeago from '../timeago/timeago.vue' import StatusPopover from '../status_popover/status_popover.vue' +import EmojiReactions from '../emoji_reactions/emoji_reactions.vue' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import fileType from 'src/services/file_type/file_type.service' import { processHtml } from 'src/services/tiny_post_html_processor/tiny_post_html_processor.service.js' @@ -311,9 +312,6 @@ const Status = { hidePostStats () { return this.mergedConfig.hidePostStats }, - emojiReactions () { - return this.status.emojiReactions - }, ...mapGetters(['mergedConfig']), ...mapState({ betterShadow: state => state.interface.browserSupport.cssFilter, @@ -334,7 +332,8 @@ const Status = { LinkPreview, AvatarList, Timeago, - StatusPopover + StatusPopover, + EmojiReactions }, methods: { visibilityIcon (visibility) { @@ -418,22 +417,6 @@ const Status = { setMedia () { const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments return () => this.$store.dispatch('setMedia', attachments) - }, - reactedWith (emoji) { - return this.status.reactedWithEmoji.includes(emoji) - }, - reactWith (emoji) { - this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) - }, - unreact (emoji) { - this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) - }, - emojiOnClick (emoji, event) { - if (this.reactedWith(emoji)) { - this.unreact(emoji) - } else { - this.reactWith(emoji) - } } }, watch: { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 4ea1b74b..87e8b5da 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -354,18 +354,10 @@
-
- -
+
find(reaction, { id: currentUser.id })))(emojiReactions) + const reactedWithEmoji = flow( + keys, + filter(reaction => find(reaction, { id: currentUser.id })) + )(emojiReactions) set(status, 'reactedWithEmoji', reactedWithEmoji) }, addOwnReaction (state, { id, emoji, currentUser }) { @@ -547,7 +550,7 @@ export const mutations = { const hasSelfAlready = !!find(listOfUsers, { id: currentUser.id }) if (!hasSelfAlready) { set(status.emojiReactions, emoji, listOfUsers.concat([{ id: currentUser.id }])) - set(status, 'reactedWithEmoji', emoji) + set(status, 'reactedWithEmoji', [...status.reactedWithEmoji, emoji]) } }, removeOwnReaction (state, { id, emoji, currentUser }) { @@ -557,7 +560,7 @@ export const mutations = { if (hasSelfAlready) { const newUsers = filter(listOfUsers, user => user.id !== currentUser.id) set(status.emojiReactions, emoji, newUsers) - set(status, 'reactedWith', emoji) + set(status, 'reactedWithEmoji', status.reactedWithEmoji.filter(e => e !== emoji)) } }, updateStatusWithPoll (state, { id, poll }) { -- cgit v1.2.3-70-g09d2 From b96993e4dd6b2d9197b430af404e8fa652888b51 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 24 Jan 2020 00:36:32 +0200 Subject: improved selectedMenu again --- src/components/emoji_input/emoji_input.vue | 12 +++-- src/components/emoji_picker/emoji_picker.scss | 9 ++++ src/components/nav_panel/nav_panel.vue | 2 + src/components/popper/popper.scss | 12 +++-- src/components/selectable_list/selectable_list.vue | 1 + src/components/side_drawer/side_drawer.vue | 12 +++-- src/components/status/status.vue | 1 + src/services/color_convert/color_convert.js | 2 +- src/services/theme_data/pleromafe.js | 63 +++++++++++++++++++++- src/services/theme_data/theme_data.service.js | 7 +-- static/themes/breezy-dark.json | 4 +- static/themes/breezy-light.json | 4 +- 12 files changed, 105 insertions(+), 24 deletions(-) (limited to 'src/components/status/status.vue') diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue index 9c2501a9..94eae560 100644 --- a/src/components/emoji_input/emoji_input.vue +++ b/src/components/emoji_input/emoji_input.vue @@ -115,6 +115,7 @@ color: var(--popoverText, $fallback--link); --faint: var(--popoverFaintText, $fallback--faint); --faintLink: var(--popoverFaintLink, $fallback--faint); + --lightText: var(--popoverLightText, $fallback--lightText); --icon: var(--popoverIcon, $fallback--icon); } } @@ -160,11 +161,12 @@ &.highlighted { background-color: $fallback--fg; - background-color: var(--selectedMenu, $fallback--fg); - color: var(--selectedMenuText, $fallback--text); - --faint: var(--selectedMenuFaintText, $fallback--faint); - --faintLink: var(--selectedMenuFaintLink, $fallback--faint); - --icon: var(--selectedMenuIcon, $fallback--icon); + background-color: var(--selectedMenuPopover, $fallback--fg); + color: var(--selectedMenuPopoverText, $fallback--text); + --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); + --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); + --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); + --icon: var(--selectedMenuPopoverIcon, $fallback--icon); } } } diff --git a/src/components/emoji_picker/emoji_picker.scss b/src/components/emoji_picker/emoji_picker.scss index 6608f393..8bd07e45 100644 --- a/src/components/emoji_picker/emoji_picker.scss +++ b/src/components/emoji_picker/emoji_picker.scss @@ -8,6 +8,15 @@ left: 0; margin: 0 !important; z-index: 1; + background-color: $fallback--bg; + background-color: var(--popover, $fallback--bg); + color: $fallback--link; + color: var(--popoverText, $fallback--link); + --lightText: var(--popoverLightText, $fallback--faint); + --faint: var(--popoverFaintText, $fallback--faint); + --faintLink: var(--popoverFaintLink, $fallback--faint); + --lightText: var(--popoverLightText, $fallback--lightText); + --icon: var(--popoverIcon, $fallback--icon); .keep-open, .too-many-emoji { diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue index afc611ea..a934a411 100644 --- a/src/components/nav_panel/nav_panel.vue +++ b/src/components/nav_panel/nav_panel.vue @@ -105,6 +105,7 @@ color: var(--selectedMenuText, $fallback--link); --faint: var(--selectedMenuFaintText, $fallback--faint); --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --lightText: var(--selectedMenuLightText, $fallback--lightText); --icon: var(--selectedMenuIcon, $fallback--icon); } @@ -116,6 +117,7 @@ color: var(--selectedMenuText, $fallback--text); --faint: var(--selectedMenuFaintText, $fallback--faint); --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --lightText: var(--selectedMenuLightText, $fallback--lightText); --icon: var(--selectedMenuIcon, $fallback--icon); &:hover { diff --git a/src/components/popper/popper.scss b/src/components/popper/popper.scss index e70b277d..591be37e 100644 --- a/src/components/popper/popper.scss +++ b/src/components/popper/popper.scss @@ -14,6 +14,7 @@ color: var(--popoverText, $fallback--text); --faint: var(--popoverFaintText, $fallback--faint); --faintLink: var(--popoverFaintLink, $fallback--faint); + --lightText: var(--popoverLightText, $fallback--lightText); --icon: var(--popoverIcon, $fallback--icon); } @@ -144,12 +145,13 @@ &:active, &:hover { background-color: $fallback--lightBg; - background-color: var(--selectedMenu, $fallback--lightBg); + background-color: var(--selectedMenuPopover, $fallback--lightBg); color: $fallback--link; - color: var(--selectedMenuText, $fallback--link); - --faint: var(--selectedMenuFaintText, $fallback--faint); - --faintLink: var(--selectedMenuFaintLink, $fallback--faint); - --icon: var(--selectedMenuIcon, $fallback--icon); + color: var(--selectedMenuPopoverText, $fallback--link); + --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); + --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); + --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); + --icon: var(--selectedMenuPopoverIcon, $fallback--icon); } } } diff --git a/src/components/selectable_list/selectable_list.vue b/src/components/selectable_list/selectable_list.vue index 2d1e44a3..a9bb12a1 100644 --- a/src/components/selectable_list/selectable_list.vue +++ b/src/components/selectable_list/selectable_list.vue @@ -72,6 +72,7 @@ color: var(--selectedMenuText, $fallback--text); --faint: var(--selectedMenuFaintText, $fallback--faint); --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --lightText: var(--selectedMenuLightText, $fallback--lightText); --icon: var(--selectedMenuIcon, $fallback--icon); } diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue index a5b754d7..25e4de01 100644 --- a/src/components/side_drawer/side_drawer.vue +++ b/src/components/side_drawer/side_drawer.vue @@ -228,6 +228,7 @@ color: var(--popoverText, $fallback--link); --faint: var(--popoverFaintText, $fallback--faint); --faintLink: var(--popoverFaintLink, $fallback--faint); + --lightText: var(--popoverLightText, $fallback--lightText); --icon: var(--popoverIcon, $fallback--icon); .button-icon:before { @@ -294,12 +295,13 @@ &:hover { background-color: $fallback--lightBg; - background-color: var(--selectedMenu, $fallback--lightBg); + background-color: var(--selectedMenuPopover, $fallback--lightBg); color: $fallback--text; - color: var(--selectedMenuText, $fallback--text); - --faint: var(--selectedMenuFaintText, $fallback--faint); - --faintLink: var(--selectedMenuFaintLink, $fallback--faint); - --icon: var(--selectedMenuIcon, $fallback--icon); + color: var(--selectedMenuPopoverText, $fallback--text); + --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); + --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); + --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); + --icon: var(--selectedMenuPopoverIcon, $fallback--icon); } } } diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 38d091ed..1997e187 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -448,6 +448,7 @@ $status-margin: 0.75em; background-color: var(--selectedPost, $fallback--lightBg); color: $fallback--text; color: var(--selectedPostText, $fallback--text); + --lightText: var(--selectedPostLightText, $fallback--light); --faint: var(--selectedPostFaintText, $fallback--faint); --faintLink: var(--selectedPostFaintLink, $fallback--faint); --icon: var(--selectedPostIcon, $fallback--icon); diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index 93cb1ba6..0bf8f646 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -69,7 +69,7 @@ const srgbToLinear = (srgb) => { * @param {Object} srgb - sRGB color * @returns {Number} relative luminance */ -const relativeLuminance = (srgb) => { +export const relativeLuminance = (srgb) => { const { r, g, b } = srgbToLinear(srgb) return 0.2126 * r + 0.7152 * g + 0.0722 * b } diff --git a/src/services/theme_data/pleromafe.js b/src/services/theme_data/pleromafe.js index 484874d7..a40d08a6 100644 --- a/src/services/theme_data/pleromafe.js +++ b/src/services/theme_data/pleromafe.js @@ -117,6 +117,11 @@ export const SLOT_INHERITANCE = { depends: ['bg'], color: (mod, bg) => brightness(5 * mod, bg).rgb }, + highlightLightText: { + depends: ['lightText'], + layer: 'highlight', + textColor: true + }, highlightFaintText: { depends: ['faint'], layer: 'highlight', @@ -146,6 +151,11 @@ export const SLOT_INHERITANCE = { depends: ['bg'], opacity: 'popover' }, + popoverLightText: { + depends: ['lightText'], + layer: 'popover', + textColor: true + }, popoverFaintText: { depends: ['faint'], layer: 'popover', @@ -178,6 +188,12 @@ export const SLOT_INHERITANCE = { variant: 'selectedPost', textColor: true }, + selectedPostLightText: { + depends: ['highlightLightText'], + layer: 'highlight', + variant: 'selectedPost', + textColor: true + }, selectedPostFaintLink: { depends: ['highlightFaintLink'], layer: 'highlight', @@ -202,9 +218,15 @@ export const SLOT_INHERITANCE = { }, selectedMenu: { - depends: ['popover'], + depends: ['bg'], color: (mod, bg) => brightness(5 * mod, bg).rgb }, + selectedMenuLightText: { + depends: ['highlightLightText'], + layer: 'selectedMenu', + variant: 'selectedMenu', + textColor: true + }, selectedMenuFaintText: { depends: ['highlightFaintText'], layer: 'selectedMenu', @@ -234,6 +256,45 @@ export const SLOT_INHERITANCE = { color: (mod, bg, text) => mixrgb(bg, text) }, + selectedMenuPopover: { + depends: ['popover'], + color: (mod, bg) => brightness(5 * mod, bg).rgb + }, + selectedMenuPopoverLightText: { + depends: ['selectedMenuLightText'], + layer: 'selectedMenuPopover', + variant: 'selectedMenuPopover', + textColor: true + }, + selectedMenuPopoverFaintText: { + depends: ['selectedMenuFaintText'], + layer: 'selectedMenuPopover', + variant: 'selectedMenuPopover', + textColor: true + }, + selectedMenuPopoverFaintLink: { + depends: ['selectedMenuFaintLink'], + layer: 'selectedMenuPopover', + variant: 'selectedMenuPopover', + textColor: 'preserve' + }, + selectedMenuPopoverText: { + depends: ['selectedMenuText'], + layer: 'selectedMenuPopover', + variant: 'selectedMenuPopover', + textColor: true + }, + selectedMenuPopoverLink: { + depends: ['selectedMenuLink'], + layer: 'selectedMenuPopover', + variant: 'selectedMenuPopover', + textColor: 'preserve' + }, + selectedMenuPopoverIcon: { + depends: ['selectedMenuPopover', 'selectedMenuText'], + color: (mod, bg, text) => mixrgb(bg, text) + }, + lightText: { depends: ['text'], color: (mod, text) => brightness(20 * mod, text).rgb diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js index ea28481b..c0861a4a 100644 --- a/src/services/theme_data/theme_data.service.js +++ b/src/services/theme_data/theme_data.service.js @@ -1,5 +1,5 @@ import { convert, brightness, contrastRatio } from 'chromatism' -import { alphaBlendLayers, getTextColor } from '../color_convert/color_convert.js' +import { alphaBlendLayers, getTextColor, relativeLuminance } from '../color_convert/color_convert.js' import { LAYERS, DEFAULT_OPACITY, SLOT_INHERITANCE } from './pleromafe.js' /* @@ -318,13 +318,14 @@ export const getColors = (sourceColors, sourceOpacity, mod) => SLOT_ORDERED.redu opacity ) ) + const isLightOnDark = relativeLuminance(bg) > 127 + const mod = isLightOnDark ? 1 : -1 + if (value.textColor === 'bw') { outputColor = contrastRatio(bg).rgb } else { let color = { ...colors[deps[0]] } if (value.color) { - const isLightOnDark = convert(bg).hsl.l < convert(color).hsl.l - const mod = isLightOnDark ? 1 : -1 color = value.color(mod, ...deps.map((dep) => ({ ...colors[dep] }))) } diff --git a/static/themes/breezy-dark.json b/static/themes/breezy-dark.json index c3bdcf65..236b94ad 100644 --- a/static/themes/breezy-dark.json +++ b/static/themes/breezy-dark.json @@ -114,8 +114,8 @@ "cGreen": "#27ae60", "cOrange": "#f67400", "btnPressed": "--accent", - "highlight": "--accent", - "selectedPost": "--bg,10" + "selectedMenu": "--accent", + "selectedMenuPopover": "--accent" }, "radii": { "btn": "2", diff --git a/static/themes/breezy-light.json b/static/themes/breezy-light.json index 478407c9..d3f74cec 100644 --- a/static/themes/breezy-light.json +++ b/static/themes/breezy-light.json @@ -117,8 +117,8 @@ "cGreen": "#27ae60", "cOrange": "#f67400", "btnPressed": "--accent", - "highlight": "--accent", - "selectedPost": "--bg,10" + "selectedMenu": "--accent", + "selectedMenuPopover": "--accent" }, "radii": { "btn": "2", -- cgit v1.2.3-70-g09d2 From a018ea622c4ae34fd204e840b20aba53f84cd051 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sun, 26 Jan 2020 15:45:12 +0200 Subject: change emoji reactions to use new format --- src/components/conversation/conversation.js | 2 +- src/components/emoji_reactions/emoji_reactions.js | 9 ++- src/components/emoji_reactions/emoji_reactions.vue | 12 ++-- src/components/status/status.vue | 1 - src/modules/statuses.js | 66 +++++++++++++--------- .../entity_normalizer/entity_normalizer.service.js | 1 + test/unit/specs/modules/statuses.spec.js | 45 +++++++++++++++ 7 files changed, 99 insertions(+), 37 deletions(-) (limited to 'src/components/status/status.vue') diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 7ff0ac08..45fb2bf6 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -150,7 +150,7 @@ const conversation = { if (!id) return this.highlight = id this.$store.dispatch('fetchFavsAndRepeats', id) - this.$store.dispatch('fetchEmojiReactions', id) + this.$store.dispatch('fetchEmojiReactionsBy', id) }, getHighlight () { return this.isExpanded ? this.highlight : null diff --git a/src/components/emoji_reactions/emoji_reactions.js b/src/components/emoji_reactions/emoji_reactions.js index e81e6e25..b37cce3d 100644 --- a/src/components/emoji_reactions/emoji_reactions.js +++ b/src/components/emoji_reactions/emoji_reactions.js @@ -4,12 +4,17 @@ const EmojiReactions = { props: ['status'], computed: { emojiReactions () { - return this.status.emojiReactions + console.log(this.status.emoji_reactions) + return this.status.emoji_reactions } }, methods: { reactedWith (emoji) { - return this.status.reactedWithEmoji.includes(emoji) + // return [] + const user = this.$store.state.users.currentUser + const reaction = this.status.emoji_reactions.find(r => r.emoji === emoji) + console.log(reaction) + return reaction.accounts && reaction.accounts.find(u => u.id === user.id) }, reactWith (emoji) { this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index d83f60b6..8a229240 100644 --- a/src/components/emoji_reactions/emoji_reactions.vue +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -1,14 +1,14 @@ diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 87e8b5da..d5739304 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -355,7 +355,6 @@ diff --git a/src/modules/statuses.js b/src/modules/statuses.js index dbae9d38..ea0c1749 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -10,10 +10,7 @@ import { first, last, isArray, - omitBy, - flow, - filter, - keys + omitBy } from 'lodash' import { set } from 'vue' import apiService from '../services/api/api.service.js' @@ -534,33 +531,48 @@ export const mutations = { newStatus.fave_num = newStatus.favoritedBy.length newStatus.favorited = !!newStatus.favoritedBy.find(({ id }) => currentUser.id === id) }, - addEmojiReactions (state, { id, emojiReactions, currentUser }) { + addEmojiReactionsBy (state, { id, emojiReactions, currentUser }) { const status = state.allStatusesObject[id] - set(status, 'emojiReactions', emojiReactions) - const reactedWithEmoji = flow( - keys, - filter(reaction => find(reaction, { id: currentUser.id })) - )(emojiReactions) - set(status, 'reactedWithEmoji', reactedWithEmoji) + set(status, 'emoji_reactions', emojiReactions) }, addOwnReaction (state, { id, emoji, currentUser }) { const status = state.allStatusesObject[id] - status.emojiReactions = status.emojiReactions || {} - const listOfUsers = (status.emojiReactions && status.emojiReactions[emoji]) || [] - const hasSelfAlready = !!find(listOfUsers, { id: currentUser.id }) - if (!hasSelfAlready) { - set(status.emojiReactions, emoji, listOfUsers.concat([{ id: currentUser.id }])) - set(status, 'reactedWithEmoji', [...status.reactedWithEmoji, emoji]) + const reactionIndex = findIndex(status.emoji_reactions, { emoji }) + const reaction = status.emoji_reactions[reactionIndex] || { emoji, count: 0, accounts: [] } + + const newReaction = { + ...reaction, + count: reaction.count + 1, + accounts: [ + ...reaction.accounts, + currentUser + ] + } + + // Update count of existing reaction if it exists, otherwise append at the end + if (reactionIndex >= 0) { + set(status.emoji_reactions, reactionIndex, newReaction) + } else { + set(status, 'emoji_reactions', [...status.emoji_reactions, newReaction]) } }, removeOwnReaction (state, { id, emoji, currentUser }) { const status = state.allStatusesObject[id] - const listOfUsers = status.emojiReactions[emoji] || [] - const hasSelfAlready = !!find(listOfUsers, { id: currentUser.id }) - if (hasSelfAlready) { - const newUsers = filter(listOfUsers, user => user.id !== currentUser.id) - set(status.emojiReactions, emoji, newUsers) - set(status, 'reactedWithEmoji', status.reactedWithEmoji.filter(e => e !== emoji)) + const reactionIndex = findIndex(status.emoji_reactions, { emoji }) + if (reactionIndex < 0) return + + const reaction = status.emoji_reactions[reactionIndex] + + const newReaction = { + ...reaction, + count: reaction.count - 1, + accounts: reaction.accounts.filter(acc => acc.id === currentUser.id) + } + + if (newReaction.count > 0) { + set(status.emoji_reactions, reactionIndex, newReaction) + } else { + set(status, 'emoji_reactions', status.emoji_reactions.filter(r => r.emoji !== emoji)) } }, updateStatusWithPoll (state, { id, poll }) { @@ -672,7 +684,7 @@ const statuses = { commit('addOwnReaction', { id, emoji, currentUser }) rootState.api.backendInteractor.reactWithEmoji({ id, emoji }).then( status => { - dispatch('fetchEmojiReactions', id) + dispatch('fetchEmojiReactionsBy', id) } ) }, @@ -681,14 +693,14 @@ const statuses = { commit('removeOwnReaction', { id, emoji, currentUser }) rootState.api.backendInteractor.unreactWithEmoji({ id, emoji }).then( status => { - dispatch('fetchEmojiReactions', id) + dispatch('fetchEmojiReactionsBy', id) } ) }, - fetchEmojiReactions ({ rootState, commit }, id) { + fetchEmojiReactionsBy ({ rootState, commit }, id) { rootState.api.backendInteractor.fetchEmojiReactions({ id }).then( emojiReactions => { - commit('addEmojiReactions', { id, emojiReactions, currentUser: rootState.users.currentUser }) + commit('addEmojiReactionsBy', { id, emojiReactions, currentUser: rootState.users.currentUser }) } ) }, diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index ee007bee..03eaa5d7 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -233,6 +233,7 @@ export const parseStatus = (data) => { output.statusnet_html = addEmojis(data.content, data.emojis) output.tags = data.tags + output.emoji_reactions = [{ emoji: 'A', count: 5 }] // data.pleroma.emoji_reactions if (data.pleroma) { const { pleroma } = data diff --git a/test/unit/specs/modules/statuses.spec.js b/test/unit/specs/modules/statuses.spec.js index f794997b..e53aa388 100644 --- a/test/unit/specs/modules/statuses.spec.js +++ b/test/unit/specs/modules/statuses.spec.js @@ -241,6 +241,51 @@ describe('Statuses module', () => { }) }) + describe('emojiReactions', () => { + it('increments count in existing reaction', () => { + const state = defaultState() + const status = makeMockStatus({ id: '1' }) + status.emoji_reactions = [ { emoji: '😂', count: 1, accounts: [] } ] + + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.addOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } }) + expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(2) + expect(state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id).to.eql('me') + }) + + it('adds a new reaction', () => { + const state = defaultState() + const status = makeMockStatus({ id: '1' }) + status.emoji_reactions = [] + + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.addOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } }) + expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(1) + expect(state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id).to.eql('me') + }) + + it('decreases count in existing reaction', () => { + const state = defaultState() + const status = makeMockStatus({ id: '1' }) + status.emoji_reactions = [ { emoji: '😂', count: 2, accounts: [{ id: 'me' }] } ] + + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: {} }) + expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(1) + expect(state.allStatusesObject['1'].emoji_reactions[0].accounts).to.eql([]) + }) + + it('removes a reaction', () => { + const state = defaultState() + const status = makeMockStatus({ id: '1' }) + status.emoji_reactions = [{ emoji: '😂', count: 1, accounts: [{ id: 'me' }] }] + + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: {} }) + expect(state.allStatusesObject['1'].emoji_reactions.length).to.eql(0) + }) + }) + describe('showNewStatuses', () => { it('resets the minId to the min of the visible statuses when adding new to visible statuses', () => { const state = defaultState() -- cgit v1.2.3-70-g09d2 From 526b43eba66c597c78857da6ded3c7f672f96e49 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 28 Jan 2020 01:29:20 +0200 Subject: Kenomo (see: #624) theme. Ability to define link color for post contents. Fixes --- src/components/emoji_input/emoji_input.vue | 2 + src/components/notifications/notifications.scss | 3 + src/components/popper/popper.scss | 2 + src/components/status/status.vue | 7 +++ src/components/style_switcher/style_switcher.vue | 17 ++++++ src/components/user_card/user_card.vue | 5 ++ src/services/theme_data/pleromafe.js | 34 ++++++++++++ static/themes/kenomo.json | 71 ++++++++++++++++++++++++ 8 files changed, 141 insertions(+) create mode 100644 static/themes/kenomo.json (limited to 'src/components/status/status.vue') diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue index 94eae560..e9ac09c3 100644 --- a/src/components/emoji_input/emoji_input.vue +++ b/src/components/emoji_input/emoji_input.vue @@ -116,6 +116,8 @@ --faint: var(--popoverFaintText, $fallback--faint); --faintLink: var(--popoverFaintLink, $fallback--faint); --lightText: var(--popoverLightText, $fallback--lightText); + --postLink: var(--popoverPostLink, $fallback--link); + --postFaintLink: var(--popoverPostFaintLink, $fallback--link); --icon: var(--popoverIcon, $fallback--icon); } } diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 71876b14..ca762432 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -68,6 +68,9 @@ a { color: var(--faintLink); } + .status-content a { + color: var(--postFaintLink); + } } padding: 0; .media-body { diff --git a/src/components/popper/popper.scss b/src/components/popper/popper.scss index 341416c1..8e2dcd55 100644 --- a/src/components/popper/popper.scss +++ b/src/components/popper/popper.scss @@ -15,6 +15,8 @@ --faint: var(--popoverFaintText, $fallback--faint); --faintLink: var(--popoverFaintLink, $fallback--faint); --lightText: var(--popoverLightText, $fallback--lightText); + --postLink: var(--popoverPostLink, $fallback--link); + --postFaintLink: var(--popoverPostFaintLink, $fallback--link); --icon: var(--popoverIcon, $fallback--icon); } diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 1997e187..b1048832 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -451,6 +451,8 @@ $status-margin: 0.75em; --lightText: var(--selectedPostLightText, $fallback--light); --faint: var(--selectedPostFaintText, $fallback--faint); --faintLink: var(--selectedPostFaintLink, $fallback--faint); + --postLink: var(--selectedPostPostLink, $fallback--faint); + --postFaintLink: var(--selectedPostFaintPostLink, $fallback--faint); --icon: var(--selectedPostIcon, $fallback--icon); } @@ -640,6 +642,11 @@ $status-margin: 0.75em; line-height: 1.4em; white-space: pre-wrap; + a { + color: $fallback--link; + color: var(--postLink, $fallback--link); + } + img, video { max-width: 100%; max-height: 400px; diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 6e38bd8e..0b3b0fbf 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -248,6 +248,23 @@
+

{{ $t('settings.style.advanced_colors.post') }}

+ + + +

{{ $t('settings.style.advanced_colors.alert') }}

brightness(20 * mod, text).rgb }, + postLink: { + depends: ['link'] + }, + border: { depends: ['fg'], opacity: 'border', diff --git a/static/themes/kenomo.json b/static/themes/kenomo.json new file mode 100644 index 00000000..98ddf974 --- /dev/null +++ b/static/themes/kenomo.json @@ -0,0 +1,71 @@ +{ + "_pleroma_theme_version": 2, + "name": "Kenomo", + "source": { + "themeEngineVersion": 3, + "fonts": {}, + "shadows": { + "panel": [], + "topBar": [], + "button": [ + { + "x": 0, + "y": 1, + "blur": 0, + "spread": 0, + "color": "#FFFFFF", + "alpha": 0.2, + "inset": true + }, + { + "x": 0, + "y": -1, + "blur": 0, + "spread": 0, + "color": "#000000", + "alpha": 0.2, + "inset": true + } + ], + "input": [ + { + "x": 0, + "y": "0", + "blur": 0, + "spread": "1", + "color": "#576574", + "alpha": "1", + "inset": true + } + ] + }, + "opacity": { + "underlay": "1", + "border": "0" + }, + "colors": { + "bg": "#ffffff", + "fg": "#f6f6f6", + "text": "#494949", + "underlay": "#ffffff", + "link": "#818181", + "accent": "#818181", + "cBlue": "#2e86de", + "cRed": "#c96248", + "cGreen": "#0fa00f", + "cOrange": "#aa7623", + "postLink": "#2e86de", + "border": "#ffffff", + "icon": "#8a8a8a", + "panel": "transparent", + "topBarText": "#4b4b4b", + "tab": "--btn,-30", + "btn": "#576574" + }, + "radii": { + "panel": "0", + "avatar": "6", + "avatarAlt": "6" + } + } +} -- cgit v1.2.3-70-g09d2 From 36e19128bf958559437144b26a3e71f30c9b3377 Mon Sep 17 00:00:00 2001 From: xenofem Date: Sat, 8 Feb 2020 13:15:09 -0500 Subject: Indicate whether collapsed statuses contain gallery media or link preview cards --- src/components/status/status.vue | 12 +++++++++++- static/fontello.json | 8 +++++++- 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'src/components/status/status.vue') diff --git a/src/components/status/status.vue b/src/components/status/status.vue index d5739304..b9e3fa1d 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -277,7 +277,17 @@ href="#" class="cw-status-hider" @click.prevent="toggleShowMore" - >{{ $t("general.show_more") }} + > + {{ $t("general.show_more") }} + + + Date: Sat, 8 Feb 2020 16:01:01 -0500 Subject: Include non-gallery attachments and distinguish between images and videos --- src/components/status/status.js | 10 ++++++++++ src/components/status/status.vue | 6 +++++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'src/components/status/status.vue') diff --git a/src/components/status/status.js b/src/components/status/status.js index 81b57667..fc5956ec 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -256,6 +256,16 @@ const Status = { file => !fileType.fileMatchesSomeType(this.galleryTypes, file) ) }, + hasImageAttachments () { + return this.status.attachments.some( + file => fileType.fileType(file.mimetype) === 'image' + ) + }, + hasVideoAttachments () { + return this.status.attachments.some( + file => fileType.fileType(file.mimetype) === 'video' + ) + }, maxThumbnails () { return this.mergedConfig.maxThumbnails }, diff --git a/src/components/status/status.vue b/src/components/status/status.vue index b9e3fa1d..0a82dcbe 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -280,9 +280,13 @@ > {{ $t("general.show_more") }} + Date: Tue, 11 Feb 2020 12:24:51 +0000 Subject: Emoji Reactions - fixes and improvements --- src/App.scss | 2 +- src/_variables.scss | 2 + src/components/emoji_reactions/emoji_reactions.js | 48 +++++++++- src/components/emoji_reactions/emoji_reactions.vue | 103 +++++++++++++++++++-- src/components/notification/notification.vue | 7 ++ src/components/notifications/notifications.scss | 4 + src/components/react_button/react_button.js | 7 +- src/components/react_button/react_button.vue | 4 + src/components/settings/settings.vue | 10 ++ src/components/status/status.vue | 1 + src/i18n/en.json | 5 +- src/i18n/fi.json | 5 +- src/modules/config.js | 4 +- src/modules/statuses.js | 36 ++++--- src/services/api/api.service.js | 28 +++--- .../entity_normalizer/entity_normalizer.service.js | 1 + .../notification_utils/notification_utils.js | 3 +- test/unit/specs/modules/statuses.spec.js | 13 ++- 18 files changed, 236 insertions(+), 47 deletions(-) (limited to 'src/components/status/status.vue') diff --git a/src/App.scss b/src/App.scss index 754ca62e..922e39b6 100644 --- a/src/App.scss +++ b/src/App.scss @@ -75,7 +75,7 @@ button { border-radius: $fallback--btnRadius; border-radius: var(--btnRadius, $fallback--btnRadius); cursor: pointer; - box-shadow: 0px 0px 2px 0px rgba(0, 0, 0, 1), 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset; + box-shadow: $fallback--buttonShadow; box-shadow: var(--buttonShadow); font-size: 14px; font-family: sans-serif; diff --git a/src/_variables.scss b/src/_variables.scss index e18101f0..30dc3e42 100644 --- a/src/_variables.scss +++ b/src/_variables.scss @@ -27,3 +27,5 @@ $fallback--tooltipRadius: 5px; $fallback--avatarRadius: 4px; $fallback--avatarAltRadius: 10px; $fallback--attachmentRadius: 10px; + +$fallback--buttonShadow: 0px 0px 2px 0px rgba(0, 0, 0, 1), 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset; diff --git a/src/components/emoji_reactions/emoji_reactions.js b/src/components/emoji_reactions/emoji_reactions.js index 95d52cb6..b799ac9a 100644 --- a/src/components/emoji_reactions/emoji_reactions.js +++ b/src/components/emoji_reactions/emoji_reactions.js @@ -1,17 +1,55 @@ +import UserAvatar from '../user_avatar/user_avatar.vue' + +const EMOJI_REACTION_COUNT_CUTOFF = 12 const EmojiReactions = { name: 'EmojiReactions', + components: { + UserAvatar + }, props: ['status'], + data: () => ({ + showAll: false, + popperOptions: { + modifiers: { + preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' } + } + } + }), computed: { + tooManyReactions () { + return this.status.emoji_reactions.length > EMOJI_REACTION_COUNT_CUTOFF + }, emojiReactions () { - return this.status.emoji_reactions + return this.showAll + ? this.status.emoji_reactions + : this.status.emoji_reactions.slice(0, EMOJI_REACTION_COUNT_CUTOFF) + }, + showMoreString () { + return `+${this.status.emoji_reactions.length - EMOJI_REACTION_COUNT_CUTOFF}` + }, + accountsForEmoji () { + return this.status.emoji_reactions.reduce((acc, reaction) => { + acc[reaction.name] = reaction.accounts || [] + return acc + }, {}) + }, + loggedIn () { + return !!this.$store.state.users.currentUser } }, methods: { + toggleShowAll () { + this.showAll = !this.showAll + }, reactedWith (emoji) { - const user = this.$store.state.users.currentUser - const reaction = this.status.emoji_reactions.find(r => r.emoji === emoji) - return reaction.accounts && reaction.accounts.find(u => u.id === user.id) + return this.status.emoji_reactions.find(r => r.name === emoji).me + }, + fetchEmojiReactionsByIfMissing () { + const hasNoAccounts = this.status.emoji_reactions.find(r => !r.accounts) + if (hasNoAccounts) { + this.$store.dispatch('fetchEmojiReactionsBy', this.status.id) + } }, reactWith (emoji) { this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) @@ -20,6 +58,8 @@ const EmojiReactions = { this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) }, emojiOnClick (emoji, event) { + if (!this.loggedIn) return + if (this.reactedWith(emoji)) { this.unreact(emoji) } else { diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index 00d6d2b7..e5b6d9f5 100644 --- a/src/components/emoji_reactions/emoji_reactions.vue +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -1,16 +1,58 @@ @@ -23,6 +65,31 @@ flex-wrap: wrap; } +.reacted-users { + padding: 0.5em; +} + +.reacted-user { + padding: 0.25em; + display: flex; + flex-direction: row; + + .reacted-user-names { + display: flex; + flex-direction: column; + margin-left: 0.5em; + + img { + width: 1em; + height: 1em; + } + } + + .reacted-user-screen-name { + font-size: 9px; + } +} + .emoji-reaction { padding: 0 0.5em; margin-right: 0.5em; @@ -38,6 +105,26 @@ &:focus { outline: none; } + + &.not-clickable { + cursor: default; + &:hover { + box-shadow: $fallback--buttonShadow; + box-shadow: var(--buttonShadow); + } + } +} + +.emoji-reaction-expand { + padding: 0 0.5em; + margin-right: 0.5em; + margin-top: 0.5em; + display: flex; + align-items: center; + justify-content: center; + &:hover { + text-decoration: underline; + } } .picked-reaction { diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 16124e50..411c0271 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -78,6 +78,13 @@ {{ $t('notifications.migrated_to') }} + + + + {{ notification.emoji }} + + +
r.name === emoji) + if (existingReaction && existingReaction.me) { + this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) + } else { + this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) + } this.closeReactionSelect() } }, diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue index c925dd71..fb43ebaf 100644 --- a/src/components/react_button/react_button.vue +++ b/src/components/react_button/react_button.vue @@ -54,6 +54,10 @@ .reaction-picker-filter { padding: 0.5em; + display: flex; + input { + flex: 1; + } } .reaction-picker-divider { diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index cef492f3..60cb8a87 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -92,6 +92,11 @@ {{ $t('settings.reply_link_preview') }} +
  • + + {{ $t('settings.emoji_reactions_on_timeline') }} + +
  • @@ -328,6 +333,11 @@ {{ $t('settings.notification_visibility_moves') }} +
  • + + {{ $t('settings.notification_visibility_emoji_reactions') }} + +
  • diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 0a82dcbe..83f07dac 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -369,6 +369,7 @@ diff --git a/src/i18n/en.json b/src/i18n/en.json index 74e71fc8..d0d654d3 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -126,7 +126,8 @@ "read": "Read!", "repeated_you": "repeated your status", "no_more_notifications": "No more notifications", - "migrated_to": "migrated to" + "migrated_to": "migrated to", + "reacted_with": "reacted with {0}" }, "polls": { "add_poll": "Add Poll", @@ -283,6 +284,7 @@ "domain_mutes": "Domains", "avatar_size_instruction": "The recommended minimum size for avatar images is 150x150 pixels.", "pad_emoji": "Pad emoji with spaces when adding from picker", + "emoji_reactions_on_timeline": "Show emoji reactions on timeline", "export_theme": "Save preset", "filtering": "Filtering", "filtering_explanation": "All statuses containing these words will be muted, one per line", @@ -331,6 +333,7 @@ "notification_visibility_mentions": "Mentions", "notification_visibility_repeats": "Repeats", "notification_visibility_moves": "User Migrates", + "notification_visibility_emoji_reactions": "Reactions", "no_rich_text_description": "Strip rich text formatting from all posts", "no_blocks": "No blocks", "no_mutes": "No mutes", diff --git a/src/i18n/fi.json b/src/i18n/fi.json index e7ed5408..ac8b2ac9 100644 --- a/src/i18n/fi.json +++ b/src/i18n/fi.json @@ -53,7 +53,8 @@ "notifications": "Ilmoitukset", "read": "Lue!", "repeated_you": "toisti viestisi", - "no_more_notifications": "Ei enempää ilmoituksia" + "no_more_notifications": "Ei enempää ilmoituksia", + "reacted_with": "lisäsi reaktion {0}" }, "polls": { "add_poll": "Lisää äänestys", @@ -140,6 +141,7 @@ "delete_account_description": "Poista tilisi ja viestisi pysyvästi.", "delete_account_error": "Virhe poistaessa tiliäsi. Jos virhe jatkuu, ota yhteyttä palvelimesi ylläpitoon.", "delete_account_instructions": "Syötä salasanasi vahvistaaksesi tilin poiston.", + "emoji_reactions_on_timeline": "Näytä emojireaktiot aikajanalla", "export_theme": "Tallenna teema", "filtering": "Suodatus", "filtering_explanation": "Kaikki viestit, jotka sisältävät näitä sanoja, suodatetaan. Yksi sana per rivi.", @@ -183,6 +185,7 @@ "notification_visibility_likes": "Tykkäykset", "notification_visibility_mentions": "Maininnat", "notification_visibility_repeats": "Toistot", + "notification_visibility_emoji_reactions": "Reaktiot", "no_rich_text_description": "Älä näytä tekstin muotoilua.", "hide_network_description": "Älä näytä seurauksiani tai seuraajiani", "nsfw_clickthrough": "Piilota NSFW liitteet klikkauksen taakse", diff --git a/src/modules/config.js b/src/modules/config.js index de9f041b..8381fa53 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -20,6 +20,7 @@ export const defaultState = { autoLoad: true, streaming: false, hoverPreview: true, + emojiReactionsOnTimeline: true, autohideFloatingPostButton: false, pauseOnUnfocused: true, stopGifs: false, @@ -29,7 +30,8 @@ export const defaultState = { mentions: true, likes: true, repeats: true, - moves: true + moves: true, + emojiReactions: false }, webPushNotifications: false, muteWords: [], diff --git a/src/modules/statuses.js b/src/modules/statuses.js index ea0c1749..25b62ac7 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -81,7 +81,8 @@ const visibleNotificationTypes = (rootState) => { rootState.config.notificationVisibility.mentions && 'mention', rootState.config.notificationVisibility.repeats && 'repeat', rootState.config.notificationVisibility.follows && 'follow', - rootState.config.notificationVisibility.moves && 'move' + rootState.config.notificationVisibility.moves && 'move', + rootState.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reactions' ].filter(_ => _) } @@ -325,6 +326,10 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item } + if (notification.type === 'pleroma:emoji_reaction') { + dispatch('fetchEmojiReactionsBy', notification.status.id) + } + // Only add a new notification if we don't have one for the same action if (!state.notifications.idStore.hasOwnProperty(notification.id)) { state.notifications.maxId = notification.id > state.notifications.maxId @@ -358,7 +363,9 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot break } - if (i18nString) { + if (notification.type === 'pleroma:emoji_reaction') { + notifObj.body = rootGetters.i18n.t('notifications.reacted_with', [notification.emoji]) + } else if (i18nString) { notifObj.body = rootGetters.i18n.t('notifications.' + i18nString) } else { notifObj.body = notification.status.text @@ -371,10 +378,10 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot } if (!notification.seen && !state.notifications.desktopNotificationSilence && visibleNotificationTypes.includes(notification.type)) { - let notification = new window.Notification(title, notifObj) + let desktopNotification = new window.Notification(title, notifObj) // Chrome is known for not closing notifications automatically // according to MDN, anyway. - setTimeout(notification.close.bind(notification), 5000) + setTimeout(desktopNotification.close.bind(desktopNotification), 5000) } } } else if (notification.seen) { @@ -537,12 +544,13 @@ export const mutations = { }, addOwnReaction (state, { id, emoji, currentUser }) { const status = state.allStatusesObject[id] - const reactionIndex = findIndex(status.emoji_reactions, { emoji }) - const reaction = status.emoji_reactions[reactionIndex] || { emoji, count: 0, accounts: [] } + const reactionIndex = findIndex(status.emoji_reactions, { name: emoji }) + const reaction = status.emoji_reactions[reactionIndex] || { name: emoji, count: 0, accounts: [] } const newReaction = { ...reaction, count: reaction.count + 1, + me: true, accounts: [ ...reaction.accounts, currentUser @@ -558,21 +566,23 @@ export const mutations = { }, removeOwnReaction (state, { id, emoji, currentUser }) { const status = state.allStatusesObject[id] - const reactionIndex = findIndex(status.emoji_reactions, { emoji }) + const reactionIndex = findIndex(status.emoji_reactions, { name: emoji }) if (reactionIndex < 0) return const reaction = status.emoji_reactions[reactionIndex] + const accounts = reaction.accounts || [] const newReaction = { ...reaction, count: reaction.count - 1, - accounts: reaction.accounts.filter(acc => acc.id === currentUser.id) + me: false, + accounts: accounts.filter(acc => acc.id !== currentUser.id) } if (newReaction.count > 0) { set(status.emoji_reactions, reactionIndex, newReaction) } else { - set(status, 'emoji_reactions', status.emoji_reactions.filter(r => r.emoji !== emoji)) + set(status, 'emoji_reactions', status.emoji_reactions.filter(r => r.name !== emoji)) } }, updateStatusWithPoll (state, { id, poll }) { @@ -681,18 +691,22 @@ const statuses = { }, reactWithEmoji ({ rootState, dispatch, commit }, { id, emoji }) { const currentUser = rootState.users.currentUser + if (!currentUser) return + commit('addOwnReaction', { id, emoji, currentUser }) rootState.api.backendInteractor.reactWithEmoji({ id, emoji }).then( - status => { + ok => { dispatch('fetchEmojiReactionsBy', id) } ) }, unreactWithEmoji ({ rootState, dispatch, commit }, { id, emoji }) { const currentUser = rootState.users.currentUser + if (!currentUser) return + commit('removeOwnReaction', { id, emoji, currentUser }) rootState.api.backendInteractor.unreactWithEmoji({ id, emoji }).then( - status => { + ok => { dispatch('fetchEmojiReactionsBy', id) } ) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index b794fd58..20eaa9a0 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -74,9 +74,9 @@ const MASTODON_SEARCH_2 = `/api/v2/search` const MASTODON_USER_SEARCH_URL = '/api/v1/accounts/search' const MASTODON_DOMAIN_BLOCKS_URL = '/api/v1/domain_blocks' const MASTODON_STREAMING = '/api/v1/streaming' -const PLEROMA_EMOJI_REACTIONS_URL = id => `/api/v1/pleroma/statuses/${id}/emoji_reactions_by` -const PLEROMA_EMOJI_REACT_URL = id => `/api/v1/pleroma/statuses/${id}/react_with_emoji` -const PLEROMA_EMOJI_UNREACT_URL = id => `/api/v1/pleroma/statuses/${id}/unreact_with_emoji` +const PLEROMA_EMOJI_REACTIONS_URL = id => `/api/v1/pleroma/statuses/${id}/reactions` +const PLEROMA_EMOJI_REACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}` +const PLEROMA_EMOJI_UNREACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}` const oldfetch = window.fetch @@ -888,25 +888,27 @@ const fetchRebloggedByUsers = ({ id }) => { return promisedRequest({ url: MASTODON_STATUS_REBLOGGEDBY_URL(id) }).then((users) => users.map(parseUser)) } -const fetchEmojiReactions = ({ id }) => { - return promisedRequest({ url: PLEROMA_EMOJI_REACTIONS_URL(id) }) +const fetchEmojiReactions = ({ id, credentials }) => { + return promisedRequest({ url: PLEROMA_EMOJI_REACTIONS_URL(id), credentials }) + .then((reactions) => reactions.map(r => { + r.accounts = r.accounts.map(parseUser) + return r + })) } const reactWithEmoji = ({ id, emoji, credentials }) => { return promisedRequest({ - url: PLEROMA_EMOJI_REACT_URL(id), - method: 'POST', - credentials, - payload: { emoji } + url: PLEROMA_EMOJI_REACT_URL(id, emoji), + method: 'PUT', + credentials }).then(parseStatus) } const unreactWithEmoji = ({ id, emoji, credentials }) => { return promisedRequest({ - url: PLEROMA_EMOJI_UNREACT_URL(id), - method: 'POST', - credentials, - payload: { emoji } + url: PLEROMA_EMOJI_UNREACT_URL(id, emoji), + method: 'DELETE', + credentials }).then(parseStatus) } diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 0a8abbbd..84169a7b 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -354,6 +354,7 @@ export const parseNotification = (data) => { ? null : parseUser(data.target) output.from_profile = parseUser(data.account) + output.emoji = data.emoji } else { const parsedNotice = parseStatus(data.notice) output.type = data.ntype diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js index 860620fc..b17bd7bf 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -7,7 +7,8 @@ export const visibleTypes = store => ([ store.state.config.notificationVisibility.mentions && 'mention', store.state.config.notificationVisibility.repeats && 'repeat', store.state.config.notificationVisibility.follows && 'follow', - store.state.config.notificationVisibility.moves && 'move' + store.state.config.notificationVisibility.moves && 'move', + store.state.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction' ].filter(_ => _)) const sortById = (a, b) => { diff --git a/test/unit/specs/modules/statuses.spec.js b/test/unit/specs/modules/statuses.spec.js index e53aa388..fe42e85b 100644 --- a/test/unit/specs/modules/statuses.spec.js +++ b/test/unit/specs/modules/statuses.spec.js @@ -245,11 +245,12 @@ describe('Statuses module', () => { it('increments count in existing reaction', () => { const state = defaultState() const status = makeMockStatus({ id: '1' }) - status.emoji_reactions = [ { emoji: '😂', count: 1, accounts: [] } ] + status.emoji_reactions = [ { name: '😂', count: 1, accounts: [] } ] mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) mutations.addOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } }) expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(2) + expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(true) expect(state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id).to.eql('me') }) @@ -261,27 +262,29 @@ describe('Statuses module', () => { mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) mutations.addOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } }) expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(1) + expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(true) expect(state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id).to.eql('me') }) it('decreases count in existing reaction', () => { const state = defaultState() const status = makeMockStatus({ id: '1' }) - status.emoji_reactions = [ { emoji: '😂', count: 2, accounts: [{ id: 'me' }] } ] + status.emoji_reactions = [ { name: '😂', count: 2, accounts: [{ id: 'me' }] } ] mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: {} }) + mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } }) expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(1) + expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(false) expect(state.allStatusesObject['1'].emoji_reactions[0].accounts).to.eql([]) }) it('removes a reaction', () => { const state = defaultState() const status = makeMockStatus({ id: '1' }) - status.emoji_reactions = [{ emoji: '😂', count: 1, accounts: [{ id: 'me' }] }] + status.emoji_reactions = [{ name: '😂', count: 1, accounts: [{ id: 'me' }] }] mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: {} }) + mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } }) expect(state.allStatusesObject['1'].emoji_reactions.length).to.eql(0) }) }) -- cgit v1.2.3-70-g09d2 From 31225f5d142b51d52bed305f25a37288c9188062 Mon Sep 17 00:00:00 2001 From: Shpuld Shpludson Date: Fri, 28 Feb 2020 16:39:47 +0000 Subject: Fix/popover performance --- CHANGELOG.md | 1 + package.json | 1 - src/components/account_actions/account_actions.js | 4 +- src/components/account_actions/account_actions.vue | 21 +-- src/components/emoji_reactions/emoji_reactions.js | 11 +- src/components/emoji_reactions/emoji_reactions.vue | 33 +++-- src/components/extra_buttons/extra_buttons.js | 3 + src/components/extra_buttons/extra_buttons.vue | 14 +- .../moderation_tools/moderation_tools.js | 11 +- .../moderation_tools/moderation_tools.vue | 17 ++- src/components/popover/popover.js | 156 ++++++++++++++++++++ src/components/popover/popover.vue | 118 +++++++++++++++ src/components/popper/popper.scss | 164 --------------------- src/components/react_button/react_button.js | 23 +-- src/components/react_button/react_button.vue | 32 ++-- src/components/status/status.vue | 11 +- src/components/status_popover/status_popover.js | 12 +- src/components/status_popover/status_popover.vue | 64 +++----- src/main.js | 8 - yarn.lock | 19 --- 20 files changed, 393 insertions(+), 330 deletions(-) create mode 100644 src/components/popover/popover.js create mode 100644 src/components/popover/popover.vue delete mode 100644 src/components/popper/popper.scss (limited to 'src/components/status/status.vue') diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cdd604b..e77334b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Registration fixed - Deactivation of remote accounts from frontend - Fixed NSFW unhiding not working with videos when using one-click unhiding/displaying +- Improved performance of anything that uses popovers (most notably statuses) ## [1.1.7 and earlier] - 2019-12-14 ### Added diff --git a/package.json b/package.json index 5c7fa31e..542086b4 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "portal-vue": "^2.1.4", "sanitize-html": "^1.13.0", "v-click-outside": "^2.1.1", - "v-tooltip": "^2.0.2", "vue": "^2.5.13", "vue-chat-scroll": "^1.2.1", "vue-i18n": "^7.3.2", diff --git a/src/components/account_actions/account_actions.js b/src/components/account_actions/account_actions.js index d2153680..5d7ecf7e 100644 --- a/src/components/account_actions/account_actions.js +++ b/src/components/account_actions/account_actions.js @@ -1,4 +1,5 @@ import ProgressButton from '../progress_button/progress_button.vue' +import Popover from '../popover/popover.vue' const AccountActions = { props: [ @@ -8,7 +9,8 @@ const AccountActions = { return { } }, components: { - ProgressButton + ProgressButton, + Popover }, methods: { showRepeats () { diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue index d3235be1..483783cf 100644 --- a/src/components/account_actions/account_actions.vue +++ b/src/components/account_actions/account_actions.vue @@ -1,13 +1,13 @@