Show your preferred badge in the left pane and avatar popup

This commit is contained in:
Evan Hahn 2021-11-08 10:29:54 -06:00 committed by GitHub
parent f02b1ebce2
commit 7de340a104
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 59 additions and 3 deletions

View File

@ -0,0 +1,7 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { BadgeType } from './types';
export const isBadgeVisible = (badge: Readonly<BadgeType>): boolean =>
'isVisible' in badge ? badge.isVisible : true;

View File

@ -22,6 +22,7 @@ import * as log from '../logging/log';
import { assert } from '../util/assert'; import { assert } from '../util/assert';
import { shouldBlurAvatar } from '../util/shouldBlurAvatar'; import { shouldBlurAvatar } from '../util/shouldBlurAvatar';
import { getBadgeImageFileLocalPath } from '../badges/getBadgeImageFileLocalPath'; import { getBadgeImageFileLocalPath } from '../badges/getBadgeImageFileLocalPath';
import { isBadgeVisible } from '../badges/isBadgeVisible';
import { BadgeImageTheme } from '../badges/BadgeImageTheme'; import { BadgeImageTheme } from '../badges/BadgeImageTheme';
export enum AvatarBlur { export enum AvatarBlur {
@ -212,7 +213,7 @@ export const Avatar: FunctionComponent<Props> = ({
} }
let badgeNode: ReactNode; let badgeNode: ReactNode;
if (badge && theme && !isMe) { if (badge && theme && !noteToSelf && isBadgeVisible(badge)) {
const badgeSize = Math.ceil(size * 0.425); const badgeSize = Math.ceil(size * 0.425);
const badgeTheme = const badgeTheme =
theme === ThemeType.light ? BadgeImageTheme.Light : BadgeImageTheme.Dark; theme === ThemeType.light ? BadgeImageTheme.Light : BadgeImageTheme.Dark;

View File

@ -11,6 +11,7 @@ import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './MainHeader'; import type { PropsType } from './MainHeader';
import { MainHeader } from './MainHeader'; import { MainHeader } from './MainHeader';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
import { ThemeType } from '../types/Util';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -26,6 +27,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
searchConversation: overrideProps.searchConversation, searchConversation: overrideProps.searchConversation,
selectedConversation: undefined, selectedConversation: undefined,
startSearchCounter: 0, startSearchCounter: 0,
theme: ThemeType.light,
phoneNumber: optionalText('phoneNumber', overrideProps.phoneNumber), phoneNumber: optionalText('phoneNumber', overrideProps.phoneNumber),
title: requiredText('title', overrideProps.title), title: requiredText('title', overrideProps.title),

View File

@ -8,10 +8,11 @@ import { createPortal } from 'react-dom';
import { showSettings } from '../shims/Whisper'; import { showSettings } from '../shims/Whisper';
import { Avatar } from './Avatar'; import { Avatar } from './Avatar';
import { AvatarPopup } from './AvatarPopup'; import { AvatarPopup } from './AvatarPopup';
import type { LocalizerType } from '../types/Util'; import type { LocalizerType, ThemeType } from '../types/Util';
import type { AvatarColorType } from '../types/Colors'; import type { AvatarColorType } from '../types/Colors';
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
import { LeftPaneSearchInput } from './LeftPaneSearchInput'; import { LeftPaneSearchInput } from './LeftPaneSearchInput';
import type { BadgeType } from '../badges/types';
export type PropsType = { export type PropsType = {
searchTerm: string; searchTerm: string;
@ -29,7 +30,9 @@ export type PropsType = {
profileName?: string; profileName?: string;
title: string; title: string;
avatarPath?: string; avatarPath?: string;
badge?: BadgeType;
hasPendingUpdate: boolean; hasPendingUpdate: boolean;
theme: ThemeType;
i18n: LocalizerType; i18n: LocalizerType;
@ -191,6 +194,7 @@ export class MainHeader extends React.Component<PropsType, StateType> {
public render(): JSX.Element { public render(): JSX.Element {
const { const {
avatarPath, avatarPath,
badge,
color, color,
disabled, disabled,
hasPendingUpdate, hasPendingUpdate,
@ -203,6 +207,7 @@ export class MainHeader extends React.Component<PropsType, StateType> {
showArchivedConversations, showArchivedConversations,
startComposing, startComposing,
startUpdate, startUpdate,
theme,
title, title,
toggleProfileEditor, toggleProfileEditor,
} = this.props; } = this.props;
@ -219,6 +224,7 @@ export class MainHeader extends React.Component<PropsType, StateType> {
<Avatar <Avatar
acceptedMessageRequest acceptedMessageRequest
avatarPath={avatarPath} avatarPath={avatarPath}
badge={badge}
className="module-main-header__avatar" className="module-main-header__avatar"
color={color} color={color}
conversationType="direct" conversationType="direct"
@ -227,6 +233,7 @@ export class MainHeader extends React.Component<PropsType, StateType> {
name={name} name={name}
phoneNumber={phoneNumber} phoneNumber={phoneNumber}
profileName={profileName} profileName={profileName}
theme={theme}
title={title} title={title}
// `sharedGroupNames` makes no sense for yourself, but // `sharedGroupNames` makes no sense for yourself, but
// `<Avatar>` needs it to determine blurring. // `<Avatar>` needs it to determine blurring.
@ -247,6 +254,7 @@ export class MainHeader extends React.Component<PropsType, StateType> {
{({ ref, style }) => ( {({ ref, style }) => (
<AvatarPopup <AvatarPopup
acceptedMessageRequest acceptedMessageRequest
badge={badge}
innerRef={ref} innerRef={ref}
i18n={i18n} i18n={i18n}
isMe isMe
@ -256,6 +264,7 @@ export class MainHeader extends React.Component<PropsType, StateType> {
name={name} name={name}
phoneNumber={phoneNumber} phoneNumber={phoneNumber}
profileName={profileName} profileName={profileName}
theme={theme}
title={title} title={title}
avatarPath={avatarPath} avatarPath={avatarPath}
size={28} size={28}

View File

@ -7,6 +7,7 @@ import { mapDispatchToProps } from '../actions';
import { MainHeader } from '../../components/MainHeader'; import { MainHeader } from '../../components/MainHeader';
import type { StateType } from '../reducer'; import type { StateType } from '../reducer';
import { getPreferredBadgeSelector } from '../selectors/badges';
import { import {
getQuery, getQuery,
getSearchConversation, getSearchConversation,
@ -15,6 +16,7 @@ import {
import { import {
getIntl, getIntl,
getRegionCode, getRegionCode,
getTheme,
getUserConversationId, getUserConversationId,
getUserNumber, getUserNumber,
getUserUuid, getUserUuid,
@ -22,6 +24,8 @@ import {
import { getMe, getSelectedConversation } from '../selectors/conversations'; import { getMe, getSelectedConversation } from '../selectors/conversations';
const mapStateToProps = (state: StateType) => { const mapStateToProps = (state: StateType) => {
const me = getMe(state);
return { return {
disabled: state.network.challengeStatus !== 'idle', disabled: state.network.challengeStatus !== 'idle',
hasPendingUpdate: Boolean(state.updates.didSnooze), hasPendingUpdate: Boolean(state.updates.didSnooze),
@ -33,7 +37,9 @@ const mapStateToProps = (state: StateType) => {
ourConversationId: getUserConversationId(state), ourConversationId: getUserConversationId(state),
ourNumber: getUserNumber(state), ourNumber: getUserNumber(state),
ourUuid: getUserUuid(state), ourUuid: getUserUuid(state),
...getMe(state), ...me,
badge: getPreferredBadgeSelector(state)(me.badges),
theme: getTheme(state),
i18n: getIntl(state), i18n: getIntl(state),
}; };
}; };

View File

@ -0,0 +1,31 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import type { BadgeType } from '../../badges/types';
import { isBadgeVisible } from '../../badges/isBadgeVisible';
import { BadgeCategory } from '../../badges/BadgeCategory';
describe('isBadgeVisible', () => {
const fakeBadge = (isVisible?: boolean): BadgeType => ({
category: BadgeCategory.Donor,
descriptionTemplate: 'test',
id: 'TEST',
images: [],
name: 'test',
...(typeof isVisible === 'boolean' ? { expiresAt: 123, isVisible } : {}),
});
it("returns true if the visibility is unspecified (someone else's badge)", () => {
assert.isTrue(isBadgeVisible(fakeBadge()));
});
it('returns false if not visible', () => {
assert.isFalse(isBadgeVisible(fakeBadge(false)));
});
it('returns true if visible', () => {
assert.isTrue(isBadgeVisible(fakeBadge(true)));
});
});