From 1e7e7a7b7aaf0cd679e7a085d77b412fd54a6dd0 Mon Sep 17 00:00:00 2001
From: infinite-persistence
<64950861+infinite-persistence@users.noreply.github.com>
Date: Mon, 7 Feb 2022 12:59:20 -0800
Subject: [PATCH] Notifications: use fetched urls instead of resolving (#820)
* Fix avatar occasionally stuck in spaceman
## Issue
Sometimes, we'll see a channel profile (e.g. in upper-right, in channel selector) stuck in the fallback Spaceman image.
This is due to OptimizedImage always starting with a blank src, and updated later when the mounted size has been determined. ChannelThumbnail, which uses OptimizedImage, captured the `onError` due to blank src.
## Fix
Don't mount the until the optimum size has been determined.
* FileThumbnail: skip resolve if thumbnail url is specified
* UriIndicator: skip resolve if channel info is specified
* Notifications: disable batch resolve + use fetched data if available + fallback to resolve if n/a
The fallback is using the individual resolve when no direct data is provided and claim is undefined.
---
flow-typed/notification.js | 3 ++
ui/component/fileThumbnail/view.jsx | 4 +-
ui/component/notification/view.jsx | 23 ++++++----
ui/component/optimizedImage/view.jsx | 2 +-
ui/component/uriIndicator/view.jsx | 64 +++++++++++++++++++++-------
ui/page/notifications/view.jsx | 4 +-
6 files changed, 70 insertions(+), 30 deletions(-)
diff --git a/flow-typed/notification.js b/flow-typed/notification.js
index 28bce14f5..dd863c575 100644
--- a/flow-typed/notification.js
+++ b/flow-typed/notification.js
@@ -115,11 +115,14 @@ declare type WebNotification = {
},
dynamic: {
comment_author: string,
+ comment_author_thumbnail?: string,
reply_author: string,
hash: string,
claim_title: string,
+ claim_thumbnail?: string,
comment?: string,
channel_url: string,
+ channel_thumbnail?: string,
},
email: {},
},
diff --git a/ui/component/fileThumbnail/view.jsx b/ui/component/fileThumbnail/view.jsx
index a5ef83ab6..702dc9876 100644
--- a/ui/component/fileThumbnail/view.jsx
+++ b/ui/component/fileThumbnail/view.jsx
@@ -30,10 +30,10 @@ function FileThumbnail(props: Props) {
const isGif = thumbnail && thumbnail.endsWith('gif');
React.useEffect(() => {
- if (!hasResolvedClaim && uri) {
+ if (!hasResolvedClaim && uri && !passedThumbnail) {
doResolveUri(uri);
}
- }, [hasResolvedClaim, uri, doResolveUri]);
+ }, [hasResolvedClaim, uri, doResolveUri, passedThumbnail]);
if (!allowGifs && isGif) {
return (
diff --git a/ui/component/notification/view.jsx b/ui/component/notification/view.jsx
index 49ee78a41..8debe8700 100644
--- a/ui/component/notification/view.jsx
+++ b/ui/component/notification/view.jsx
@@ -50,11 +50,12 @@ export default function Notification(props: Props) {
const stickerFromComment = isCommentNotification && commentText && parseSticker(commentText);
const notificationTarget = getNotificationTarget();
- const creatorIcon = (channelUrl) => (
-
-
+ const creatorIcon = (channelUrl, channelThumbnail) => (
+
+
);
+
let channelUrl;
let icon;
switch (notification_rule) {
@@ -64,19 +65,19 @@ export default function Notification(props: Props) {
case RULE.COMMENT:
case RULE.CREATOR_COMMENT:
channelUrl = notification_parameters.dynamic.comment_author;
- icon = creatorIcon(channelUrl);
+ icon = creatorIcon(channelUrl, notification_parameters?.dynamic?.comment_author_thumbnail);
break;
case RULE.COMMENT_REPLY:
channelUrl = notification_parameters.dynamic.reply_author;
- icon = creatorIcon(channelUrl);
+ icon = creatorIcon(channelUrl, notification_parameters?.dynamic?.comment_author_thumbnail);
break;
case RULE.NEW_CONTENT:
channelUrl = notification_parameters.dynamic.channel_url;
- icon = creatorIcon(channelUrl);
+ icon = creatorIcon(channelUrl, notification_parameters?.dynamic?.channel_thumbnail);
break;
case RULE.NEW_LIVESTREAM:
channelUrl = notification_parameters.dynamic.channel_url;
- icon = creatorIcon(channelUrl);
+ icon = creatorIcon(channelUrl, notification_parameters?.dynamic?.channel_thumbnail);
break;
case RULE.DAILY_WATCH_AVAILABLE:
case RULE.DAILY_WATCH_REMIND:
@@ -111,7 +112,7 @@ export default function Notification(props: Props) {
let uriIndicator;
const title = titleSplit.map((message, index) => {
if (channelName === message) {
- uriIndicator = ;
+ uriIndicator = ;
fullTitle.push(' ');
const resultTitle = fullTitle;
fullTitle = [' '];
@@ -204,7 +205,11 @@ export default function Notification(props: Props) {
{notification_rule === RULE.NEW_CONTENT && (
-
+
)}
{notification_rule === RULE.NEW_LIVESTREAM && (
void,
uri: string,
+ channelInfo: ?ChannelInfo, // Direct channel info to use, bypassing the need to resolve 'uri'.
// to allow for other elements to be nested within the UriIndicator
children: ?Node,
inline: boolean,
@@ -24,23 +29,53 @@ type Props = {
class UriIndicator extends React.PureComponent {
componentDidMount() {
- this.resolve(this.props);
+ this.resolveClaim(this.props);
}
componentDidUpdate() {
- this.resolve(this.props);
+ this.resolveClaim(this.props);
}
- resolve = (props: Props) => {
- const { isResolvingUri, resolveUri, claim, uri } = props;
+ resolveClaim = (props: Props) => {
+ const { isResolvingUri, resolveUri, claim, uri, channelInfo } = props;
- if (!isResolvingUri && claim === undefined && uri) {
+ if (!channelInfo && !isResolvingUri && claim === undefined && uri) {
resolveUri(uri);
}
};
+ resolveState = (channelInfo: ?ChannelInfo, claim: ?Claim, isLinkType: ?boolean) => {
+ if (channelInfo) {
+ return {
+ hasChannelData: true,
+ isAnonymous: false,
+ channelName: channelInfo.name,
+ channelLink: isLinkType ? channelInfo.uri : false,
+ };
+ } else if (claim) {
+ const signingChannel = claim.signing_channel && claim.signing_channel.amount;
+ const isChannelClaim = claim.value_type === 'channel';
+ const channelClaim = isChannelClaim ? claim : claim.signing_channel;
+
+ return {
+ hasChannelData: Boolean(channelClaim),
+ isAnonymous: !signingChannel && !isChannelClaim,
+ channelName: channelClaim?.name,
+ channelLink: isLinkType ? channelClaim?.canonical_url || channelClaim?.permanent_url : false,
+ };
+ } else {
+ return {
+ hasChannelData: false,
+ isAnonymous: undefined,
+ channelName: undefined,
+ channelLink: undefined,
+ };
+ }
+ };
+
render() {
const {
+ channelInfo,
link,
isResolvingUri,
claim,
@@ -52,7 +87,7 @@ class UriIndicator extends React.PureComponent {
className,
} = this.props;
- if (!claim) {
+ if (!channelInfo && !claim) {
return (
{isResolvingUri || claim === undefined ? __('Validating...') : __('[Removed]')}
@@ -60,9 +95,9 @@ class UriIndicator extends React.PureComponent {
);
}
- const isChannelClaim = claim.value_type === 'channel';
- const signingChannel = claim.signing_channel && claim.signing_channel.amount;
- if (!signingChannel && !isChannelClaim) {
+ const data = this.resolveState(channelInfo, claim, link);
+
+ if (data.isAnonymous) {
if (hideAnonymous) {
return null;
}
@@ -74,15 +109,12 @@ class UriIndicator extends React.PureComponent {
);
}
- const channelClaim = isChannelClaim ? claim : claim.signing_channel;
-
- if (channelClaim) {
- const { name } = channelClaim;
- const channelLink = link ? channelClaim.canonical_url || channelClaim.permanent_url : false;
+ if (data.hasChannelData) {
+ const { channelName, channelLink } = data;
const inner = (
- {name}
+ {channelName}
);
diff --git a/ui/page/notifications/view.jsx b/ui/page/notifications/view.jsx
index ed587e766..88d405899 100644
--- a/ui/page/notifications/view.jsx
+++ b/ui/page/notifications/view.jsx
@@ -22,7 +22,7 @@ type Props = {
unseenCount: number,
doSeeAllNotifications: () => void,
doReadNotifications: () => void,
- doNotificationList: (?Array) => void,
+ doNotificationList: (?Array, ?boolean) => void,
doNotificationCategories: () => void,
activeChannel: ?ChannelClaim,
doCommentReactList: (Array) => Promise,
@@ -97,7 +97,7 @@ export default function NotificationsPage(props: Props) {
try {
const matchingCategory = arrayNotificationCategories.find((category) => category.name === name);
if (matchingCategory) {
- doNotificationList(matchingCategory.types);
+ doNotificationList(matchingCategory.types, false);
}
} catch (e) {
console.error(e); // eslint-disable-line no-console