Merge branch 'subscription-feedback' into 'develop'

Improve UX after subscribing to account if web notifications are disabled

See merge request soapbox-pub/soapbox-fe!1608
environments/review-develop-3zknud/deployments/491
Justin 2022-07-07 01:01:58 +00:00
commit 725b00039d
4 zmienionych plików z 74 dodań i 8 usunięć

Wyświetl plik

@ -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'

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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<ITooltip> = ({
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 (
<ReachTooltip label={text}>
{children}
</ReachTooltip>
<React.Fragment>
{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
<Portal>
<div
data-reach-tooltip-arrow='true'
style={{
left:
triggerRect && triggerRect.left - 10 + triggerRect.width / 2 as any,
top: triggerRect && triggerRect.bottom + window.scrollY as any,
}}
/>
</Portal>
)}
<TooltipPopup
{...tooltip}
label={text}
aria-label={text}
position={centered}
/>
</React.Fragment>
);
};

Wyświetl plik

@ -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<boolean>(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'
/>
);