Skip to content

Commit

Permalink
Onboarding story
Browse files Browse the repository at this point in the history
  • Loading branch information
josh-signal committed Nov 9, 2022
1 parent 94f318e commit 19a42ed
Show file tree
Hide file tree
Showing 42 changed files with 725 additions and 143 deletions.
1 change: 1 addition & 0 deletions app/main.ts
Expand Up @@ -399,6 +399,7 @@ async function prepareUrl(
serverUrl: config.get<string>('serverUrl'),
storageUrl: config.get<string>('storageUrl'),
updatesUrl: config.get<string>('updatesUrl'),
resourcesUrl: config.get<string>('resourcesUrl'),
cdnUrl0: config.get<ConfigType>('cdn').get<string>('0'),
cdnUrl2: config.get<ConfigType>('cdn').get<string>('2'),
certificateAuthority: config.get<string>('certificateAuthority'),
Expand Down
1 change: 1 addition & 0 deletions config/default.json
Expand Up @@ -9,6 +9,7 @@
},
"contentProxyUrl": "http://contentproxy.signal.org:443",
"updatesUrl": "https://updates2.signal.org/desktop",
"resourcesUrl": "https://updates2.signal.org",
"updatesPublicKey": "05fd7dd3de7149dc0a127909fee7de0f7620ddd0de061b37a2c303e37de802a401",
"sfuUrl": "https://sfu.voip.signal.org/",
"updatesEnabled": false,
Expand Down
4 changes: 4 additions & 0 deletions images/icons/v2/official-20.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion protos/SignalStorage.proto
Expand Up @@ -169,7 +169,7 @@ message AccountRecord {
optional bool displayBadgesOnProfile = 23;
optional bool keepMutedChatsArchived = 25;
optional bool hasSetMyStoriesPrivacy = 26;
reserved /* hasViewedOnboardingStory */ 27;
optional bool hasViewedOnboardingStory = 27;
reserved 28; // deprecatedStoriesDisabled
optional bool storiesDisabled = 29;
optional OptionalBool storyViewReceiptsEnabled = 30;
Expand Down
1 change: 1 addition & 0 deletions sticker-creator/window/phase3-sticker-functions.ts
Expand Up @@ -30,6 +30,7 @@ const WebAPI = initializeWebAPI({
url: config.serverUrl,
storageUrl: config.storageUrl,
updatesUrl: config.updatesUrl,
resourcesUrl: config.resourcesUrl,
directoryConfig: config.directoryConfig,
cdnUrlObject: {
0: config.cdnUrl0,
Expand Down
14 changes: 14 additions & 0 deletions stylesheets/components/Avatar.scss
Expand Up @@ -160,4 +160,18 @@
border-color: $color-ultramarine-dawn;
}
}

&--signal-official {
.module-Avatar__contents {
align-items: center;
background-color: $color-ultramarine;
display: flex;
justify-content: center;
}

.module-Avatar__image {
height: 66%;
width: 66%;
}
}
}
10 changes: 10 additions & 0 deletions stylesheets/components/StoryListItem.scss
Expand Up @@ -58,6 +58,8 @@
&--title {
@include font-body-1-bold;
color: $color-gray-05;
display: flex;
align-items: center;
}

&--timestamp,
Expand Down Expand Up @@ -175,4 +177,12 @@
width: 12px;
@include color-svg('../images/icons/v2/chevron-right-20.svg', $color-white);
}

&__signal-official {
background: url('../images/icons/v2/official-20.svg') no-repeat center;
display: inline-block;
height: 16px;
margin-left: 6px;
width: 16px;
}
}
42 changes: 41 additions & 1 deletion ts/ConversationController.ts
Expand Up @@ -28,6 +28,7 @@ import { sleep } from './util/sleep';
import { isNotNil } from './util/isNotNil';
import { MINUTE, SECOND } from './util/durations';
import { getUuidsForE164s } from './util/getUuidsForE164s';
import { SIGNAL_ACI, SIGNAL_AVATAR_PATH } from './types/Conversation';

