Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add displaying svg logos inside QR code #39072

Merged
merged 21 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2054,7 +2054,7 @@ PODS:
- RNSound/Core (= 0.11.2)
- RNSound/Core (0.11.2):
- React-Core
- RNSVG (14.1.0):
- RNSVG (15.4.0):
- glog
- hermes-engine
- RCT-Folly (= 2022.05.16.00)
Expand All @@ -2072,9 +2072,9 @@ PODS:
- React-utils
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- RNSVG/common (= 14.1.0)
- RNSVG/common (= 15.4.0)
- Yoga
- RNSVG/common (14.1.0):
- RNSVG/common (15.4.0):
- glog
- hermes-engine
- RCT-Folly (= 2022.05.16.00)
Expand Down Expand Up @@ -2643,7 +2643,7 @@ SPEC CHECKSUMS:
RNScreens: abd354e98519ed267600b7ee64fdcb8e060b1218
RNShare: 2a4cdfc0626ad56b0ef583d424f2038f772afe58
RNSound: 6c156f925295bdc83e8e422e7d8b38d33bc71852
RNSVG: 18f1381e046be2f1c30b4724db8d0c966238089f
RNSVG: 8c067e7203053d4c82f456cbeab1fe509ac797dd
SDWebImage: 750adf017a315a280c60fde706ab1e552a3ae4e9
SDWebImageAVIFCoder: 8348fef6d0ec69e129c66c9fe4d74fbfbf366112
SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c
Expand Down
18 changes: 10 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@
"react-native-permissions": "^3.10.0",
"react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#da50d2c5c54e268499047f9cc98b8df4196c1ddf",
"react-native-plaid-link-sdk": "11.11.0",
"react-native-qrcode-svg": "^6.2.0",
"react-native-qrcode-svg": "git+https://github.com/Expensify/react-native-qrcode-svg",
"react-native-quick-sqlite": "git+https://github.com/margelo/react-native-quick-sqlite#abc91857d4b3efb2020ec43abd2a508563b64316",
"react-native-reanimated": "^3.8.0",
"react-native-release-profiler": "^0.2.1",
Expand All @@ -173,7 +173,7 @@
"react-native-screens": "3.32.0",
"react-native-share": "^10.0.2",
"react-native-sound": "^0.11.2",
"react-native-svg": "14.1.0",
"react-native-svg": "15.4.0",
"react-native-tab-view": "^3.5.2",
"react-native-url-polyfill": "^2.0.0",
"react-native-view-shot": "3.8.0",
Expand Down
39 changes: 35 additions & 4 deletions src/components/QRCode.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import type {ImageSourcePropType} from 'react-native';
import QRCodeLibrary from 'react-native-qrcode-svg';
import type {Svg} from 'react-native-svg';
import type {Svg, SvgProps} from 'react-native-svg';
import useTheme from '@hooks/useTheme';
import CONST from '@src/CONST';

