Notification menu (#1652)
* Save notification menu prototype * Add dynamic links * Add timestamps * Mark as seen on click * Fix guest mode * Fix discussion links & channel thumbnails * Adjust some details * Adjust theme * Replaxe Menu with MuiMenu * Fix Mui behavior & transitions * Adjust Mui menu behavior * Adjust some padding * Fix read & see * Clean code * Adjust border on top notification * Add case for comment replies * Save * Make alignment pixel perfect * Clean code * Adjust gif avatars, stickers & tips * Add delete function * Add delete icon hover effect * Add outline to delete icon * Fix seeNotification call * Add case for empty notification listä
This commit is contained in:
parent
70695dfd3f
commit
54ee4ee94a
5 changed files with 506 additions and 58 deletions
|
@ -1,14 +1,26 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectUnseenNotificationCount } from 'redux/selectors/notifications';
|
import { selectNotifications, selectUnseenNotificationCount } from 'redux/selectors/notifications';
|
||||||
import { doSeeAllNotifications } from 'redux/actions/notifications';
|
import {
|
||||||
import { selectUser } from 'redux/selectors/user';
|
doReadNotifications,
|
||||||
|
doSeeNotifications,
|
||||||
|
doDeleteNotification,
|
||||||
|
doSeeAllNotifications,
|
||||||
|
} from 'redux/actions/notifications';
|
||||||
|
import { selectUser, selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
import NotificationHeaderButton from './view';
|
import NotificationHeaderButton from './view';
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
|
notifications: selectNotifications(state),
|
||||||
unseenCount: selectUnseenNotificationCount(state),
|
unseenCount: selectUnseenNotificationCount(state),
|
||||||
user: selectUser(state),
|
user: selectUser(state),
|
||||||
|
authenticated: selectUserVerifiedEmail(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, {
|
const perform = (dispatch, ownProps) => ({
|
||||||
doSeeAllNotifications,
|
readNotification: ([id]) => dispatch(doReadNotifications([id])),
|
||||||
})(NotificationHeaderButton);
|
seeNotification: ([id]) => dispatch(doSeeNotifications([id])),
|
||||||
|
deleteNotification: (id) => dispatch(doDeleteNotification(id)),
|
||||||
|
doSeeAllNotifications: doSeeAllNotifications,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(NotificationHeaderButton);
|
||||||
|
|
|
@ -5,37 +5,229 @@ import { ENABLE_UI_NOTIFICATIONS } from 'config';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import Button from 'component/button';
|
|
||||||
import Icon from 'component/common/icon';
|
import Icon from 'component/common/icon';
|
||||||
import NotificationBubble from 'component/notificationBubble';
|
import NotificationBubble from 'component/notificationBubble';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Tooltip from 'component/common/tooltip';
|
import Tooltip from 'component/common/tooltip';
|
||||||
|
import { formatLbryUrlForWeb } from 'util/url';
|
||||||
|
import Notification from 'component/notification';
|
||||||
|
import DateTime from 'component/dateTime';
|
||||||
|
import ChannelThumbnail from 'component/channelThumbnail';
|
||||||
|
import { Menu as MuiMenu } from '@mui/material';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import ClickAwayListener from '@mui/material/ClickAwayListener';
|
||||||
|
import { RULE } from 'constants/notifications';
|
||||||
|
import UriIndicator from 'component/uriIndicator';
|
||||||
|
import { generateNotificationTitle } from '../notification/helpers/title';
|
||||||
|
import { generateNotificationText } from '../notification/helpers/text';
|
||||||
|
import { parseURI } from 'util/lbryURI';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
notifications: Array<Notification>,
|
||||||
unseenCount: number,
|
unseenCount: number,
|
||||||
user: ?User,
|
user: ?User,
|
||||||
|
authenticated: boolean,
|
||||||
|
readNotification: (Array<number>) => void,
|
||||||
|
seeNotification: (Array<number>) => void,
|
||||||
|
deleteNotification: (number) => void,
|
||||||
doSeeAllNotifications: () => void,
|
doSeeAllNotifications: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function NotificationHeaderButton(props: Props) {
|
export default function NotificationHeaderButton(props: Props) {
|
||||||
const { unseenCount, user, doSeeAllNotifications } = props;
|
const {
|
||||||
|
notifications,
|
||||||
|
unseenCount,
|
||||||
|
user,
|
||||||
|
authenticated,
|
||||||
|
readNotification,
|
||||||
|
seeNotification,
|
||||||
|
deleteNotification,
|
||||||
|
doSeeAllNotifications,
|
||||||
|
} = props;
|
||||||
|
const list = notifications.slice(0, 5);
|
||||||
|
|
||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
const notificationsEnabled = ENABLE_UI_NOTIFICATIONS || (user && user.experimental_ui);
|
const notificationsEnabled = authenticated && (ENABLE_UI_NOTIFICATIONS || (user && user.experimental_ui));
|
||||||
|
|
||||||
|
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||||
|
const [clicked, setClicked] = React.useState(false);
|
||||||
|
const open = Boolean(anchorEl);
|
||||||
|
const handleClick = (event) => setAnchorEl(!anchorEl ? event.currentTarget : null);
|
||||||
|
const handleClose = () => setAnchorEl(null);
|
||||||
|
|
||||||
|
const menuProps = {
|
||||||
|
id: 'notification-menu',
|
||||||
|
anchorEl,
|
||||||
|
open,
|
||||||
|
onClose: handleClose,
|
||||||
|
MenuListProps: {
|
||||||
|
'aria-labelledby': 'basic-button',
|
||||||
|
sx: { padding: 'var(--spacing-xs)' },
|
||||||
|
},
|
||||||
|
className: 'menu__list--header menu__list--notifications',
|
||||||
|
sx: { 'z-index': 2 },
|
||||||
|
PaperProps: { className: 'MuiMenu-list--paper' },
|
||||||
|
disableScrollLock: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickAway = () => {
|
||||||
|
if (!clicked) {
|
||||||
|
if (open) setClicked(true);
|
||||||
|
} else {
|
||||||
|
setAnchorEl(null);
|
||||||
|
setClicked(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const creatorIcon = (channelUrl, channelThumbnail) => (
|
||||||
|
<UriIndicator uri={channelUrl} link showAtSign channelInfo={{ uri: channelUrl, name: '' }}>
|
||||||
|
<ChannelThumbnail small thumbnailPreview={channelThumbnail} uri={channelThumbnail ? undefined : channelUrl} />
|
||||||
|
</UriIndicator>
|
||||||
|
);
|
||||||
|
|
||||||
function handleMenuClick() {
|
function handleMenuClick() {
|
||||||
if (unseenCount > 0) doSeeAllNotifications();
|
if (unseenCount > 0) doSeeAllNotifications();
|
||||||
push(`/$/${PAGES.NOTIFICATIONS}`);
|
push(`/$/${PAGES.NOTIFICATIONS}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleNotificationDelete(e, id) {
|
||||||
|
e.stopPropagation();
|
||||||
|
deleteNotification(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!open) setClicked(false);
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
if (!notificationsEnabled) return null;
|
if (!notificationsEnabled) return null;
|
||||||
|
|
||||||
|
function handleNotificationClick(notification) {
|
||||||
|
const { id, notification_parameters, is_read } = notification;
|
||||||
|
|
||||||
|
if (!is_read) {
|
||||||
|
seeNotification([id]);
|
||||||
|
readNotification([id]);
|
||||||
|
}
|
||||||
|
let notificationLink = formatLbryUrlForWeb(notification_parameters.device.target);
|
||||||
|
if (notification_parameters.dynamic.hash) {
|
||||||
|
notificationLink += '?lc=' + notification_parameters.dynamic.hash + '&view=discussion';
|
||||||
|
}
|
||||||
|
push(notificationLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
function menuEntry(notification) {
|
||||||
|
const { id, active_at, notification_rule, notification_parameters, is_read, type } = notification;
|
||||||
|
|
||||||
|
let channelUrl;
|
||||||
|
let icon;
|
||||||
|
switch (notification_rule) {
|
||||||
|
case RULE.CREATOR_SUBSCRIBER:
|
||||||
|
icon = <Icon icon={ICONS.SUBSCRIBE} sectionIcon />;
|
||||||
|
break;
|
||||||
|
case RULE.COMMENT:
|
||||||
|
case RULE.CREATOR_COMMENT:
|
||||||
|
channelUrl = notification_parameters.dynamic.comment_author;
|
||||||
|
icon = creatorIcon(channelUrl, notification_parameters?.dynamic?.comment_author_thumbnail);
|
||||||
|
break;
|
||||||
|
case RULE.COMMENT_REPLY:
|
||||||
|
channelUrl = notification_parameters.dynamic.reply_author;
|
||||||
|
icon = creatorIcon(channelUrl, notification_parameters?.dynamic?.comment_author_thumbnail);
|
||||||
|
break;
|
||||||
|
case RULE.NEW_CONTENT:
|
||||||
|
channelUrl = notification_parameters.dynamic.channel_url;
|
||||||
|
icon = creatorIcon(channelUrl, notification_parameters?.dynamic?.channel_thumbnail);
|
||||||
|
break;
|
||||||
|
case RULE.NEW_LIVESTREAM:
|
||||||
|
channelUrl = notification_parameters.dynamic.channel_url;
|
||||||
|
icon = creatorIcon(channelUrl, notification_parameters?.dynamic?.channel_thumbnail);
|
||||||
|
break;
|
||||||
|
case RULE.WEEKLY_WATCH_REMINDER:
|
||||||
|
case RULE.DAILY_WATCH_AVAILABLE:
|
||||||
|
case RULE.DAILY_WATCH_REMIND:
|
||||||
|
case RULE.MISSED_OUT:
|
||||||
|
case RULE.REWARDS_APPROVAL_PROMPT:
|
||||||
|
icon = <Icon icon={ICONS.LBC} sectionIcon />;
|
||||||
|
break;
|
||||||
|
case RULE.FIAT_TIP:
|
||||||
|
icon = <Icon icon={ICONS.FINANCE} sectionIcon />;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
icon = <Icon icon={ICONS.NOTIFICATION} sectionIcon />;
|
||||||
|
}
|
||||||
|
|
||||||
|
let channelName;
|
||||||
|
if (channelUrl) {
|
||||||
|
try {
|
||||||
|
({ claimName: channelName } = parseURI(channelUrl));
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<a onClick={() => handleNotificationClick(notification)}>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
is_read ? 'menu__list--notification' : 'menu__list--notification menu__list--notification-unread'
|
||||||
|
}
|
||||||
|
key={id}
|
||||||
|
>
|
||||||
|
<div className="notification__icon">{icon}</div>
|
||||||
|
<div className="menu__list--notification-info">
|
||||||
|
<div className="menu__list--notification-type">
|
||||||
|
{generateNotificationTitle(notification_rule, notification_parameters, channelName)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
type === 'comments' ? 'menu__list--notification-title blockquote' : 'menu__list--notification-title'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{generateNotificationText(notification_rule, notification_parameters)}
|
||||||
|
</div>
|
||||||
|
{!is_read && <span>•</span>}
|
||||||
|
<DateTime timeAgo date={active_at} />
|
||||||
|
</div>
|
||||||
|
<div className="delete-notification" onClick={(e) => handleNotificationDelete(e, id)}>
|
||||||
|
<Icon icon={ICONS.DELETE} sectionIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip title={__('Notifications')}>
|
notificationsEnabled && (
|
||||||
<Button onClick={handleMenuClick} className="header__navigationItem--icon">
|
<>
|
||||||
<Icon size={18} icon={ICONS.NOTIFICATION} aria-hidden />
|
<Tooltip title={__('Notifications')}>
|
||||||
<NotificationBubble />
|
<Button className="header__navigationItem--icon" onClick={handleClick}>
|
||||||
</Button>
|
<Icon size={18} icon={ICONS.NOTIFICATION} aria-hidden />
|
||||||
</Tooltip>
|
<NotificationBubble />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<ClickAwayListener onClickAway={handleClickAway}>
|
||||||
|
<MuiMenu {...menuProps}>
|
||||||
|
<div className="menu__list--notifications-header" />
|
||||||
|
<div className="menu__list--notifications-list">
|
||||||
|
{list.map((notification) => {
|
||||||
|
return menuEntry(notification);
|
||||||
|
})}
|
||||||
|
{list.length === 0 && (
|
||||||
|
<div className="menu__list--notification-empty">
|
||||||
|
<div className="menu__list--notification-empty-title">{__('No notifications')}</div>
|
||||||
|
<div className="menu__list--notification-empty-text">
|
||||||
|
{__("You don't have any notifications yet, but they will be here when you do!")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a onClick={handleMenuClick}>
|
||||||
|
<div className="menu__list--notifications-more">{__('View all')}</div>
|
||||||
|
</a>
|
||||||
|
</MuiMenu>
|
||||||
|
</ClickAwayListener>
|
||||||
|
</>
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import React from 'react';
|
||||||
import Skeleton from '@mui/material/Skeleton';
|
import Skeleton from '@mui/material/Skeleton';
|
||||||
import ChannelSelector from 'component/channelSelector';
|
import ChannelSelector from 'component/channelSelector';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
|
import ClickAwayListener from '@mui/material/ClickAwayListener';
|
||||||
|
|
||||||
type HeaderMenuButtonProps = {
|
type HeaderMenuButtonProps = {
|
||||||
myChannelClaimIds: ?Array<string>,
|
myChannelClaimIds: ?Array<string>,
|
||||||
|
@ -28,6 +29,7 @@ export default function HeaderProfileMenuButton(props: HeaderMenuButtonProps) {
|
||||||
const { myChannelClaimIds, activeChannelClaim, authenticated, email, signOut } = props;
|
const { myChannelClaimIds, activeChannelClaim, authenticated, email, signOut } = props;
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||||
|
const [clicked, setClicked] = React.useState(false);
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
const handleClick = (event) => setAnchorEl(!anchorEl ? event.currentTarget : null);
|
const handleClick = (event) => setAnchorEl(!anchorEl ? event.currentTarget : null);
|
||||||
const handleClose = () => setAnchorEl(null);
|
const handleClose = () => setAnchorEl(null);
|
||||||
|
@ -37,6 +39,19 @@ export default function HeaderProfileMenuButton(props: HeaderMenuButtonProps) {
|
||||||
const noActiveChannel = activeChannelUrl === null;
|
const noActiveChannel = activeChannelUrl === null;
|
||||||
const pendingChannelFetch = !noActiveChannel && myChannelClaimIds === undefined;
|
const pendingChannelFetch = !noActiveChannel && myChannelClaimIds === undefined;
|
||||||
|
|
||||||
|
const handleClickAway = () => {
|
||||||
|
if (!clicked) {
|
||||||
|
if (open) setClicked(true);
|
||||||
|
} else {
|
||||||
|
setAnchorEl(null);
|
||||||
|
setClicked(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!open) setClicked(false);
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
const menuProps = {
|
const menuProps = {
|
||||||
id: 'basic-menu',
|
id: 'basic-menu',
|
||||||
anchorEl,
|
anchorEl,
|
||||||
|
@ -49,6 +64,7 @@ export default function HeaderProfileMenuButton(props: HeaderMenuButtonProps) {
|
||||||
className: 'menu__list--header',
|
className: 'menu__list--header',
|
||||||
sx: { 'z-index': 2 },
|
sx: { 'z-index': 2 },
|
||||||
PaperProps: { className: 'MuiMenu-list--paper' },
|
PaperProps: { className: 'MuiMenu-list--paper' },
|
||||||
|
disableScrollLock: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -87,46 +103,50 @@ export default function HeaderProfileMenuButton(props: HeaderMenuButtonProps) {
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{authenticated ? (
|
{authenticated ? (
|
||||||
<MuiMenu {...menuProps}>
|
<ClickAwayListener onClickAway={handleClickAway}>
|
||||||
<ChannelSelector storeSelection isHeaderMenu />
|
<MuiMenu {...menuProps}>
|
||||||
|
<ChannelSelector storeSelection isHeaderMenu />
|
||||||
|
|
||||||
<hr className="menu__separator" />
|
<hr className="menu__separator" />
|
||||||
<HeaderMenuLink useMui page={PAGES.UPLOADS} icon={ICONS.PUBLISH} name={__('Uploads')} />
|
<HeaderMenuLink useMui page={PAGES.UPLOADS} icon={ICONS.PUBLISH} name={__('Uploads')} />
|
||||||
<HeaderMenuLink useMui page={PAGES.CHANNELS} icon={ICONS.CHANNEL} name={__('Channels')} />
|
<HeaderMenuLink useMui page={PAGES.CHANNELS} icon={ICONS.CHANNEL} name={__('Channels')} />
|
||||||
<HeaderMenuLink
|
<HeaderMenuLink
|
||||||
useMui
|
useMui
|
||||||
page={PAGES.CREATOR_DASHBOARD}
|
page={PAGES.CREATOR_DASHBOARD}
|
||||||
icon={ICONS.ANALYTICS}
|
icon={ICONS.ANALYTICS}
|
||||||
name={__('Creator Analytics')}
|
name={__('Creator Analytics')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<hr className="menu__separator" />
|
<hr className="menu__separator" />
|
||||||
<HeaderMenuLink useMui page={PAGES.REWARDS} icon={ICONS.REWARDS} name={__('Rewards')} />
|
<HeaderMenuLink useMui page={PAGES.REWARDS} icon={ICONS.REWARDS} name={__('Rewards')} />
|
||||||
<HeaderMenuLink useMui page={PAGES.INVITE} icon={ICONS.INVITE} name={__('Invites')} />
|
<HeaderMenuLink useMui page={PAGES.INVITE} icon={ICONS.INVITE} name={__('Invites')} />
|
||||||
<HeaderMenuLink useMui page={PAGES.ODYSEE_MEMBERSHIP} icon={ICONS.UPGRADE} name={__('Odysee Premium')} />
|
<HeaderMenuLink useMui page={PAGES.ODYSEE_MEMBERSHIP} icon={ICONS.UPGRADE} name={__('Odysee Premium')} />
|
||||||
|
|
||||||
<hr className="menu__separator" />
|
<hr className="menu__separator" />
|
||||||
<HeaderMenuLink useMui page={PAGES.SETTINGS} icon={ICONS.SETTINGS} name={__('Settings')} />
|
<HeaderMenuLink useMui page={PAGES.SETTINGS} icon={ICONS.SETTINGS} name={__('Settings')} />
|
||||||
<HeaderMenuLink useMui page={PAGES.HELP} icon={ICONS.HELP} name={__('Help')} />
|
<HeaderMenuLink useMui page={PAGES.HELP} icon={ICONS.HELP} name={__('Help')} />
|
||||||
|
|
||||||
<hr className="menu__separator" />
|
<hr className="menu__separator" />
|
||||||
<MuiMenuItem onClick={signOut}>
|
<MuiMenuItem onClick={signOut}>
|
||||||
<div className="menu__link" style={{ flexDirection: 'column', alignItems: 'flex-start' }}>
|
<div className="menu__link" style={{ flexDirection: 'column', alignItems: 'flex-start' }}>
|
||||||
<div className="menu__link-label">
|
<div className="menu__link-label">
|
||||||
<Icon aria-hidden icon={ICONS.SIGN_OUT} />
|
<Icon aria-hidden icon={ICONS.SIGN_OUT} />
|
||||||
{__('Sign Out')}
|
{__('Sign Out')}
|
||||||
|
</div>
|
||||||
|
<span className="menu__link-help">{email}</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="menu__link-help">{email}</span>
|
</MuiMenuItem>
|
||||||
</div>
|
</MuiMenu>
|
||||||
</MuiMenuItem>
|
</ClickAwayListener>
|
||||||
</MuiMenu>
|
|
||||||
) : (
|
) : (
|
||||||
<MuiMenu {...menuProps}>
|
<ClickAwayListener onClickAway={handleClickAway}>
|
||||||
<HeaderMenuLink useMui page={PAGES.AUTH_SIGNIN} icon={ICONS.SIGN_IN} name={__('Log In')} />
|
<MuiMenu {...menuProps}>
|
||||||
<HeaderMenuLink useMui page={PAGES.AUTH} icon={ICONS.SIGN_UP} name={__('Sign Up')} />
|
<HeaderMenuLink useMui page={PAGES.AUTH_SIGNIN} icon={ICONS.SIGN_IN} name={__('Log In')} />
|
||||||
<HeaderMenuLink useMui page={PAGES.SETTINGS} icon={ICONS.SETTINGS} name={__('Settings')} />
|
<HeaderMenuLink useMui page={PAGES.AUTH} icon={ICONS.SIGN_UP} name={__('Sign Up')} />
|
||||||
<HeaderMenuLink useMui page={PAGES.HELP} icon={ICONS.HELP} name={__('Help')} />
|
<HeaderMenuLink useMui page={PAGES.SETTINGS} icon={ICONS.SETTINGS} name={__('Settings')} />
|
||||||
</MuiMenu>
|
<HeaderMenuLink useMui page={PAGES.HELP} icon={ICONS.HELP} name={__('Help')} />
|
||||||
|
</MuiMenu>
|
||||||
|
</ClickAwayListener>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -243,9 +243,9 @@ $contentMaxWidth: 60rem;
|
||||||
|
|
||||||
.notification__title {
|
.notification__title {
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-xsmall);
|
||||||
color: var(--color-text);
|
color: var(--color-text-subtitle);
|
||||||
margin-bottom: var(--spacing-xxs);
|
// margin-bottom: var(--spacing-xxs);
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 1;
|
-webkit-line-clamp: 1;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
|
@ -265,8 +265,8 @@ $contentMaxWidth: 60rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification__text {
|
.notification__text {
|
||||||
font-size: var(--font-xsmall);
|
font-size: var(--font-small);
|
||||||
color: var(--color-text-subtitle);
|
color: var(--color-text);
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 1;
|
-webkit-line-clamp: 1;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
|
@ -284,6 +284,7 @@ $contentMaxWidth: 60rem;
|
||||||
blockquote {
|
blockquote {
|
||||||
padding-left: var(--spacing-xs);
|
padding-left: var(--spacing-xs);
|
||||||
border-left: 2px solid;
|
border-left: 2px solid;
|
||||||
|
color: var(--color-text) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,9 +156,10 @@ reach-portal {
|
||||||
}
|
}
|
||||||
|
|
||||||
.MuiPaper-root {
|
.MuiPaper-root {
|
||||||
top: calc(var(--header-height) - 11px) !important;
|
top: calc(var(--header-height) - 10px) !important;
|
||||||
|
transition: none !important;
|
||||||
@media (max-width: $breakpoint-small) {
|
@media (max-width: $breakpoint-small) {
|
||||||
top: calc(var(--header-height-mobile) - 11px) !important;
|
top: calc(var(--header-height-mobile) - 10px) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,6 +191,230 @@ reach-portal {
|
||||||
@extend .menu__list;
|
@extend .menu__list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu__list--notifications {
|
||||||
|
.MuiMenu-list {
|
||||||
|
padding: 0;
|
||||||
|
backdrop-filter: blur(4px) !important;
|
||||||
|
|
||||||
|
background-color: unset !important;
|
||||||
|
}
|
||||||
|
.MuiMenu-list--paper {
|
||||||
|
border-radius: 0 0 var(--border-radius) var(--border-radius);
|
||||||
|
background-color: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu__list--notifications-header {
|
||||||
|
height: var(--spacing-xs);
|
||||||
|
background-color: rgba(var(--color-header-background-base), 0.9);
|
||||||
|
backdrop-filter: blur(4px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:nth-child(1) {
|
||||||
|
.menu__list--notification {
|
||||||
|
border-top: 1px solid rgba(var(--color-header-button-base), 0.95);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu__list--notifications-list {
|
||||||
|
overflow: hidden !important;
|
||||||
|
.menu__list--notification {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden !important;
|
||||||
|
width: 440px;
|
||||||
|
padding: var(--spacing-s);
|
||||||
|
display: flex;
|
||||||
|
background-color: rgba(var(--color-header-button-base), 0.95);
|
||||||
|
border-top: 1px solid var(--color-header-background);
|
||||||
|
transition: border-left 0.4s;
|
||||||
|
|
||||||
|
.notification__icon {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
.channel-thumbnail {
|
||||||
|
margin-right: var(--spacing-s);
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
img,
|
||||||
|
canvas {
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.menu__list--notification-channel-unread {
|
||||||
|
border: 2px solid var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu__list--notification-info {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
.menu__list--notification-type {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: wrap;
|
||||||
|
gap: var(--spacing-xxxs);
|
||||||
|
width: 100%;
|
||||||
|
color: rgba(var(--color-text-base), 0.6);
|
||||||
|
font-size: var(--font-xsmall);
|
||||||
|
|
||||||
|
.notification__claim-title {
|
||||||
|
width: 100%;
|
||||||
|
color: rgba(var(--color-text-base), 0.6);
|
||||||
|
font-size: var(--font-xsmall);
|
||||||
|
font-weight: 400;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button__content {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
.channel-name {
|
||||||
|
margin: 0;
|
||||||
|
color: rgba(var(--color-text-base), 0.6);
|
||||||
|
font-size: var(--font-xsmall);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin-right: 0;
|
||||||
|
.credit-amount {
|
||||||
|
color: rgba(var(--color-text-base), 0.6);
|
||||||
|
font-size: var(--font-xsmall);
|
||||||
|
font-weight: 400;
|
||||||
|
svg {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu__list--notification-title {
|
||||||
|
color: var(--color-text);
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-bottom: -3px;
|
||||||
|
width: 100%;
|
||||||
|
.notification__text {
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: block;
|
||||||
|
blockquote {
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: block;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.date_time {
|
||||||
|
color: rgba(var(--color-text-base), 0.6);
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
color: var(--color-primary);
|
||||||
|
display: inline-block;
|
||||||
|
font-size: var(--font-xxsmall);
|
||||||
|
margin-right: var(--spacing-xxxs);
|
||||||
|
}
|
||||||
|
.sticker__comment {
|
||||||
|
margin-left: 0;
|
||||||
|
padding: var(--spacing-xxs);
|
||||||
|
padding-left: 0;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgba(var(--color-header-background-base), 1);
|
||||||
|
.menu__list--notification-info {
|
||||||
|
.menu__list--notification-title {
|
||||||
|
.notification__text {
|
||||||
|
color: var(--color-primary);
|
||||||
|
blockquote {
|
||||||
|
color: var(--color-primary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.delete-notification {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu__list--notification-unread {
|
||||||
|
border-left: 2px solid var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu__list--notification-empty {
|
||||||
|
display: block;
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: normal;
|
||||||
|
width: 440px;
|
||||||
|
padding: var(--spacing-m) var(--spacing-s);
|
||||||
|
background-color: rgba(var(--color-header-button-base), 0.95);
|
||||||
|
color: var(--color-text);
|
||||||
|
text-align: center;
|
||||||
|
.menu__list--notification-empty-title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: var(--font-body);
|
||||||
|
}
|
||||||
|
.menu__list--notification-empty-text {
|
||||||
|
color: rgba(var(--color-text-base), 0.6);
|
||||||
|
font-size: var(--font-xsmall);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-notification {
|
||||||
|
position: absolute;
|
||||||
|
top: var(--spacing-xs);
|
||||||
|
right: var(--spacing-xxs);
|
||||||
|
opacity: 0;
|
||||||
|
.icon__wrapper {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
padding: 0;
|
||||||
|
background-color: rgba(var(--color-header-button-base), 0.95);
|
||||||
|
outline: 4px solid rgba(var(--color-header-background-base), 1);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
.icon__wrapper {
|
||||||
|
background-color: var(--color-primary);
|
||||||
|
color: var(--color-primary-contrast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu__list--notifications-more {
|
||||||
|
color: var(--color-text);
|
||||||
|
background-color: rgba(var(--color-header-background-base), 1);
|
||||||
|
padding: var(--spacing-s) var(--spacing-xs);
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid rgba(var(--color-header-button-base), 0.95);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.MuiMenuItem-root {
|
.MuiMenuItem-root {
|
||||||
margin-left: 0px !important;
|
margin-left: 0px !important;
|
||||||
font-size: var(--font-small) !important;
|
font-size: var(--font-small) !important;
|
||||||
|
@ -199,8 +424,6 @@ reach-portal {
|
||||||
.menu__link {
|
.menu__link {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
// padding: var(--spacing-s);
|
|
||||||
// padding-right: var(--spacing-l);
|
|
||||||
padding: var(--spacing-xs) var(--spacing-s) var(--spacing-xs) var(--spacing-s);
|
padding: var(--spacing-xs) var(--spacing-s) var(--spacing-xs) var(--spacing-s);
|
||||||
height: var(--button-height);
|
height: var(--button-height);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
|
|
Loading…
Reference in a new issue