From e68c534e6c27436d24a9ae51c210c4fa2b1b37ae Mon Sep 17 00:00:00 2001
From: Jason <84899178+jasonhenriquez@users.noreply.github.com>
Date: Wed, 22 Nov 2023 17:19:11 +0000
Subject: [PATCH] A new way to subscribe (#4238)
* Implement styling for new subscribe button
* Implement dropdown element and styling
* Implements remaining styling and logic
* Correct use of aria-selected in FT
Note: was able to add aria-controls to ft-profile-selector because it keeps the hidden dropdown in the DOM. The same is not true of the ft-icon-button or ft-subscribe-button. Main point: aria-expanded should go on the button opening the dropdown, not the dropdown itself.
* Resolve subscribe button staying upon bug
* Remove unneeded vendor-specific transition prefixes
See discussion here: https://www.web-plus-plus.com/Articles/css-transition-moz-and-webkit-vs-css3
---
.../ft-channel-bubble/ft-channel-bubble.css | 5 -
.../ft-icon-button/ft-icon-button.vue | 3 +-
src/renderer/components/ft-input/ft-input.css | 8 -
.../ft-profile-bubble/ft-profile-bubble.css | 5 -
.../ft-profile-selector.css | 5 -
.../ft-profile-selector.js | 4 +
.../ft-profile-selector.vue | 5 +-
.../ft-subscribe-button.css | 8 -
.../ft-subscribe-button.js | 89 +++++++++--
.../ft-subscribe-button.scss | 141 ++++++++++++++++++
.../ft-subscribe-button.vue | 87 ++++++++++-
.../side-nav-more-options.css | 4 -
src/renderer/components/side-nav/side-nav.css | 4 -
src/renderer/main.js | 2 +
src/renderer/views/Channel/Channel.css | 1 +
.../SubscribedChannels/SubscribedChannels.vue | 3 +-
static/locales/en-US.yaml | 2 +
17 files changed, 308 insertions(+), 68 deletions(-)
delete mode 100644 src/renderer/components/ft-subscribe-button/ft-subscribe-button.css
create mode 100644 src/renderer/components/ft-subscribe-button/ft-subscribe-button.scss
diff --git a/src/renderer/components/ft-channel-bubble/ft-channel-bubble.css b/src/renderer/components/ft-channel-bubble/ft-channel-bubble.css
index fcb7b96e5..1a4878d64 100644
--- a/src/renderer/components/ft-channel-bubble/ft-channel-bubble.css
+++ b/src/renderer/components/ft-channel-bubble/ft-channel-bubble.css
@@ -11,17 +11,12 @@
overflow: hidden;
color: inherit;
text-decoration: none;
- -webkit-transition: background 0.2s ease-out;
- -moz-transition: background 0.2s ease-out;
- -o-transition: background 0.2s ease-out;
transition: background 0.2s ease-out;
}
.bubblePadding:hover {
background-color: var(--side-nav-hover-color);
color: var(--side-nav-hover-text-color);
- -moz-transition: background 0.2s ease-in;
- -o-transition: background 0.2s ease-in;
transition: background 0.2s ease-in;
}
diff --git a/src/renderer/components/ft-icon-button/ft-icon-button.vue b/src/renderer/components/ft-icon-button/ft-icon-button.vue
index b1760e528..c699f4392 100644
--- a/src/renderer/components/ft-icon-button/ft-icon-button.vue
+++ b/src/renderer/components/ft-icon-button/ft-icon-button.vue
@@ -14,6 +14,7 @@
}"
tabindex="0"
role="button"
+ :aria-expanded="dropdownShown"
@click="handleIconClick"
@mousedown="handleIconMouseDown"
@keydown.enter.prevent="handleIconClick"
@@ -33,7 +34,6 @@
v-if="dropdownOptions.length > 0"
class="list"
role="listbox"
- :aria-expanded="dropdownShown"
>
{
+ return profile?.name?.length > 0 ? Array.from(profile.name)[0].toUpperCase() : ''
+ })
+ },
+
profileList: function () {
return this.$store.getters.getProfileList
},
+ /* sort by 'All Channels' -> active profile -> unsubscribed channels -> subscribed channels */
+ profileDisplayList: function () {
+ const mainProfileAndActiveProfile = [this.profileList[0]]
+ if (this.activeProfile._id !== MAIN_PROFILE_ID) {
+ mainProfileAndActiveProfile.push(this.activeProfile)
+ }
+
+ return [
+ ...mainProfileAndActiveProfile,
+ ...this.profileList.filter((profile, i) =>
+ i !== 0 && !this.isActiveProfile(profile) && !this.isProfileSubscribed(profile)),
+ ...this.profileList.filter((profile, i) =>
+ i !== 0 && !this.isActiveProfile(profile) && this.isProfileSubscribed(profile))
+ ]
+ },
+
activeProfile: function () {
return this.$store.getters.getActiveProfile
},
subscriptionInfo: function () {
- return this.activeProfile.subscriptions.find((channel) => {
- return channel.id === this.channelId
- }) ?? null
- },
-
- isSubscribed: function () {
- return this.subscriptionInfo !== null
+ return this.subscriptionInfoForProfile(this.activeProfile)
},
hideChannelSubscriptions: function () {
@@ -55,23 +80,27 @@ export default defineComponent({
},
subscribedText: function () {
- let subscribedValue = (this.isSubscribed ? this.$t('Channel.Unsubscribe') : this.$t('Channel.Subscribe')).toUpperCase()
+ let subscribedValue = (this.isProfileSubscribed(this.activeProfile) ? this.$t('Channel.Unsubscribe') : this.$t('Channel.Subscribe')).toUpperCase()
if (this.subscriptionCountText !== '' && !this.hideChannelSubscriptions) {
subscribedValue += ' ' + this.subscriptionCountText
}
return subscribedValue
+ },
+
+ isProfileDropdownEnabled: function () {
+ return !this.hideProfileDropdownToggle && this.profileList.length > 1
}
},
methods: {
- handleSubscription: function () {
+ handleSubscription: function (profile = this.activeProfile) {
if (this.channelId === '') {
return
}
- const currentProfile = deepCopy(this.activeProfile)
+ const currentProfile = deepCopy(profile)
- if (this.isSubscribed) {
+ if (this.isProfileSubscribed(profile)) {
currentProfile.subscriptions = currentProfile.subscriptions.filter((channel) => {
return channel.id !== this.channelId
})
@@ -79,16 +108,16 @@ export default defineComponent({
this.updateProfile(currentProfile)
showToast(this.$t('Channel.Channel has been removed from your subscriptions'))
- if (this.activeProfile._id === MAIN_PROFILE_ID) {
+ if (profile._id === MAIN_PROFILE_ID) {
// Check if a subscription exists in a different profile.
// Remove from there as well.
let duplicateSubscriptions = 0
- this.profileList.forEach((profile) => {
- if (profile._id === MAIN_PROFILE_ID) {
+ this.profileList.forEach((profileInList) => {
+ if (profileInList._id === MAIN_PROFILE_ID) {
return
}
- duplicateSubscriptions += this.unsubscribe(profile, this.channelId)
+ duplicateSubscriptions += this.unsubscribe(profileInList, this.channelId)
})
if (duplicateSubscriptions > 0) {
@@ -107,7 +136,7 @@ export default defineComponent({
this.updateProfile(currentProfile)
showToast(this.$t('Channel.Added channel to your subscriptions'))
- if (this.activeProfile._id !== MAIN_PROFILE_ID) {
+ if (profile._id !== MAIN_PROFILE_ID) {
const primaryProfile = deepCopy(this.profileList.find(prof => {
return prof._id === MAIN_PROFILE_ID
}))
@@ -122,6 +151,34 @@ export default defineComponent({
}
}
}
+
+ if (this.isProfileDropdownEnabled && !this.isProfileDropdownOpen) {
+ this.toggleProfileDropdown()
+ }
+ },
+
+ handleProfileDropdownFocusOut: function () {
+ if (!this.$refs.subscribeButton.matches(':focus-within')) {
+ this.isProfileDropdownOpen = false
+ }
+ },
+
+ toggleProfileDropdown: function() {
+ this.isProfileDropdownOpen = !this.isProfileDropdownOpen
+ },
+
+ isActiveProfile: function (profile) {
+ return profile._id === this.activeProfile._id
+ },
+
+ subscriptionInfoForProfile: function (profile) {
+ return profile.subscriptions.find((channel) => {
+ return channel.id === this.channelId
+ }) ?? null
+ },
+
+ isProfileSubscribed: function (profile) {
+ return this.subscriptionInfoForProfile(profile) !== null
},
unsubscribe: function(profile, channelId) {
diff --git a/src/renderer/components/ft-subscribe-button/ft-subscribe-button.scss b/src/renderer/components/ft-subscribe-button/ft-subscribe-button.scss
new file mode 100644
index 000000000..1d8667fa1
--- /dev/null
+++ b/src/renderer/components/ft-subscribe-button/ft-subscribe-button.scss
@@ -0,0 +1,141 @@
+.buttonList {
+ margin: 5px;
+ margin-block-end: 10px;
+ border-radius: 4px;
+ block-size: fit-content;
+ box-shadow: 0px 1px 2px rgb(0 0 0 / 50%);
+ display: flex;
+ flex-wrap: nowrap;
+ /* addresses odd clipping behavior when adjusting window size */
+ background-color: var(--primary-color);
+}
+
+.ftSubscribeButton {
+ position: relative;
+ text-align: start;
+}
+
+/* Ensures style here overrides style of .btn */
+.subscribeButton.btn {
+ min-inline-size: 150px;
+ white-space: initial;
+}
+
+.subscribeButton.btn, .profileDropdownToggle.btn {
+ align-self: center;
+ margin-block: 0;
+ margin-inline: 0;
+}
+
+.dropdownOpened {
+ .subscribeButton, .profileDropdownToggle {
+ border-end-start-radius: 0;
+ border-end-end-radius: 0;
+ }
+}
+
+.profileDropdownToggle.btn {
+ border-inline-start: none !important;
+ border-start-start-radius: 0;
+ border-end-start-radius: 0;
+ display: inline-block;
+ min-inline-size: 1em;
+ padding-inline: 10px;
+ box-sizing: content-box;
+}
+
+.hasProfileDropdownToggle {
+ .subscribeButton.btn {
+ min-inline-size: 100px;
+ padding-inline: 5px;
+ border-inline-end: 2px solid var(--primary-color-active) !important;
+ border-start-end-radius: 0;
+ border-end-end-radius: 0;
+ box-sizing: content-box;
+ }
+}
+
+.hasProfileDropdownToggle > .subscribeButton.btn, .profileDropdownToggle.btn {
+ padding-block: 5px;
+ padding-inline: 6px;
+ box-shadow: none;
+ flex: auto;
+ block-size: 2em;
+}
+
+.profileDropdown {
+ background-color: var(--side-nav-color);
+ box-shadow: 0 1px 2px rgb(0 0 0 / 50%);
+ color: var(--secondary-text-color);
+ display: inline;
+ font-size: 12px;
+ max-block-size: 200px;
+ margin-block: -10px 0;
+ margin-inline: 5px 0;
+ overflow-y: scroll;
+ position: absolute;
+ text-align: center;
+ -webkit-user-select: none;
+ user-select: none;
+ z-index: 3;
+ // accounts for parent's left and right margins
+ inline-size: calc(100% - 10px);
+
+ .profileList {
+ list-style-type: none;
+ margin: 0;
+ padding-inline: 0;
+ }
+
+ .profile {
+ cursor: pointer;
+ display: flex;
+ gap: 0.5em;
+ padding-inline-start: 0.5em;
+ block-size: 50px;
+ align-items: center;
+ transition: background 0.2s ease-out;
+
+ &:hover {
+ background-color: var(--side-nav-hover-color);
+ color: var(--side-nav-hover-text-color);
+ transition: background 0.2s ease-in;
+ }
+
+ .colorOption {
+ inline-size: 40px;
+ block-size: 40px;
+ cursor: pointer;
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ flex-shrink: 0;
+ border-radius: 50%;
+ -webkit-border-radius: 50%;
+ }
+
+ .initial {
+ font-size: 20px;
+ line-height: 1em;
+ text-align: center;
+ user-select: none;
+ -webkit-user-select: none;
+ }
+
+ .profileName {
+ padding-inline-end: 1em;
+ text-align: start;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ &.subscribed {
+ background-color: var(--primary-color);
+
+ .profileName {
+ color: var(--text-with-main-color);
+ }
+ }
+ }
+}
diff --git a/src/renderer/components/ft-subscribe-button/ft-subscribe-button.vue b/src/renderer/components/ft-subscribe-button/ft-subscribe-button.vue
index 11bf54e7e..7aded099d 100644
--- a/src/renderer/components/ft-subscribe-button/ft-subscribe-button.vue
+++ b/src/renderer/components/ft-subscribe-button/ft-subscribe-button.vue
@@ -1,12 +1,83 @@
-
+
-
+
diff --git a/src/renderer/components/side-nav-more-options/side-nav-more-options.css b/src/renderer/components/side-nav-more-options/side-nav-more-options.css
index 6da86c064..4945a84ec 100644
--- a/src/renderer/components/side-nav-more-options/side-nav-more-options.css
+++ b/src/renderer/components/side-nav-more-options/side-nav-more-options.css
@@ -14,16 +14,12 @@
.navOption:hover {
background-color: var(--side-nav-hover-color);
color: var(--side-nav-hover-text-color);
- -moz-transition: background 0.2s ease-in;
- -o-transition: background 0.2s ease-in;
transition: background 0.2s ease-in;
}
.navOption:active {
background-color: var(--side-nav-active-color);
color: var(--side-nav-active-text-color);
- -moz-transition: background 0.2s ease-in;
- -o-transition: background 0.2s ease-in;
transition: background 0.2s ease-in;
}
diff --git a/src/renderer/components/side-nav/side-nav.css b/src/renderer/components/side-nav/side-nav.css
index de36a3f62..a9a869114 100644
--- a/src/renderer/components/side-nav/side-nav.css
+++ b/src/renderer/components/side-nav/side-nav.css
@@ -53,16 +53,12 @@
.navOption:hover, .navChannel:hover {
background-color: var(--side-nav-hover-color);
color: var(--side-nav-hover-text-color);
- -moz-transition: background 0.2s ease-in;
- -o-transition: background 0.2s ease-in;
transition: background 0.2s ease-in;
}
.navOption:active, .navChannel:active {
background-color: var(--side-nav-active-color);
color: var(--side-nav-active-text-color);
- -moz-transition: background 0.2s ease-in;
- -o-transition: background 0.2s ease-in;
transition: background 0.2s ease-in;
}
diff --git a/src/renderer/main.js b/src/renderer/main.js
index c8345edf4..d522cf0bd 100644
--- a/src/renderer/main.js
+++ b/src/renderer/main.js
@@ -9,6 +9,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'
// to avoid code conflict and duplicate entries
import {
faAngleDown,
+ faAngleUp,
faArrowDown,
faArrowLeft,
faArrowRight,
@@ -81,6 +82,7 @@ Vue.config.productionTip = process.env.NODE_ENV === 'development'
library.add(
// solid icons
faAngleDown,
+ faAngleUp,
faArrowDown,
faArrowLeft,
faArrowRight,
diff --git a/src/renderer/views/Channel/Channel.css b/src/renderer/views/Channel/Channel.css
index 28567c032..3852c84b5 100644
--- a/src/renderer/views/Channel/Channel.css
+++ b/src/renderer/views/Channel/Channel.css
@@ -69,6 +69,7 @@
display: flex;
gap: 30px;
justify-content: space-between;
+ align-items: center;
}
.subscribeButton {
diff --git a/src/renderer/views/SubscribedChannels/SubscribedChannels.vue b/src/renderer/views/SubscribedChannels/SubscribedChannels.vue
index 73bcb9af2..88b8ad36c 100644
--- a/src/renderer/views/SubscribedChannels/SubscribedChannels.vue
+++ b/src/renderer/views/SubscribedChannels/SubscribedChannels.vue
@@ -26,7 +26,7 @@