diff --git a/static/app-strings.json b/static/app-strings.json index 8aa9a27e5..94b0d12ac 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -1464,7 +1464,7 @@ "Your other content language": "Your other content language", "Search only in this language by default": "Search only in this language by default", "This link leads to an external website.": "This link leads to an external website.", - "Hold on, we are setting up your account": "Hold on, we are setting up your account", + "Please wait a bit, we are still getting your account ready. on, we are setting up your account": "Hold on, we are setting up your account", "No Content Found": "No Content Found", "Publish Something": "Publish Something", "Watch on lbry.tv": "Watch on lbry.tv", diff --git a/ui/component/notification/index.js b/ui/component/notification/index.js index 3ac1bbe26..f5750245b 100644 --- a/ui/component/notification/index.js +++ b/ui/component/notification/index.js @@ -1,7 +1,8 @@ import { connect } from 'react-redux'; -import { doSeeNotifications } from 'redux/actions/notifications'; +import { doSeeNotifications, doDeleteNotification } from 'redux/actions/notifications'; import Notification from './view'; export default connect(null, { doSeeNotifications, + doDeleteNotification, })(Notification); diff --git a/ui/component/notification/view.jsx b/ui/component/notification/view.jsx index 373e3f118..b6fa716ea 100644 --- a/ui/component/notification/view.jsx +++ b/ui/component/notification/view.jsx @@ -9,27 +9,31 @@ import Icon from 'component/common/icon'; import DateTime from 'component/dateTime'; import Button from 'component/button'; import ChannelThumbnail from 'component/channelThumbnail'; -import { MenuItem } from '@reach/menu-button'; import { formatLbryUrlForWeb } from 'util/url'; import { useHistory } from 'react-router'; import { parseURI } from 'lbry-redux'; import { PAGE_VIEW_QUERY, DISCUSSION_PAGE } from 'page/channel/view'; import FileThumbnail from 'component/fileThumbnail'; +import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button'; +import NotificationContentChannelMenu from 'component/notificationContentChannelMenu'; type Props = { notification: WebNotification, menuButton: boolean, children: any, doSeeNotifications: ([number]) => void, + doDeleteNotification: number => void, }; export default function Notification(props: Props) { - const { notification, menuButton = false, doSeeNotifications } = props; + const { notification, menuButton = false, doSeeNotifications, doDeleteNotification } = props; const { push } = useHistory(); const { notification_rule, notification_parameters, is_seen, id } = notification; const isCommentNotification = notification_rule === NOTIFICATIONS.NOTIFICATION_COMMENT || notification_rule === NOTIFICATIONS.NOTIFICATION_REPLY; const commentText = isCommentNotification && notification_parameters.dynamic.comment; + const channelUrl = + (notification_rule === NOTIFICATIONS.NEW_CONTENT && notification.notification_parameters.dynamic.channel_url) || ''; let notificationTarget; switch (notification_rule) { @@ -153,12 +157,29 @@ export default function Notification(props: Props) {
+ {!is_seen &&
+ +
+ + e.stopPropagation()}> + + + + doDeleteNotification(id)}> + + {__('Delete')} + + {notification_rule === NOTIFICATIONS.NEW_CONTENT && channelUrl ? ( + + ) : null} + + +
); diff --git a/ui/component/notificationContentChannelMenu/index.js b/ui/component/notificationContentChannelMenu/index.js new file mode 100644 index 000000000..920acb797 --- /dev/null +++ b/ui/component/notificationContentChannelMenu/index.js @@ -0,0 +1,14 @@ +import { connect } from 'react-redux'; +import { doChannelSubscribe } from 'redux/actions/subscriptions'; +import { doToast } from 'redux/actions/notifications'; +import { makeSelectNotificationsDisabled } from 'redux/selectors/subscriptions'; +import SubscribeButton from './view'; + +const select = (state, props) => ({ + notificationsDisabled: makeSelectNotificationsDisabled(props.uri)(state), +}); + +export default connect(select, { + doChannelSubscribe, + doToast, +})(SubscribeButton); diff --git a/ui/component/notificationContentChannelMenu/view.jsx b/ui/component/notificationContentChannelMenu/view.jsx new file mode 100644 index 000000000..5d7803345 --- /dev/null +++ b/ui/component/notificationContentChannelMenu/view.jsx @@ -0,0 +1,42 @@ +// @flow +import * as ICONS from 'constants/icons'; +import React from 'react'; +import { MenuItem } from '@reach/menu-button'; +import { parseURI } from 'lbry-redux'; +import Icon from 'component/common/icon'; + +type Props = { + uri: string, + notificationsDisabled: boolean, + doToast: ({ message: string }) => void, + doChannelSubscribe: Subscription => void, +}; + +export default function NotificationContentChannelMenu(props: Props) { + const { uri, notificationsDisabled, doToast, doChannelSubscribe } = props; + const { claimName } = parseURI(uri); + + function handleClick() { + doChannelSubscribe({ + uri, + channelName: claimName, + notificationsDisabled: !notificationsDisabled, + }); + + doToast({ + message: !notificationsDisabled + ? __('Notifications turned off for %channel%.', { channel: claimName }) + : __('Notifications turned on for %channel%.', { channel: claimName }), + }); + } + + return ( + +
+ + {notificationsDisabled ? __('Turn Back On') : __('Turn Off')} +
+ {claimName} +
+ ); +} diff --git a/ui/component/subscribeButton/view.jsx b/ui/component/subscribeButton/view.jsx index 9f5100157..ea0b2caff 100644 --- a/ui/component/subscribeButton/view.jsx +++ b/ui/component/subscribeButton/view.jsx @@ -94,7 +94,7 @@ export default function SubscribeButton(props: Props) { }); if (newNotificationsDisabled === false) { - doToast({ message: __('Notifications enabled for %channel%!', { channel: claimName }) }); + doToast({ message: __('Notifications turned on for %channel%!', { channel: claimName }) }); } }} /> diff --git a/ui/constants/action_types.js b/ui/constants/action_types.js index 9472da83b..849b18033 100644 --- a/ui/constants/action_types.js +++ b/ui/constants/action_types.js @@ -238,6 +238,9 @@ export const NOTIFICATION_READ_FAILED = 'NOTIFICATION_READ_FAILED'; export const NOTIFICATION_SEEN_STARTED = 'NOTIFICATION_SEEN_STARTED'; export const NOTIFICATION_SEEN_COMPLETED = 'NOTIFICATION_SEEN_COMPLETED'; export const NOTIFICATION_SEEN_FAILED = 'NOTIFICATION_SEEN_FAILED'; +export const NOTIFICATION_DELETE_STARTED = 'NOTIFICATION_DELETE_STARTED'; +export const NOTIFICATION_DELETE_COMPLETED = 'NOTIFICATION_DELETE_COMPLETED'; +export const NOTIFICATION_DELETE_FAILED = 'NOTIFICATION_DELETE_FAILED'; export const CREATE_TOAST = 'CREATE_TOAST'; export const DISMISS_TOAST = 'DISMISS_TOAST'; export const CREATE_ERROR = 'CREATE_ERROR'; diff --git a/ui/redux/actions/app.js b/ui/redux/actions/app.js index 25fea04b8..e4e9f01ba 100644 --- a/ui/redux/actions/app.js +++ b/ui/redux/actions/app.js @@ -338,7 +338,7 @@ export function doAlertWaitingForSync() { return dispatch => dispatch( doToast({ - message: __('Hold on, we are setting up your account'), + message: __('Please wait a bit, we are still getting your account ready.'), isError: false, }) ); diff --git a/ui/redux/actions/notifications.js b/ui/redux/actions/notifications.js index 00e161af6..bee5ea5ae 100644 --- a/ui/redux/actions/notifications.js +++ b/ui/redux/actions/notifications.js @@ -126,3 +126,15 @@ export function doSeeAllNotifications() { dispatch(doSeeNotifications(unSeenNotifications)); }; } + +export function doDeleteNotification(notificationId: number) { + return (dispatch: Dispatch<*>) => { + Lbryio.call('notification', 'delete', { notification_ids: notificationId }) + .then(() => { + dispatch({ type: ACTIONS.NOTIFICATION_DELETE_COMPLETED, data: { notificationId } }); + }) + .catch(() => { + dispatch(doToast({ isError: true, message: __('Unable to delete this right now. Please try again later.') })); + }); + }; +} diff --git a/ui/redux/reducers/notifications.js b/ui/redux/reducers/notifications.js index fca02c474..6c218221b 100644 --- a/ui/redux/reducers/notifications.js +++ b/ui/redux/reducers/notifications.js @@ -82,6 +82,18 @@ export default handleActions( notifications: newNotifications, }; }, + [ACTIONS.NOTIFICATION_DELETE_COMPLETED]: (state, action) => { + const { notifications } = state; + const { notificationId } = action.data; + const newNotifications = notifications.filter(notification => { + return notification.id !== notificationId; + }); + + return { + ...state, + notifications: newNotifications, + }; + }, // Errors [ACTIONS.CREATE_ERROR]: (state: NotificationState, action: DoError) => { diff --git a/ui/scss/component/_notification.scss b/ui/scss/component/_notification.scss index b79f3a019..9f1ed7939 100644 --- a/ui/scss/component/_notification.scss +++ b/ui/scss/component/_notification.scss @@ -34,13 +34,14 @@ $contentMaxWidth: 35rem; @media (min-width: $breakpoint-small) { align-items: center; + margin-left: var(--spacing-m); } } .notification__wrapper { width: 100%; display: flex; - padding: var(--spacing-m); + padding: var(--spacing-m) 0; .channel-thumbnail { @include handleChannelGif(3rem); @@ -111,7 +112,7 @@ $contentMaxWidth: 35rem; } .notification__text-wrapper { - max-width: calc(#{$contentMaxWidth} - (#{$thumbnailWidth} * 16 / 9) - var(--spacing-m)); //25rem; + max-width: calc(#{$contentMaxWidth} - (#{$thumbnailWidth} * 16 / 9) - var(--spacing-m)); } .notification__title { @@ -179,6 +180,15 @@ $contentMaxWidth: 35rem; .notification__extra { display: flex; align-items: center; + justify-content: flex-end; + margin-right: var(--spacing-m); + flex-direction: row-reverse; + + @media (min-width: $breakpoint-small) { + margin-left: var(--spacing-s); + margin-top: 0; + flex-direction: row; + } } .notification__mark-seen { @@ -193,3 +203,19 @@ $contentMaxWidth: 35rem; margin-top: 0; } } + +.notification__menu { + margin-right: var(--spacing-m); + + .icon { + stroke: var(--color-text-help); + } + + button { + border-radius: var(--border-radius); + + &:focus { + @include linkFocus; + } + } +}