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 {
|
.module-message__unopened-gift-badge {
|
||||||
width: 240px;
|
width: 240px;
|
||||||
height: 132px;
|
height: 132px;
|
||||||
|
@ -1285,18 +1280,22 @@ $message-padding-horizontal: 12px;
|
||||||
top: -$message-padding-vertical;
|
top: -$message-padding-vertical;
|
||||||
bottom: $message-padding-vertical;
|
bottom: $message-padding-vertical;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.module-message__unopened-gift-badge--outgoing {
|
&--outgoing {
|
||||||
@include light-theme {
|
@include light-theme {
|
||||||
border-bottom: 1px solid $color-white-alpha-80;
|
border-bottom: 1px solid $color-white-alpha-80;
|
||||||
}
|
}
|
||||||
@include dark-theme {
|
@include dark-theme {
|
||||||
border-bottom: 1px solid $color-gray-95;
|
border-bottom: 1px solid $color-gray-95;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.module-message__unopened-gift-badge__ribbon-horizontal {
|
&__container {
|
||||||
|
cursor: default;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__ribbon-horizontal {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -1304,8 +1303,9 @@ $message-padding-horizontal: 12px;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
}
|
}
|
||||||
.module-message__unopened-gift-badge__ribbon-vertical {
|
|
||||||
|
&__ribbon-vertical {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
@ -1313,8 +1313,9 @@ $message-padding-horizontal: 12px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
}
|
}
|
||||||
.module-message__unopened-gift-badge__bow {
|
|
||||||
|
&__bow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
// Centered
|
// Centered
|
||||||
|
@ -1328,22 +1329,23 @@ $message-padding-horizontal: 12px;
|
||||||
// 75.26px by 51.93px in Figma, but there's a buffer in the SVG file
|
// 75.26px by 51.93px in Figma, but there's a buffer in the SVG file
|
||||||
width: 81px;
|
width: 81px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.module-message__unopened-gift-badge__text {
|
&__text {
|
||||||
@include font-body-2;
|
@include font-body-2-italic;
|
||||||
}
|
|
||||||
.module-message__unopened-gift-badge__text--incoming {
|
&--incoming {
|
||||||
@include light-theme {
|
@include light-theme {
|
||||||
color: $color-gray-60;
|
color: $color-gray-60;
|
||||||
}
|
}
|
||||||
@include dark-theme {
|
@include dark-theme {
|
||||||
color: $color-gray-25;
|
color: $color-gray-25;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.module-message__unopened-gift-badge__container
|
}
|
||||||
.module-message__text--incoming {
|
|
||||||
@include font-body-2;
|
&__container .module-message__text--incoming {
|
||||||
|
@include font-body-2-italic;
|
||||||
|
|
||||||
@include light-theme {
|
@include light-theme {
|
||||||
color: $color-gray-60;
|
color: $color-gray-60;
|
||||||
|
@ -1351,10 +1353,9 @@ $message-padding-horizontal: 12px;
|
||||||
@include dark-theme {
|
@include dark-theme {
|
||||||
color: $color-gray-25;
|
color: $color-gray-25;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.module-message__unopened-gift-badge__container
|
&__container .module-message__text--outgoing {
|
||||||
.module-message__text--outgoing {
|
@include font-body-2-italic;
|
||||||
@include font-body-2;
|
|
||||||
|
|
||||||
@include light-theme {
|
@include light-theme {
|
||||||
color: $color-white-alpha-80;
|
color: $color-white-alpha-80;
|
||||||
|
@ -1362,6 +1363,7 @@ $message-padding-horizontal: 12px;
|
||||||
@include dark-theme {
|
@include dark-theme {
|
||||||
color: $color-white-alpha-80;
|
color: $color-white-alpha-80;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.module-message__redeemed-gift-badge {
|
.module-message__redeemed-gift-badge {
|
||||||
|
|
|
@ -62,7 +62,7 @@ export function parseBoostBadgeListFromServer(
|
||||||
const parsed = parseBadgeFromServer(item.badge, updatesUrl);
|
const parsed = parseBadgeFromServer(item.badge, updatesUrl);
|
||||||
|
|
||||||
if (parsed) {
|
if (parsed) {
|
||||||
result[`BOOST-${level}`] = parsed;
|
result[level] = parsed;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1727,9 +1727,10 @@ story.add('EmbeddedContact: Loading Avatar', () => {
|
||||||
story.add('Gift Badge: Unopened', () => {
|
story.add('Gift Badge: Unopened', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
giftBadge: {
|
giftBadge: {
|
||||||
state: GiftBadgeStates.Unopened,
|
id: 'GIFT',
|
||||||
expiration: Date.now() + DAY * 30,
|
expiration: Date.now() + DAY * 30,
|
||||||
level: 3,
|
level: 3,
|
||||||
|
state: GiftBadgeStates.Unopened,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return renderBothDirections(props);
|
return renderBothDirections(props);
|
||||||
|
@ -1738,7 +1739,7 @@ story.add('Gift Badge: Unopened', () => {
|
||||||
const getPreferredBadge = () => ({
|
const getPreferredBadge = () => ({
|
||||||
category: BadgeCategory.Donor,
|
category: BadgeCategory.Donor,
|
||||||
descriptionTemplate: 'This is a description of the badge',
|
descriptionTemplate: 'This is a description of the badge',
|
||||||
id: 'BOOST-3',
|
id: 'GIFT',
|
||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
transparent: {
|
transparent: {
|
||||||
|
@ -1754,9 +1755,10 @@ story.add('Gift Badge: Redeemed (30 days)', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
getPreferredBadge,
|
getPreferredBadge,
|
||||||
giftBadge: {
|
giftBadge: {
|
||||||
state: GiftBadgeStates.Redeemed,
|
|
||||||
expiration: Date.now() + DAY * 30 + SECOND,
|
expiration: Date.now() + DAY * 30 + SECOND,
|
||||||
|
id: 'GIFT',
|
||||||
level: 3,
|
level: 3,
|
||||||
|
state: GiftBadgeStates.Redeemed,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return renderBothDirections(props);
|
return renderBothDirections(props);
|
||||||
|
@ -1766,21 +1768,23 @@ story.add('Gift Badge: Redeemed (24 hours)', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
getPreferredBadge,
|
getPreferredBadge,
|
||||||
giftBadge: {
|
giftBadge: {
|
||||||
state: GiftBadgeStates.Redeemed,
|
|
||||||
expiration: Date.now() + DAY + SECOND,
|
expiration: Date.now() + DAY + SECOND,
|
||||||
|
id: 'GIFT',
|
||||||
level: 3,
|
level: 3,
|
||||||
|
state: GiftBadgeStates.Redeemed,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return renderBothDirections(props);
|
return renderBothDirections(props);
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('Gift Badge: Redeemed (60 minutes)', () => {
|
story.add('Gift Badge: Opened (60 minutes)', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
getPreferredBadge,
|
getPreferredBadge,
|
||||||
giftBadge: {
|
giftBadge: {
|
||||||
state: GiftBadgeStates.Redeemed,
|
|
||||||
expiration: Date.now() + HOUR + SECOND,
|
expiration: Date.now() + HOUR + SECOND,
|
||||||
|
id: 'GIFT',
|
||||||
level: 3,
|
level: 3,
|
||||||
|
state: GiftBadgeStates.Opened,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return renderBothDirections(props);
|
return renderBothDirections(props);
|
||||||
|
@ -1790,21 +1794,23 @@ story.add('Gift Badge: Redeemed (1 minute)', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
getPreferredBadge,
|
getPreferredBadge,
|
||||||
giftBadge: {
|
giftBadge: {
|
||||||
state: GiftBadgeStates.Redeemed,
|
|
||||||
expiration: Date.now() + MINUTE + SECOND,
|
expiration: Date.now() + MINUTE + SECOND,
|
||||||
|
id: 'GIFT',
|
||||||
level: 3,
|
level: 3,
|
||||||
|
state: GiftBadgeStates.Redeemed,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return renderBothDirections(props);
|
return renderBothDirections(props);
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('Gift Badge: Redeemed (expired)', () => {
|
story.add('Gift Badge: Opened (expired)', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
getPreferredBadge,
|
getPreferredBadge,
|
||||||
giftBadge: {
|
giftBadge: {
|
||||||
state: GiftBadgeStates.Redeemed,
|
|
||||||
expiration: Date.now(),
|
expiration: Date.now(),
|
||||||
|
id: 'GIFT',
|
||||||
level: 3,
|
level: 3,
|
||||||
|
state: GiftBadgeStates.Opened,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return renderBothDirections(props);
|
return renderBothDirections(props);
|
||||||
|
@ -1814,9 +1820,10 @@ story.add('Gift Badge: Missing Badge', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
getPreferredBadge: () => undefined,
|
getPreferredBadge: () => undefined,
|
||||||
giftBadge: {
|
giftBadge: {
|
||||||
state: GiftBadgeStates.Redeemed,
|
|
||||||
expiration: Date.now() + MINUTE + SECOND,
|
expiration: Date.now() + MINUTE + SECOND,
|
||||||
|
id: 'MISSING',
|
||||||
level: 3,
|
level: 3,
|
||||||
|
state: GiftBadgeStates.Redeemed,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return renderBothDirections(props);
|
return renderBothDirections(props);
|
||||||
|
|
|
@ -180,12 +180,14 @@ export type AudioAttachmentProps = {
|
||||||
|
|
||||||
export enum GiftBadgeStates {
|
export enum GiftBadgeStates {
|
||||||
Unopened = 'Unopened',
|
Unopened = 'Unopened',
|
||||||
|
Opened = 'Opened',
|
||||||
Redeemed = 'Redeemed',
|
Redeemed = 'Redeemed',
|
||||||
}
|
}
|
||||||
export type GiftBadgeType = {
|
export type GiftBadgeType = {
|
||||||
level: number;
|
|
||||||
expiration: number;
|
expiration: number;
|
||||||
state: GiftBadgeStates.Redeemed | GiftBadgeStates.Unopened;
|
id: string | undefined;
|
||||||
|
level: number;
|
||||||
|
state: GiftBadgeStates;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PropsData = {
|
export type PropsData = {
|
||||||
|
@ -1329,8 +1331,11 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (giftBadge.state === GiftBadgeStates.Redeemed) {
|
if (
|
||||||
const badgeId = `BOOST-${giftBadge.level}`;
|
giftBadge.state === GiftBadgeStates.Redeemed ||
|
||||||
|
giftBadge.state === GiftBadgeStates.Opened
|
||||||
|
) {
|
||||||
|
const badgeId = giftBadge.id || `BOOST-${giftBadge.level}`;
|
||||||
const badgeSize = 64;
|
const badgeSize = 64;
|
||||||
const badge = getPreferredBadge([{ id: badgeId }]);
|
const badge = getPreferredBadge([{ id: badgeId }]);
|
||||||
const badgeImagePath = getBadgeImageFileLocalPath(
|
const badgeImagePath = getBadgeImageFileLocalPath(
|
||||||
|
|
|
@ -348,7 +348,7 @@ story.add('Video Tap-to-View', () => {
|
||||||
|
|
||||||
story.add('Gift Badge', () => {
|
story.add('Gift Badge', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
text: '',
|
text: "Some text which shouldn't be rendered",
|
||||||
isGiftBadge: true,
|
isGiftBadge: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -333,7 +333,7 @@ export class Quote extends React.Component<Props, State> {
|
||||||
isViewOnce,
|
isViewOnce,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (text) {
|
if (text && !isGiftBadge) {
|
||||||
const quoteText = bodyRanges
|
const quoteText = bodyRanges
|
||||||
? getTextWithMentions(bodyRanges, text)
|
? getTextWithMentions(bodyRanges, text)
|
||||||
: text;
|
: text;
|
||||||
|
|
|
@ -98,7 +98,9 @@ export class ViewSyncs extends Collection {
|
||||||
message.set({
|
message.set({
|
||||||
giftBadge: {
|
giftBadge: {
|
||||||
...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?: {
|
giftBadge?: {
|
||||||
expiration: number;
|
expiration: number;
|
||||||
level: number;
|
level: number;
|
||||||
|
id: string | undefined;
|
||||||
receiptCredentialPresentation: string;
|
receiptCredentialPresentation: string;
|
||||||
state: GiftBadgeStates;
|
state: GiftBadgeStates;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2682,10 +2682,6 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
const giftBadge = message.get('giftBadge');
|
const giftBadge = message.get('giftBadge');
|
||||||
if (giftBadge) {
|
if (giftBadge) {
|
||||||
const { level } = giftBadge;
|
const { level } = giftBadge;
|
||||||
const existingBadgesById = reduxState.badges.byId;
|
|
||||||
|
|
||||||
const badgeId = `BOOST-${level}`;
|
|
||||||
if (!existingBadgesById[badgeId]) {
|
|
||||||
const { updatesUrl } = window.SignalContext.config;
|
const { updatesUrl } = window.SignalContext.config;
|
||||||
strictAssert(
|
strictAssert(
|
||||||
typeof updatesUrl === 'string',
|
typeof updatesUrl === 'string',
|
||||||
|
@ -2699,23 +2695,18 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
await window.textsecure.messaging.server.getBoostBadgesFromServer(
|
await window.textsecure.messaging.server.getBoostBadgesFromServer(
|
||||||
userLanguages
|
userLanguages
|
||||||
);
|
);
|
||||||
const boostBadges = parseBoostBadgeListFromServer(
|
const boostBadgesByLevel = parseBoostBadgeListFromServer(
|
||||||
response,
|
response,
|
||||||
updatesUrl
|
updatesUrl
|
||||||
);
|
);
|
||||||
const badge = boostBadges[badgeId];
|
const badge = boostBadgesByLevel[level];
|
||||||
if (!badge) {
|
if (!badge) {
|
||||||
log.error(
|
log.error(
|
||||||
`handleDataMessage: gift badge ${badgeId} not found on server`
|
`handleDataMessage: gift badge with level ${level} not found on server`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await window.reduxActions.badges.updateOrCreate([
|
await window.reduxActions.badges.updateOrCreate([badge]);
|
||||||
{
|
giftBadge.id = badge.id;
|
||||||
...badge,
|
|
||||||
id: badgeId,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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 ProcessedStoryContext = Proto.DataMessage.IStoryContext;
|
||||||
|
|
||||||
export type ProcessedGiftBadge = {
|
export type ProcessedGiftBadge = {
|
||||||
receiptCredentialPresentation: string;
|
|
||||||
level: number;
|
|
||||||
expiration: number;
|
expiration: number;
|
||||||
|
id: string | undefined;
|
||||||
|
level: number;
|
||||||
|
receiptCredentialPresentation: string;
|
||||||
state: GiftBadgeStates;
|
state: GiftBadgeStates;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -249,6 +249,7 @@ export function processGiftBadge(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
expiration: timestamp + Number(receipt.getReceiptExpirationTime()),
|
expiration: timestamp + Number(receipt.getReceiptExpirationTime()),
|
||||||
|
id: undefined,
|
||||||
level: Number(receipt.getReceiptLevel()),
|
level: Number(receipt.getReceiptLevel()),
|
||||||
receiptCredentialPresentation: Bytes.toBase64(
|
receiptCredentialPresentation: Bytes.toBase64(
|
||||||
giftBadge.receiptCredentialPresentation
|
giftBadge.receiptCredentialPresentation
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue