Gift Badges: A few tweaks

This commit is contained in:
Scott Nonnenberg 2022-05-16 12:54:38 -07:00 committed by GitHub
parent 0a0b5a7bfe
commit a114e4e210
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 134 additions and 124 deletions

View file

@ -1268,11 +1268,6 @@ $message-padding-horizontal: 12px;
}
}
.module-message__unopened-gift-badge__container {
cursor: pointer;
user-select: none;
}
.module-message__unopened-gift-badge {
width: 240px;
height: 132px;
@ -1285,82 +1280,89 @@ $message-padding-horizontal: 12px;
top: -$message-padding-vertical;
bottom: $message-padding-vertical;
}
}
.module-message__unopened-gift-badge--outgoing {
@include light-theme {
border-bottom: 1px solid $color-white-alpha-80;
&--outgoing {
@include light-theme {
border-bottom: 1px solid $color-white-alpha-80;
}
@include dark-theme {
border-bottom: 1px solid $color-gray-95;
}
}
@include dark-theme {
border-bottom: 1px solid $color-gray-95;
&__container {
cursor: default;
user-select: none;
}
}
.module-message__unopened-gift-badge__ribbon-horizontal {
position: absolute;
left: 0;
right: 0;
height: 16px;
top: 50%;
transform: translateY(-50%);
background-color: $color-white;
}
.module-message__unopened-gift-badge__ribbon-vertical {
position: absolute;
top: 0;
bottom: 0;
width: 16px;
left: 50%;
transform: translateX(-50%);
background-color: $color-white;
}
.module-message__unopened-gift-badge__bow {
position: absolute;
// Centered
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
// For proper alignment with the ribbons
margin-top: 3px;
// 75.26px by 51.93px in Figma, but there's a buffer in the SVG file
width: 81px;
height: 60px;
}
.module-message__unopened-gift-badge__text {
@include font-body-2;
}
.module-message__unopened-gift-badge__text--incoming {
@include light-theme {
color: $color-gray-60;
&__ribbon-horizontal {
position: absolute;
left: 0;
right: 0;
height: 16px;
top: 50%;
transform: translateY(-50%);
background-color: $color-white;
}
@include dark-theme {
color: $color-gray-25;
}
}
.module-message__unopened-gift-badge__container
.module-message__text--incoming {
@include font-body-2;
@include light-theme {
color: $color-gray-60;
&__ribbon-vertical {
position: absolute;
top: 0;
bottom: 0;
width: 16px;
left: 50%;
transform: translateX(-50%);
background-color: $color-white;
}
@include dark-theme {
color: $color-gray-25;
}
}
.module-message__unopened-gift-badge__container
.module-message__text--outgoing {
@include font-body-2;
@include light-theme {
color: $color-white-alpha-80;
&__bow {
position: absolute;
// Centered
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
// For proper alignment with the ribbons
margin-top: 3px;
// 75.26px by 51.93px in Figma, but there's a buffer in the SVG file
width: 81px;
height: 60px;
}
@include dark-theme {
color: $color-white-alpha-80;
&__text {
@include font-body-2-italic;
&--incoming {
@include light-theme {
color: $color-gray-60;
}
@include dark-theme {
color: $color-gray-25;
}
}
}
&__container .module-message__text--incoming {
@include font-body-2-italic;
@include light-theme {
color: $color-gray-60;
}
@include dark-theme {
color: $color-gray-25;
}
}
&__container .module-message__text--outgoing {
@include font-body-2-italic;
@include light-theme {
color: $color-white-alpha-80;
}
@include dark-theme {
color: $color-white-alpha-80;
}
}
}

View file

@ -62,7 +62,7 @@ export function parseBoostBadgeListFromServer(
const parsed = parseBadgeFromServer(item.badge, updatesUrl);
if (parsed) {
result[`BOOST-${level}`] = parsed;
result[level] = parsed;
}
});

View file

@ -1727,9 +1727,10 @@ story.add('EmbeddedContact: Loading Avatar', () => {
story.add('Gift Badge: Unopened', () => {
const props = createProps({
giftBadge: {
state: GiftBadgeStates.Unopened,
id: 'GIFT',
expiration: Date.now() + DAY * 30,
level: 3,
state: GiftBadgeStates.Unopened,
},
});
return renderBothDirections(props);
@ -1738,7 +1739,7 @@ story.add('Gift Badge: Unopened', () => {
const getPreferredBadge = () => ({
category: BadgeCategory.Donor,
descriptionTemplate: 'This is a description of the badge',
id: 'BOOST-3',
id: 'GIFT',
images: [
{
transparent: {
@ -1754,9 +1755,10 @@ story.add('Gift Badge: Redeemed (30 days)', () => {
const props = createProps({
getPreferredBadge,
giftBadge: {
state: GiftBadgeStates.Redeemed,
expiration: Date.now() + DAY * 30 + SECOND,
id: 'GIFT',
level: 3,
state: GiftBadgeStates.Redeemed,
},
});
return renderBothDirections(props);
@ -1766,21 +1768,23 @@ story.add('Gift Badge: Redeemed (24 hours)', () => {
const props = createProps({
getPreferredBadge,
giftBadge: {
state: GiftBadgeStates.Redeemed,
expiration: Date.now() + DAY + SECOND,
id: 'GIFT',
level: 3,
state: GiftBadgeStates.Redeemed,
},
});
return renderBothDirections(props);
});
story.add('Gift Badge: Redeemed (60 minutes)', () => {
story.add('Gift Badge: Opened (60 minutes)', () => {
const props = createProps({
getPreferredBadge,
giftBadge: {
state: GiftBadgeStates.Redeemed,
expiration: Date.now() + HOUR + SECOND,
id: 'GIFT',
level: 3,
state: GiftBadgeStates.Opened,
},
});
return renderBothDirections(props);
@ -1790,21 +1794,23 @@ story.add('Gift Badge: Redeemed (1 minute)', () => {
const props = createProps({
getPreferredBadge,
giftBadge: {
state: GiftBadgeStates.Redeemed,
expiration: Date.now() + MINUTE + SECOND,
id: 'GIFT',
level: 3,
state: GiftBadgeStates.Redeemed,
},
});
return renderBothDirections(props);
});
story.add('Gift Badge: Redeemed (expired)', () => {
story.add('Gift Badge: Opened (expired)', () => {
const props = createProps({
getPreferredBadge,
giftBadge: {
state: GiftBadgeStates.Redeemed,
expiration: Date.now(),
id: 'GIFT',
level: 3,
state: GiftBadgeStates.Opened,
},
});
return renderBothDirections(props);
@ -1814,9 +1820,10 @@ story.add('Gift Badge: Missing Badge', () => {
const props = createProps({
getPreferredBadge: () => undefined,
giftBadge: {
state: GiftBadgeStates.Redeemed,
expiration: Date.now() + MINUTE + SECOND,
id: 'MISSING',
level: 3,
state: GiftBadgeStates.Redeemed,
},
});
return renderBothDirections(props);

View file

@ -180,12 +180,14 @@ export type AudioAttachmentProps = {
export enum GiftBadgeStates {
Unopened = 'Unopened',
Opened = 'Opened',
Redeemed = 'Redeemed',
}
export type GiftBadgeType = {
level: number;
expiration: number;
state: GiftBadgeStates.Redeemed | GiftBadgeStates.Unopened;
id: string | undefined;
level: number;
state: GiftBadgeStates;
};
export type PropsData = {
@ -1329,8 +1331,11 @@ export class Message extends React.PureComponent<Props, State> {
);
}
if (giftBadge.state === GiftBadgeStates.Redeemed) {
const badgeId = `BOOST-${giftBadge.level}`;
if (
giftBadge.state === GiftBadgeStates.Redeemed ||
giftBadge.state === GiftBadgeStates.Opened
) {
const badgeId = giftBadge.id || `BOOST-${giftBadge.level}`;
const badgeSize = 64;
const badge = getPreferredBadge([{ id: badgeId }]);
const badgeImagePath = getBadgeImageFileLocalPath(

View file

@ -348,7 +348,7 @@ story.add('Video Tap-to-View', () => {
story.add('Gift Badge', () => {
const props = createProps({
text: '',
text: "Some text which shouldn't be rendered",
isGiftBadge: true,
});

View file

@ -333,7 +333,7 @@ export class Quote extends React.Component<Props, State> {
isViewOnce,
} = this.props;
if (text) {
if (text && !isGiftBadge) {
const quoteText = bodyRanges
? getTextWithMentions(bodyRanges, text)
: text;

View file

@ -98,7 +98,9 @@ export class ViewSyncs extends Collection {
message.set({
giftBadge: {
...giftBadge,
state: GiftBadgeStates.Redeemed,
state: isIncoming(message.attributes)
? GiftBadgeStates.Redeemed
: GiftBadgeStates.Opened,
},
});
}

1
ts/model-types.d.ts vendored
View file

@ -183,6 +183,7 @@ export type MessageAttributesType = {
giftBadge?: {
expiration: number;
level: number;
id: string | undefined;
receiptCredentialPresentation: string;
state: GiftBadgeStates;
};

View file

@ -2682,40 +2682,31 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const giftBadge = message.get('giftBadge');
if (giftBadge) {
const { level } = giftBadge;
const existingBadgesById = reduxState.badges.byId;
const badgeId = `BOOST-${level}`;
if (!existingBadgesById[badgeId]) {
const { updatesUrl } = window.SignalContext.config;
strictAssert(
typeof updatesUrl === 'string',
'getProfile: expected updatesUrl to be a defined string'
const { updatesUrl } = window.SignalContext.config;
strictAssert(
typeof updatesUrl === 'string',
'getProfile: expected updatesUrl to be a defined string'
);
const userLanguages = getUserLanguages(
navigator.languages,
window.getLocale()
);
const response =
await window.textsecure.messaging.server.getBoostBadgesFromServer(
userLanguages
);
const userLanguages = getUserLanguages(
navigator.languages,
window.getLocale()
const boostBadgesByLevel = parseBoostBadgeListFromServer(
response,
updatesUrl
);
const badge = boostBadgesByLevel[level];
if (!badge) {
log.error(
`handleDataMessage: gift badge with level ${level} not found on server`
);
const response =
await window.textsecure.messaging.server.getBoostBadgesFromServer(
userLanguages
);
const boostBadges = parseBoostBadgeListFromServer(
response,
updatesUrl
);
const badge = boostBadges[badgeId];
if (!badge) {
log.error(
`handleDataMessage: gift badge ${badgeId} not found on server`
);
} else {
await window.reduxActions.badges.updateOrCreate([
{
...badge,
id: badgeId,
},
]);
}
} else {
await window.reduxActions.badges.updateOrCreate([badge]);
giftBadge.id = badge.id;
}
}

View file

@ -189,9 +189,10 @@ export type ProcessedGroupCallUpdate = Proto.DataMessage.IGroupCallUpdate;
export type ProcessedStoryContext = Proto.DataMessage.IStoryContext;
export type ProcessedGiftBadge = {
receiptCredentialPresentation: string;
level: number;
expiration: number;
id: string | undefined;
level: number;
receiptCredentialPresentation: string;
state: GiftBadgeStates;
};

View file

@ -249,6 +249,7 @@ export function processGiftBadge(
return {
expiration: timestamp + Number(receipt.getReceiptExpirationTime()),
id: undefined,
level: Number(receipt.getReceiptLevel()),
receiptCredentialPresentation: Bytes.toBase64(
giftBadge.receiptCredentialPresentation