Expand All @@ -19,6 +19,22 @@ type QRCodeProps = {
*/
logo?: ImageSourcePropType;

/**
* If the logo to be displayed in the middle of the QR code is an SVG, then this prop needs to be used
* instead of standard `logo`.
*/
svgLogo?: React.FC<SvgProps>;

/**
* Background color to be used for logo.
*/
logoBackgroundColor?: string;
luacmartins marked this conversation as resolved.
Show resolved Hide resolved

/**
* Fill color to be used for logos of type SVG.
*/
svgLogoFillColor?: string;

/** The size ratio of logo to QR code */
logoRatio?: QRCodeLogoRatio;

Expand All @@ -35,21 +51,36 @@ type QRCodeProps = {
backgroundColor?: string;

/**
* Function to retrieve the internal component ref and be able to call it's
* Function to retrieve the internal component ref and be able to call its
* methods
*/
getRef?: (ref: Svg) => Svg;
};

function QRCode({url, logo, getRef, size = 120, color, backgroundColor, logoRatio = CONST.QR.DEFAULT_LOGO_SIZE_RATIO, logoMarginRatio = CONST.QR.DEFAULT_LOGO_MARGIN_RATIO}: QRCodeProps) {
function QRCode({
url,
logo,
svgLogo,
svgLogoFillColor,
logoBackgroundColor,
getRef,
size = 120,
color,
backgroundColor,
logoRatio = CONST.QR.DEFAULT_LOGO_SIZE_RATIO,
logoMarginRatio = CONST.QR.DEFAULT_LOGO_MARGIN_RATIO,
}: QRCodeProps) {
const theme = useTheme();

return (
<QRCodeLibrary
getRef={getRef}
value={url}
size={size}
logo={logo}
logoBackgroundColor={backgroundColor ?? theme.highlightBG}
logoSVG={svgLogo}
logoColor={svgLogoFillColor}
logoBackgroundColor={logoBackgroundColor ?? theme.highlightBG}
logoSize={size * logoRatio}
logoMargin={size * logoMarginRatio}
logoBorderRadius={size}
Expand Down
5 changes: 4 additions & 1 deletion src/components/QRShare/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import type {QRShareHandle, QRShareProps} from './types';

function QRShare({url, title, subtitle, logo, logoRatio, logoMarginRatio}: QRShareProps, ref: ForwardedRef<QRShareHandle>) {
function QRShare({url, title, subtitle, logo, svgLogo, svgLogoFillColor, logoBackgroundColor, logoRatio, logoMarginRatio}: QRShareProps, ref: ForwardedRef<QRShareHandle>) {
const styles = useThemeStyles();
const theme = useTheme();

Expand Down Expand Up @@ -48,6 +48,9 @@ function QRShare({url, title, subtitle, logo, logoRatio, logoMarginRatio}: QRSha
<QRCode
getRef={(svg) => (svgRef.current = svg)}
url={url}
svgLogo={svgLogo}
svgLogoFillColor={svgLogoFillColor}
logoBackgroundColor={logoBackgroundColor}
logo={logo}
size={qrCodeSize}
logoRatio={logoRatio}
Expand Down
19 changes: 18 additions & 1 deletion src/components/QRShare/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type React from 'react';
import type {ImageSourcePropType} from 'react-native';
import type {Svg} from 'react-native-svg';
import type {Svg, SvgProps} from 'react-native-svg';
import type {QRCodeLogoMarginRatio, QRCodeLogoRatio} from '@components/QRCode';

type QRShareProps = {
Expand All @@ -18,11 +19,27 @@ type QRShareProps = {
* */
subtitle?: string;

/**
* If the logo to be displayed in the middle of the QR code is an SVG, then this prop needs to be used
* instead of standard `logo`
*/
svgLogo?: React.FC<SvgProps>;

/**
* The logo which will be display in the middle of the QR code
*/
logo?: ImageSourcePropType;

/**
* Background color to be used for logo.
*/
logoBackgroundColor?: string;

/**
* Fill color to be used for logos of type SVG
*/
svgLogoFillColor?: string;

/**
* The size ratio of logo to QR code
*/
Expand Down
3 changes: 2 additions & 1 deletion src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import lodashIsEqual from 'lodash/isEqual';
import lodashMaxBy from 'lodash/maxBy';
import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {SvgProps} from 'react-native-svg';
import type {OriginalMessageModifiedExpense} from 'src/types/onyx/OriginalMessage';
import type {TupleToUnion, ValueOf} from 'type-fest';
import type {FileObject} from '@components/AttachmentModal';
Expand Down Expand Up @@ -1730,7 +1731,7 @@ function formatReportLastMessageText(lastMessageText: string, isModifiedExpenseM
/**
* Helper method to return the default avatar associated with the given login
*/
function getDefaultWorkspaceAvatar(workspaceName?: string): IconAsset {
function getDefaultWorkspaceAvatar(workspaceName?: string): React.FC<SvgProps> {
if (!workspaceName) {
return defaultWorkspaceAvatars.WorkspaceBuilding;
}
Expand Down
51 changes: 46 additions & 5 deletions src/pages/ShareCodePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, {useMemo, useRef} from 'react';
import {View} from 'react-native';
import type {ImageSourcePropType} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import type {SvgProps} from 'react-native-svg';
import expensifyLogo from '@assets/images/expensify-logo-round-transparent.png';
import ContextMenuItem from '@components/ContextMenuItem';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
Expand All @@ -14,6 +15,7 @@ import ScrollView from '@components/ScrollView';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import Clipboard from '@libs/Clipboard';
import Navigation from '@libs/Navigation/Navigation';
Expand All @@ -22,17 +24,38 @@ import * as Url from '@libs/Url';
import * as UserUtils from '@libs/UserUtils';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type {Report} from '@src/types/onyx';
import type {Policy, Report} from '@src/types/onyx';

type ShareCodePageOnyxProps = {
/** The report currently being looked at */
report?: OnyxEntry<Report>;

/** The policy for the report currently being looked at */
policy?: OnyxEntry<Policy>;
};

type ShareCodePageProps = ShareCodePageOnyxProps;

function ShareCodePage({report}: ShareCodePageProps) {
/**
* When sharing a policy (workspace) only return user avatar that is user defined. Default ws avatars have separate logic.
* In any other case default to expensify logo
*/

function getLogoForWorkspace(report: OnyxEntry<Report>, policy?: OnyxEntry<Policy>): ImageSourcePropType | undefined {
if (!policy || !policy.id || report?.type !== 'chat') {
return expensifyLogo;
}

if (!policy.avatarURL) {
return undefined;
}
luacmartins marked this conversation as resolved.
Show resolved Hide resolved

return policy.avatarURL as ImageSourcePropType;
}

function ShareCodePage({report, policy}: ShareCodePageProps) {
const themeStyles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {translate} = useLocalize();
const {environmentURL} = useEnvironment();
const qrCodeRef = useRef<QRShareHandle>(null);
Expand Down Expand Up @@ -64,6 +87,21 @@ function ShareCodePage({report}: ShareCodePageProps) {
? `${urlWithTrailingSlash}${ROUTES.REPORT_WITH_ID.getRoute(report.reportID)}`
: `${urlWithTrailingSlash}${ROUTES.PROFILE.getRoute(currentUserPersonalDetails.accountID ?? '-1')}`;

const logo = isReport ? getLogoForWorkspace(report, policy) : (UserUtils.getAvatarUrl(currentUserPersonalDetails?.avatar, currentUserPersonalDetails?.accountID) as ImageSourcePropType);

// Default logos (avatars) are SVG and they require some special logic to display correctly
let svgLogo: React.FC<SvgProps> | undefined;
let logoBackgroundColor: string | undefined;
let svgLogoFillColor: string | undefined;

if (!logo && policy && !policy.avatarURL) {
svgLogo = ReportUtils.getDefaultWorkspaceAvatar(policy.name) || Expensicons.FallbackAvatar;

const defaultWorkspaceAvatarColors = StyleUtils.getDefaultWorkspaceAvatarColor(policy.id ?? '');
logoBackgroundColor = defaultWorkspaceAvatarColors.backgroundColor?.toString();
svgLogoFillColor = defaultWorkspaceAvatarColors.fill;
}

return (
<ScreenWrapper testID={ShareCodePage.displayName}>
<HeaderWithBackButton
Expand All @@ -85,9 +123,12 @@ function ShareCodePage({report}: ShareCodePageProps) {
url={url}
title={title}
subtitle={subtitle}
logo={isReport ? expensifyLogo : (UserUtils.getAvatarUrl(currentUserPersonalDetails?.avatar, currentUserPersonalDetails?.accountID) as ImageSourcePropType)}
logoRatio={isReport ? CONST.QR.EXPENSIFY_LOGO_SIZE_RATIO : CONST.QR.DEFAULT_LOGO_SIZE_RATIO}
logoMarginRatio={isReport ? CONST.QR.EXPENSIFY_LOGO_MARGIN_RATIO : CONST.QR.DEFAULT_LOGO_MARGIN_RATIO}
logo={logo}
svgLogo={svgLogo}
logoBackgroundColor={logoBackgroundColor}
svgLogoFillColor={svgLogoFillColor}
logoRatio={CONST.QR.DEFAULT_LOGO_SIZE_RATIO}
logoMarginRatio={CONST.QR.DEFAULT_LOGO_MARGIN_RATIO}
/>
</View>

Expand Down
27 changes: 23 additions & 4 deletions src/pages/home/report/ReportDetailsShareCodePage.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
import React from 'react';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import * as ReportUtils from '@libs/ReportUtils';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import ShareCodePage from '@pages/ShareCodePage';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Policy} from '@src/types/onyx';
import type {WithReportOrNotFoundProps} from './withReportOrNotFound';
import withReportOrNotFound from './withReportOrNotFound';

type ReportDetailsShareCodePageProps = WithReportOrNotFoundProps;
type ReportDetailsShareCodePageOnyxProps = {
policy: OnyxEntry<Policy>;
};

function ReportDetailsShareCodePage({report}: ReportDetailsShareCodePageProps) {
type ReportDetailsShareCodePageProps = ReportDetailsShareCodePageOnyxProps & WithReportOrNotFoundProps;

function ReportDetailsShareCodePage({report, policy}: ReportDetailsShareCodePageProps) {
if (ReportUtils.isSelfDM(report)) {
return <NotFoundPage />;
}
return <ShareCodePage report={report} />;
return (
<ShareCodePage
report={report}
policy={policy}
/>
);
}

export default withReportOrNotFound()(ReportDetailsShareCodePage);
export default withReportOrNotFound()(
withOnyx<ReportDetailsShareCodePageProps, ReportDetailsShareCodePageOnyxProps>({
policy: {
key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`,
},
})(ReportDetailsShareCodePage),
);
Loading
Loading