From 3f744554844f7ec0183474c35f9edc6abbbed0c0 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 6 Jul 2022 14:19:42 -0400 Subject: [PATCH 1/3] Improve UI of IconButton when disabled --- app/soapbox/components/ui/icon-button/icon-button.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/soapbox/components/ui/icon-button/icon-button.tsx b/app/soapbox/components/ui/icon-button/icon-button.tsx index f74f2ca04..aa317b6ea 100644 --- a/app/soapbox/components/ui/icon-button/icon-button.tsx +++ b/app/soapbox/components/ui/icon-button/icon-button.tsx @@ -25,6 +25,7 @@ const IconButton = React.forwardRef((props: IIconButton, ref: React.ForwardedRef type='button' className={classNames('flex items-center space-x-2 p-1 rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2 dark:ring-offset-0 focus:ring-primary-500', { 'bg-white dark:bg-transparent': !transparent, + 'opacity-50': filteredProps.disabled, }, className)} {...filteredProps} data-testid='icon-button' From b2f2bcb8331f13f0f8acfe8825ddc8789d9ed11d Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 6 Jul 2022 14:19:51 -0400 Subject: [PATCH 2/3] Center tooltips and add arrow --- app/soapbox/components/ui/tooltip/tooltip.css | 4 ++ app/soapbox/components/ui/tooltip/tooltip.tsx | 49 +++++++++++++++++-- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/app/soapbox/components/ui/tooltip/tooltip.css b/app/soapbox/components/ui/tooltip/tooltip.css index 6d439ddb3..9e4addd1f 100644 --- a/app/soapbox/components/ui/tooltip/tooltip.css +++ b/app/soapbox/components/ui/tooltip/tooltip.css @@ -6,3 +6,7 @@ @apply pointer-events-none absolute px-2.5 py-1.5 rounded shadow whitespace-nowrap text-xs font-medium bg-gray-800 text-white; z-index: 100; } + +[data-reach-tooltip-arrow] { + @apply absolute z-50 w-0 h-0 border-l-8 border-solid border-l-transparent border-r-8 border-r-transparent border-b-8 border-b-gray-800; +} diff --git a/app/soapbox/components/ui/tooltip/tooltip.tsx b/app/soapbox/components/ui/tooltip/tooltip.tsx index 4efa4749f..e04221200 100644 --- a/app/soapbox/components/ui/tooltip/tooltip.tsx +++ b/app/soapbox/components/ui/tooltip/tooltip.tsx @@ -1,4 +1,5 @@ -import { default as ReachTooltip } from '@reach/tooltip'; +import Portal from '@reach/portal'; +import { TooltipPopup, useTooltip } from '@reach/tooltip'; import React from 'react'; import './tooltip.css'; @@ -8,15 +9,55 @@ interface ITooltip { text: string, } +const centered = (triggerRect: any, tooltipRect: any) => { + const triggerCenter = triggerRect.left + triggerRect.width / 2; + const left = triggerCenter - tooltipRect.width / 2; + const maxLeft = window.innerWidth - tooltipRect.width - 2; + return { + left: Math.min(Math.max(2, left), maxLeft) + window.scrollX, + top: triggerRect.bottom + 8 + window.scrollY, + }; +}; + /** Hoverable tooltip element. */ const Tooltip: React.FC = ({ children, text, }) => { + // get the props from useTooltip + const [trigger, tooltip] = useTooltip(); + + // destructure off what we need to position the triangle + const { isVisible, triggerRect } = tooltip; + return ( - - {children} - + + {React.cloneElement(children as any, trigger)} + + {isVisible && ( + // The Triangle. We position it relative to the trigger, not the popup + // so that collisions don't have a triangle pointing off to nowhere. + // Using a Portal may seem a little extreme, but we can keep the + // positioning logic simpler here instead of needing to consider + // the popup's position relative to the trigger and collisions + +
+ + )} + + ); }; From 3e8ebb166147fe564b58ed9489b11f0c64290a65 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 6 Jul 2022 14:20:20 -0400 Subject: [PATCH 3/3] Improve UX after subscribing to account --- .../ui/components/subscription-button.tsx | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/app/soapbox/features/ui/components/subscription-button.tsx b/app/soapbox/features/ui/components/subscription-button.tsx index bbe01f3ba..22eb637d7 100644 --- a/app/soapbox/features/ui/components/subscription-button.tsx +++ b/app/soapbox/features/ui/components/subscription-button.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { @@ -16,6 +16,7 @@ const messages = defineMessages({ subscribe: { id: 'account.subscribe', defaultMessage: 'Subscribe to notifications from @{name}' }, unsubscribe: { id: 'account.unsubscribe', defaultMessage: 'Unsubscribe to notifications from @{name}' }, subscribeSuccess: { id: 'account.subscribe.success', defaultMessage: 'You have subscribed to this account.' }, + subscribeSuccessNotice: { id: 'account.subscribe.successNotice', defaultMessage: 'You have subscribed to this account, but your web notifications are disabled. Please enable them to receive notifications from @{name}.' }, unsubscribeSuccess: { id: 'account.unsubscribe.success', defaultMessage: 'You have unsubscribed from this account.' }, subscribeFailure: { id: 'account.subscribe.failure', defaultMessage: 'An error occurred trying to subscribed to this account.' }, unsubscribeFailure: { id: 'account.unsubscribe.failure', defaultMessage: 'An error occurred trying to unsubscribed to this account.' }, @@ -30,6 +31,14 @@ const SubscriptionButton = ({ account }: ISubscriptionButton) => { const features = useFeatures(); const intl = useIntl(); + const [hasWebNotificationsEnabled, setWebNotificationsEnabled] = useState(true); + + const checkWebNotifications = () => { + Notification.requestPermission() + .then((value) => setWebNotificationsEnabled(value === 'granted')) + .catch(() => null); + }; + const isFollowing = account.relationship?.following; const isRequested = account.relationship?.requested; const isSubscribed = features.accountNotifies ? @@ -39,8 +48,13 @@ const SubscriptionButton = ({ account }: ISubscriptionButton) => { intl.formatMessage(messages.unsubscribe, { name: account.get('username') }) : intl.formatMessage(messages.subscribe, { name: account.get('username') }); - const onSubscribeSuccess = () => - dispatch(snackbar.success(intl.formatMessage(messages.subscribeSuccess))); + const onSubscribeSuccess = () => { + if (hasWebNotificationsEnabled) { + dispatch(snackbar.success(intl.formatMessage(messages.subscribeSuccess))); + } else { + dispatch(snackbar.info(intl.formatMessage(messages.subscribeSuccessNotice, { name: account.get('username') }))); + } + }; const onSubscribeFailure = () => dispatch(snackbar.error(intl.formatMessage(messages.subscribeFailure))); @@ -83,6 +97,12 @@ const SubscriptionButton = ({ account }: ISubscriptionButton) => { } }; + useEffect(() => { + if (features.accountSubscriptions || features.accountNotifies) { + checkWebNotifications(); + } + }, []); + if (!features.accountSubscriptions && !features.accountNotifies) { return null; } @@ -93,7 +113,7 @@ const SubscriptionButton = ({ account }: ISubscriptionButton) => { src={isSubscribed ? require('@tabler/icons/icons/bell-ringing.svg') : require('@tabler/icons/icons/bell.svg')} onClick={handleToggle} title={title} - className='text-primary-700 bg-primary-100 dark:!bg-slate-700 dark:!text-white hover:bg-primary-200 p-2' + className='text-primary-700 bg-primary-100 dark:!bg-slate-700 dark:!text-white hover:bg-primary-200 disabled:hover:bg-primary-100 p-2' iconClassName='w-5 h-5' /> );