Gift Badges: A few tweaks
This commit is contained in:
parent
0a0b5a7bfe
commit
a114e4e210
11 changed files with 134 additions and 124 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ export function parseBoostBadgeListFromServer(
|
|||
const parsed = parseBadgeFromServer(item.badge, updatesUrl);
|
||||
|
||||
if (parsed) {
|
||||
result[`BOOST-${level}`] = parsed;
|
||||
result[level] = parsed;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
1
ts/model-types.d.ts
vendored
|
@ -183,6 +183,7 @@ export type MessageAttributesType = {
|
|||
giftBadge?: {
|
||||
expiration: number;
|
||||
level: number;
|
||||
id: string | undefined;
|
||||
receiptCredentialPresentation: string;
|
||||
state: GiftBadgeStates;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
5
ts/textsecure/Types.d.ts
vendored
5
ts/textsecure/Types.d.ts
vendored
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -249,6 +249,7 @@ export function processGiftBadge(
|
|||
|
||||
return {
|
||||
expiration: timestamp + Number(receipt.getReceiptExpirationTime()),
|
||||
id: undefined,
|
||||
level: Number(receipt.getReceiptLevel()),
|
||||
receiptCredentialPresentation: Bytes.toBase64(
|
||||
giftBadge.receiptCredentialPresentation
|
||||
|
|
Loading…
Reference in a new issue