type ConvoMatchType =
| {
Expand Down Expand Up @@ -129,8 +130,8 @@ const {
export function start(): void {
const conversations = new window.Whisper.ConversationCollection();

window.getConversations = () => conversations;
window.ConversationController = new ConversationController(conversations);
window.getConversations = () => conversations;
}

export class ConversationController {
Expand All @@ -144,6 +145,8 @@ export class ConversationController {

private _combineConversationsQueue = new PQueue({ concurrency: 1 });

private _signalConversationId: undefined | string;

constructor(private _conversations: ConversationModelCollectionType) {
const debouncedUpdateUnreadCount = debounce(
this.updateUnreadCount.bind(this),
Expand Down Expand Up @@ -406,6 +409,43 @@ export class ConversationController {
return conversation;
}

getOrCreateSignalConversation(): ConversationModel {
const conversation = this.getOrCreate(SIGNAL_ACI, 'private', {
muteExpiresAt: Number.MAX_SAFE_INTEGER,
profileAvatar: { path: SIGNAL_AVATAR_PATH },
profileName: 'Signal',
profileSharing: true,
});

this._signalConversationId = conversation.id;

return conversation;
}

getSignalConversationId(): string {
if (this._signalConversationId) {
return this._signalConversationId;
}

let conversation = this.get(SIGNAL_ACI);

if (!conversation) {
conversation = this.getOrCreateSignalConversation();
}

this._signalConversationId = conversation.id;

return conversation.id;
}

isSignalConversation(uuidOrId: string): boolean {
if (uuidOrId === SIGNAL_ACI) {
return true;
}

return this.getSignalConversationId() === uuidOrId;
}

areWePrimaryDevice(): boolean {
const ourDeviceId = window.textsecure.storage.user.getDeviceId();

Expand Down
2 changes: 2 additions & 0 deletions ts/background.ts
Expand Up @@ -156,6 +156,7 @@ import MessageSender from './textsecure/SendMessage';
import type AccountManager from './textsecure/AccountManager';
import { onStoryRecipientUpdate } from './util/onStoryRecipientUpdate';
import { StoryViewModeType, StoryViewTargetType } from './types/Stories';
import { downloadOnboardingStory } from './util/downloadOnboardingStory';

const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;

Expand Down Expand Up @@ -1031,6 +1032,7 @@ export async function startApp(): Promise<void> {
(async () => {
menuOptions = await window.SignalContext.getMenuOptions();
})(),
downloadOnboardingStory(),
]);
await window.ConversationController.checkForConflicts();
} catch (error) {
Expand Down
6 changes: 5 additions & 1 deletion ts/components/Avatar.tsx
Expand Up @@ -25,6 +25,7 @@ import { getBadgeImageFileLocalPath } from '../badges/getBadgeImageFileLocalPath
import { getInitials } from '../util/getInitials';
import { isBadgeVisible } from '../badges/isBadgeVisible';
import { shouldBlurAvatar } from '../util/shouldBlurAvatar';
import { SIGNAL_AVATAR_PATH } from '../types/Conversation';

export enum AvatarBlur {
NoBlur,
Expand Down Expand Up @@ -295,7 +296,10 @@ export const Avatar: FunctionComponent<Props> = ({
'module-Avatar',
Boolean(storyRing) && 'module-Avatar--with-story',
storyRing === HasStories.Unread && 'module-Avatar--with-story--unread',
className
className,
avatarPath === SIGNAL_AVATAR_PATH
? 'module-Avatar--signal-official'
: undefined
)}
style={{
minWidth: size,
Expand Down
8 changes: 8 additions & 0 deletions ts/components/CompositionArea.tsx
Expand Up @@ -92,6 +92,7 @@ export type OwnProps = Readonly<{
) => unknown;
compositionApi?: MutableRefObject<CompositionAPIType>;
conversationId: string;
uuid?: string;
draftAttachments: ReadonlyArray<AttachmentDraftType>;
errorDialogAudioRecorderType?: ErrorDialogAudioRecorderType;
errorRecording: (e: ErrorDialogAudioRecorderType) => unknown;
Expand All @@ -101,6 +102,7 @@ export type OwnProps = Readonly<{
isFetchingUUID?: boolean;
isGroupV1AndDisabled?: boolean;
isMissingMandatoryProfileSharing?: boolean;
isSignalConversation?: boolean;
recordingState: RecordingState;
isSMSOnly?: boolean;
left?: boolean;
Expand Down Expand Up @@ -176,6 +178,7 @@ export const CompositionArea = ({
processAttachments,
removeAttachment,
theme,
isSignalConversation,

// AttachmentList
draftAttachments,
Expand Down Expand Up @@ -481,6 +484,11 @@ export const CompositionArea = ({
};
}, [setLarge]);

if (isSignalConversation) {
// TODO DESKTOP-4547
return <div />;
}

if (
isBlocked ||
areWePending ||
Expand Down
16 changes: 8 additions & 8 deletions ts/components/Stories.tsx
Expand Up @@ -30,8 +30,11 @@ export type PropsType = {
addStoryData: AddStoryData;
deleteStoryForEveryone: (story: StoryViewType) => unknown;
getPreferredBadge: PreferredBadgeSelectorType;
hasViewReceiptSetting: boolean;
hiddenStories: Array<ConversationStoryType>;
i18n: LocalizerType;
isStoriesSettingsVisible: boolean;
isViewingStory: boolean;
me: ConversationType;
myStories: Array<MyStoryType>;
onForwardStory: (storyId: string) => unknown;
Expand All @@ -46,19 +49,19 @@ export type PropsType = {
stories: Array<ConversationStoryType>;
toggleHideStories: (conversationId: string) => unknown;
toggleStoriesView: () => unknown;
viewUserStories: ViewUserStoriesActionCreatorType;
viewStory: ViewStoryActionCreatorType;
isViewingStory: boolean;
isStoriesSettingsVisible: boolean;
hasViewReceiptSetting: boolean;
viewUserStories: ViewUserStoriesActionCreatorType;
};

export const Stories = ({
addStoryData,
deleteStoryForEveryone,
getPreferredBadge,
hasViewReceiptSetting,
hiddenStories,
i18n,
isStoriesSettingsVisible,
isViewingStory,
me,
myStories,
onForwardStory,
Expand All @@ -73,11 +76,8 @@ export const Stories = ({
stories,
toggleHideStories,
toggleStoriesView,
viewUserStories,
viewStory,
isViewingStory,
isStoriesSettingsVisible,
hasViewReceiptSetting,
viewUserStories,
}: PropsType): JSX.Element => {
const width = getWidthFromPreferredWidth(preferredWidthFromStorage, {
requiresFullWidth: true,
Expand Down
88 changes: 51 additions & 37 deletions ts/components/StoryListItem.tsx
Expand Up @@ -3,15 +3,16 @@

import React, { useState } from 'react';
import classNames from 'classnames';
import type { ConversationType } from '../state/ducks/conversations';
import type { ConversationStoryType, StoryViewType } from '../types/Stories';
import { StoryViewTargetType, HasStories } from '../types/Stories';
import type { ConversationType } from '../state/ducks/conversations';
import type { LocalizerType } from '../types/Util';
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
import type { ViewUserStoriesActionCreatorType } from '../state/ducks/stories';
import { Avatar, AvatarSize } from './Avatar';
import { ConfirmationDialog } from './ConfirmationDialog';
import { ContextMenu } from './ContextMenu';
import { SIGNAL_ACI } from '../types/Conversation';
import { StoryViewTargetType, HasStories } from '../types/Stories';

import { MessageTimestamp } from './conversation/MessageTimestamp';
import { StoryImage } from './StoryImage';
Expand Down Expand Up @@ -97,6 +98,8 @@ export const StoryListItem = ({

const { firstName, title } = sender;

const isSignalOfficial = sender.uuid === SIGNAL_ACI;

let avatarStoryRing: HasStories | undefined;
if (attachment) {
avatarStoryRing = isUnread ? HasStories.Unread : HasStories.Read;
Expand All @@ -109,40 +112,46 @@ export const StoryListItem = ({
repliesElement = <div className="StoryListItem__info--replies--others" />;
}

const menuOptions = [
{
icon: 'StoryListItem__icon--hide',
label: isHidden
? i18n('StoryListItem__unhide')
: i18n('StoryListItem__hide'),
onClick: () => {
if (isHidden) {
onHideStory(conversationId);
} else {
setHasConfirmHideStory(true);
}
},
},
];

if (!isSignalOfficial) {
menuOptions.push({
icon: 'StoryListItem__icon--info',
label: i18n('StoryListItem__info'),
onClick: () =>
viewUserStories({
conversationId,
viewTarget: StoryViewTargetType.Details,
}),
});

menuOptions.push({
icon: 'StoryListItem__icon--chat',
label: i18n('StoryListItem__go-to-chat'),
onClick: () => onGoToConversation(conversationId),
});
}

return (
<>
<ContextMenu
aria-label={i18n('StoryListItem__label')}
i18n={i18n}
menuOptions={[
{
icon: 'StoryListItem__icon--hide',
label: isHidden
? i18n('StoryListItem__unhide')
: i18n('StoryListItem__hide'),
onClick: () => {
if (isHidden) {
onHideStory(conversationId);
} else {
setHasConfirmHideStory(true);
}
},
},
{
icon: 'StoryListItem__icon--info',
label: i18n('StoryListItem__info'),
onClick: () =>
viewUserStories({
conversationId,
viewTarget: StoryViewTargetType.Details,
}),
},
{
icon: 'StoryListItem__icon--chat',
label: i18n('StoryListItem__go-to-chat'),
onClick: () => onGoToConversation(conversationId),
},
]}
menuOptions={menuOptions}
moduleClassName={classNames('StoryListItem', {
'StoryListItem--hidden': isHidden,
})}
Expand All @@ -162,13 +171,18 @@ export const StoryListItem = ({
<>
<div className="StoryListItem__info--title">
{group ? group.title : title}
{isSignalOfficial && (
<span className="StoryListItem__signal-official" />
)}
</div>
<MessageTimestamp
i18n={i18n}
isRelativeTime
module="StoryListItem__info--timestamp"
timestamp={timestamp}
/>
{!isSignalOfficial && (
<MessageTimestamp
i18n={i18n}
isRelativeTime
module="StoryListItem__info--timestamp"
timestamp={timestamp}
/>
)}
</>
{repliesElement}
</div>
Expand Down

0 comments on commit 19a42ed

Please sign in to comment.