Skip to content

Commit

Permalink
Add NFT details screen
Browse files Browse the repository at this point in the history
  • Loading branch information
muliswilliam committed May 25, 2022
1 parent 8181774 commit c6e5bda
Show file tree
Hide file tree
Showing 29 changed files with 431 additions and 100 deletions.
3 changes: 3 additions & 0 deletions browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,8 @@ source_set("ui") {
"views/wallet_bubble_focus_observer.h",
"wallet_bubble_manager_delegate_impl.cc",
"wallet_bubble_manager_delegate_impl.h",
"webui/brave_wallet/nft/nft_ui.cc",
"webui/brave_wallet/nft/nft_ui.h",
"webui/brave_wallet/page_handler/wallet_page_handler.cc",
"webui/brave_wallet/page_handler/wallet_page_handler.h",
"webui/brave_wallet/trezor/trezor_ui.cc",
Expand All @@ -620,6 +622,7 @@ source_set("ui") {
"//brave/components/brave_wallet/common",
"//brave/components/brave_wallet/common:mojom",
"//brave/components/brave_wallet_ui:resources",
"//brave/components/brave_wallet_ui/nft:nft_display_generated",
"//brave/components/brave_wallet_ui/page:brave_wallet_page_generated",
"//brave/components/brave_wallet_ui/panel:brave_wallet_panel_generated",
"//brave/components/brave_wallet_ui/trezor:trezor_bridge_generated",
Expand Down
54 changes: 54 additions & 0 deletions browser/ui/webui/brave_wallet/nft/nft_ui.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* Copyright (c) 2021 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "brave/browser/ui/webui/brave_wallet/nft/nft_ui.h"

#include <string>

#include "brave/common/webui_url_constants.h"
#include "brave/components/nft_display/resources/grit/nft_display_generated_map.h"
#include "components/grit/brave_components_resources.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_data_source.h"
#include "ui/resources/grit/webui_generated_resources.h"

namespace nft {

UntrustedNftUI::UntrustedNftUI(content::WebUI* web_ui)
: ui::UntrustedWebUIController(web_ui) {
auto* untrusted_source = content::WebUIDataSource::Create(kUntrustedNftURL);
untrusted_source->SetDefaultResource(IDR_BRAVE_WALLET_NFT_DISPLAY_HTML);
untrusted_source->AddResourcePaths(
base::make_span(kNftDisplayGenerated, kNftDisplayGeneratedSize));
untrusted_source->AddFrameAncestor(GURL(kBraveUIWalletPageURL));
untrusted_source->AddFrameAncestor(GURL(kBraveUIWalletPanelURL));

// TODO(nvonpentz) Determine CSP. Commented below was copied from trezor_ui.cc
//
// untrusted_source->OverrideContentSecurityPolicy(
// network::mojom::CSPDirectiveName::StyleSrc,
// std::string("style-src 'unsafe-inline';"));
untrusted_source->AddResourcePath("load_time_data.js",
IDR_WEBUI_JS_LOAD_TIME_DATA_JS);
untrusted_source->UseStringsJs();
untrusted_source->AddString("braveWalletNftBridgeUrl", kUntrustedNftURL);
untrusted_source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::ImgSrc,
std::string("img-src 'self' https: data:;"));
auto* browser_context = web_ui->GetWebContents()->GetBrowserContext();
content::WebUIDataSource::Add(browser_context, untrusted_source);
}

UntrustedNftUI::~UntrustedNftUI() = default;

std::unique_ptr<content::WebUIController>
UntrustedNftUIConfig::CreateWebUIController(content::WebUI* web_ui) {
return std::make_unique<UntrustedNftUI>(web_ui);
}

UntrustedNftUIConfig::UntrustedNftUIConfig()
: WebUIConfig(content::kChromeUIUntrustedScheme, kUntrustedNftHost) {}

} // namespace nft
37 changes: 37 additions & 0 deletions browser/ui/webui/brave_wallet/nft/nft_ui.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* Copyright (c) 2021 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_BROWSER_UI_WEBUI_BRAVE_WALLET_NFT_NFT_UI_H_
#define BRAVE_BROWSER_UI_WEBUI_BRAVE_WALLET_NFT_NFT_UI_H_

#include <memory>

#include "content/public/browser/web_ui.h"
#include "content/public/common/url_constants.h"
#include "ui/webui/untrusted_web_ui_controller.h"
#include "ui/webui/webui_config.h"

namespace nft {

class UntrustedNftUI : public ui::UntrustedWebUIController {
public:
explicit UntrustedNftUI(content::WebUI* web_ui);
UntrustedNftUI(const UntrustedNftUI&) = delete;
UntrustedNftUI& operator=(const UntrustedNftUI&) = delete;
~UntrustedNftUI() override;
};

class UntrustedNftUIConfig : public ui::WebUIConfig {
public:
UntrustedNftUIConfig();
~UntrustedNftUIConfig() override = default;

std::unique_ptr<content::WebUIController> CreateWebUIController(
content::WebUI* web_ui) override;
};

} // namespace nft

#endif // BRAVE_BROWSER_UI_WEBUI_BRAVE_WALLET_NFT_NFT_UI_H_
3 changes: 3 additions & 0 deletions browser/ui/webui/brave_wallet/wallet_page_ui.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ WalletPageUI::WalletPageUI(content::WebUI* web_ui)
source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::FrameSrc,
std::string("frame-src ") + kUntrustedTrezorURL + ";");
source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::FrameSrc,
std::string("frame-src ") + kUntrustedNftURL + ";");
source->AddString("braveWalletTrezorBridgeUrl", kUntrustedTrezorURL);
auto* profile = Profile::FromWebUI(web_ui);
content::WebUIDataSource::Add(profile, source);
Expand Down
2 changes: 2 additions & 0 deletions common/webui_url_constants.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ const char kWalletSettingsURL[] = "brave://settings/wallet";
const char kBraveSyncPath[] = "braveSync";
const char kBraveSyncSetupPath[] = "braveSync/setup";
const char kTorInternalsHost[] = "tor-internals";
const char kUntrustedNftHost[] = "nft-display";
const char kUntrustedNftURL[] = "chrome-untrusted://nft-display/";
const char kUntrustedTrezorHost[] = "trezor-bridge";
const char kUntrustedTrezorURL[] = "chrome-untrusted://trezor-bridge/";
const char kShieldsPanelURL[] = "chrome://brave-shields.top-chrome";
Expand Down
2 changes: 2 additions & 0 deletions common/webui_url_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ extern const char kWalletSettingsURL[];
extern const char kBraveSyncPath[];
extern const char kBraveSyncSetupPath[];
extern const char kTorInternalsHost[];
extern const char kUntrustedNftHost[];
extern const char kUntrustedNftURL[];
extern const char kUntrustedTrezorHost[];
extern const char kUntrustedTrezorURL[];
extern const char kShieldsPanelURL[];
Expand Down
2 changes: 2 additions & 0 deletions components/brave_wallet_ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import("//tools/grit/repack.gni")

repack("resources") {
deps = [
"//brave/components/brave_wallet_ui/nft:nft_display_generated",
"//brave/components/brave_wallet_ui/page:brave_wallet_page_generated",
"//brave/components/brave_wallet_ui/panel:brave_wallet_panel_generated",
"//brave/components/brave_wallet_ui/trezor:trezor_bridge_generated",
]
sources = [
"$root_gen_dir/brave/components/brave_wallet_page/resources/brave_wallet_page_generated.pak",
"$root_gen_dir/brave/components/brave_wallet_panel/resources/brave_wallet_panel_generated.pak",
"$root_gen_dir/brave/components/nft_display/resources/nft_display_generated.pak",
"$root_gen_dir/brave/components/trezor_bridge/resources/trezor_bridge_generated.pak",
]
output =
Expand Down
11 changes: 10 additions & 1 deletion components/brave_wallet_ui/common/async/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,16 @@ handler.on(WalletActions.getAllTokensList.getType(), async (store) => {
})

handler.on(WalletActions.addUserAsset.getType(), async (store: Store, payload: BraveWallet.BlockchainToken) => {
const { braveWalletService } = getAPIProxy()
const { braveWalletService, jsonRpcService } = getAPIProxy()
if (payload.isErc721) {
// Get NFTMetadata
const result = await jsonRpcService.getERC721Metadata(payload.contractAddress, payload.tokenId, payload.chainId)
if (!result.error) {
const response = JSON.parse(result.response)
payload.logo = response.image || payload.logo
}
}

const result = await braveWalletService.addUserAsset(payload)
store.dispatch(WalletActions.addUserAssetError(!result.success))
})
Expand Down
20 changes: 16 additions & 4 deletions components/brave_wallet_ui/common/hooks/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,24 @@ import usePricing from './pricing'
import useBalance from './balance'
import { useIsMounted } from './useIsMounted'
import { useLib } from './useLib'
import { httpifyIpfsUrl } from '../../utils/string-utils'

const assetsLogo = (assets: BraveWallet.BlockchainToken[]) => {
return assets.map(token => ({
...token,
logo: `chrome://erc-token-images/${token.logo}`
}) as BraveWallet.BlockchainToken)
return assets.map(token => {
let logo = token.logo
if (token.logo?.startsWith('ipfs://')) {
logo = httpifyIpfsUrl(token.logo)
} else if (token.logo?.startsWith('data:image/')) {
logo = token.logo
} else {
logo = `chrome://erc-token-images/${token.logo}`
}

return {
...token,
logo
} as BraveWallet.BlockchainToken
})
}

export function useAssets () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ const EditVisibleAssetsModal = ({ onClose }: Props) => {
<>
{filteredTokenList.slice(0, tokenDisplayAmount).map((token) =>
<AssetWatchlistItem
key={`${token.contractAddress}-${token.symbol}-${token.chainId}`}
key={`${token.contractAddress}-${token.symbol}-${token.chainId}-${token.tokenId}`}
isCustom={isCustomToken(token)}
token={token}
networkList={networkList}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import {
NetworkIconWrapper,
NameColumn,
Spacer,
NetworkDescriptionText
NetworkDescriptionText,
NFTAssetIcon
} from './style'
import { withPlaceholderIcon, CreateNetworkIcon, LoadingSkeleton } from '../../shared'
import { WithHideBalancePlaceholder } from '../'
Expand Down Expand Up @@ -57,7 +58,7 @@ const PortfolioAssetItem = (props: Props) => {
const [assetNetworkSkeletonWidth, setAssetNetworkSkeletonWidth] = React.useState(0)

const AssetIconWithPlaceholder = React.useMemo(() => {
return withPlaceholderIcon(AssetIcon, { size: 'big', marginLeft: 0, marginRight: 8 })
return withPlaceholderIcon(token.isErc721 ? NFTAssetIcon : AssetIcon, { size: 'big', marginLeft: 0, marginRight: 8 })
}, [])

const formattedAssetBalance = token.isErc721
Expand All @@ -78,8 +79,8 @@ const PortfolioAssetItem = (props: Props) => {
}, [fiatBalance])

const isLoading = React.useMemo(() => {
return formattedAssetBalance === ''
}, [formattedAssetBalance])
return formattedAssetBalance === '' && !token.isErc721
}, [formattedAssetBalance, token])

const tokensNetwork = React.useMemo(() => {
return getTokensNetwork(networks, token)
Expand Down Expand Up @@ -111,7 +112,7 @@ const PortfolioAssetItem = (props: Props) => {
{token.visible &&
// Selecting an erc721 token is temp disabled until UI is ready for viewing NFTs
// or when showing loading skeleton
<StyledWrapper disabled={token.isErc721 || isLoading} onClick={action}>
<StyledWrapper disabled={isLoading} onClick={action}>
<NameAndIcon>
<IconsWrapper>
{isLoading
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import styled from 'styled-components'
import { AssetIconProps, AssetIconFactory, WalletButton } from '../../shared/style'
import { AssetIconProps, AssetIconFactory, WalletButton, AssetIconIframe } from '../../shared/style'

interface StyleProps {
disabled: boolean
Expand Down Expand Up @@ -60,9 +60,15 @@ export const AssetBalanceText = styled.span`
// support with custom AssetIconFactory.
//
// Ref: https://styled-components.com/docs/advanced#style-objects
export const AssetIcon = AssetIconFactory<AssetIconProps>({
const assetIconProps = {
width: '40px',
height: 'auto'
}
export const AssetIcon = AssetIconFactory<AssetIconProps>(assetIconProps)
export const NFTAssetIcon = AssetIconIframe<AssetIconProps>({
...assetIconProps,
height: '40px',
border: 'transparent'
})

export const IconsWrapper = styled.div`
Expand Down
Loading

0 comments on commit c6e5bda

Please sign in to comment.