Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion stylesheets/_modules.scss
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@

.module-conversation-list-item__header__name {
flex-grow: 1;
flex-shrink: 1;
flex-shrink: 1000; // we need this to take all the shrinking instead of the ListItemIcons
font-size: 14px;
line-height: 18px;

Expand Down
7 changes: 6 additions & 1 deletion ts/components/dialog/UpdateConversationDetailsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@ function useDescriptionErrorString({
// description is always optional
return '';
}
const charLength = newDescription?.length || 0;

// "👨🏻‍❤️‍💋‍👨🏻" is supposed to be 1char, but 35 bytes, and this is the only way
// I found to have this to have the correct count
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
const charLength = [...segmenter.segment(newDescription)].length;

const byteLength = new TextEncoder().encode(newDescription).length;

if (
Expand Down
17 changes: 9 additions & 8 deletions ts/components/dialog/user-settings/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const StyledProfileName = styled(Flex)`
}
`;

const StyledName = styled.p`
const StyledName = styled.div`
font-size: var(--font-size-xl);
line-height: 1.4;
font-weight: 700;
Expand All @@ -116,14 +116,15 @@ export const ProfileName = (props: { profileName: string; onClick: () => void })
<StyledProfileName $container={true} $justifyContent="center" $alignItems="center">
<StyledName data-testid="your-profile-name" onClick={onClick}>
{profileName}
{showPro.show ? (
<ProIconButton
iconSize={'medium'}
dataTestId="pro-badge-profile-name"
onClick={showPro.cb}
style={{ display: 'inline-flex', marginInlineStart: 'var(--margins-sm)' }}
/>
) : null}
</StyledName>
{showPro.show ? (
<ProIconButton
iconSize={'medium'}
dataTestId="pro-badge-profile-name"
onClick={showPro.cb}
/>
) : null}
</StyledProfileName>
);
};
10 changes: 9 additions & 1 deletion ts/components/leftpane/conversation-list-item/HeaderItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const NotificationSettingIcon = () => {
iconType="mute"
iconColor={'var(--conversation-tab-text-color)'}
iconSize="small"
style={{ flexShrink: 0 }}
/>
);
case 'mentions_only':
Expand All @@ -44,6 +45,7 @@ const NotificationSettingIcon = () => {
iconType="bell"
iconColor={'var(--conversation-tab-text-color)'}
iconSize="small"
style={{ flexShrink: 0 }}
/>
);
default:
Expand All @@ -63,7 +65,12 @@ const PinIcon = () => {
const isPinned = useIsPinned(conversationId);

return isPinned ? (
<SessionIcon iconType="pin" iconColor={'var(--conversation-tab-text-color)'} iconSize="small" />
<SessionIcon
iconType="pin"
iconColor={'var(--conversation-tab-text-color)'}
iconSize="small"
style={{ flexShrink: 0 }}
/>
) : null;
};

Expand Down Expand Up @@ -100,6 +107,7 @@ const MentionAtSymbol = styled.span`
min-width: 16px;
border-radius: 8px;
cursor: pointer;
flex-shrink: 0;

&:hover {
filter: grayscale(0.7);
Expand Down
52 changes: 37 additions & 15 deletions ts/components/leftpane/overlay/choose-action/ContactRow.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { createPortal } from 'react-dom';
import { contextMenu } from 'react-contexify';
import type { ReactNode } from 'react';
import styled, { CSSProperties } from 'styled-components';

import { openConversationWithMessages } from '../../../../state/ducks/conversations';
import { Avatar, AvatarSize } from '../../../avatar/Avatar';
import { useShowUserDetailsCbFromConversation } from '../../../menuAndSettingsHooks/useShowUserDetailsCb';
import { ContactName } from '../../../conversation/ContactName/ContactName';
import { MemoConversationListItemContextMenu } from '../../../menu/ConversationListItemContextMenu';
import { ContextConversationProvider } from '../../../../contexts/ConvoIdContext';

const Portal = ({ children }: { children: ReactNode }) => {
return createPortal(children, document.querySelector('.inbox.index') as Element);
};

type Props = { id: string; displayName?: string; style: CSSProperties };

Expand Down Expand Up @@ -56,23 +66,35 @@ export const ContactRowBreak = (props: { char: string; key: string; style: CSSPr

export const ContactRow = (props: Props) => {
const { id, style } = props;
const triggerId = `contact-row-${id}-ctxmenu`;

return (
<StyledRowContainer
style={style}
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onClick={async () => openConversationWithMessages({ conversationKey: id, messageId: null })}
>
<AvatarItem id={id} />
<ContactName
data-testid="module-conversation__user__profile-name"
pubkey={id}
contactNameContext="contact-list-row"
extraNameStyle={{
color: 'var(--text-primary-color)',
fontSize: 'var(--font-size-lg)',
<ContextConversationProvider value={id}>
<StyledRowContainer
style={style}
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onClick={async () => openConversationWithMessages({ conversationKey: id, messageId: null })}
onContextMenu={e => {
contextMenu.show({
id: triggerId,
event: e,
});
}}
/>
</StyledRowContainer>
>
<AvatarItem id={id} />
<ContactName
data-testid="module-conversation__user__profile-name"
pubkey={id}
contactNameContext="contact-list-row"
extraNameStyle={{
color: 'var(--text-primary-color)',
fontSize: 'var(--font-size-lg)',
}}
/>
<Portal>
<MemoConversationListItemContextMenu triggerId={triggerId} />
</Portal>
</StyledRowContainer>
</ContextConversationProvider>
);
};
13 changes: 12 additions & 1 deletion ts/mains/main_node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,10 @@ app.on('ready', async () => {
const userDataPath = getRealPath(app.getPath('userData'));
const installPath = getRealPath(join(app.getAppPath(), '..', '..'));

// Register signal handlers
process.on('SIGINT', () => gracefulShutdown('SIGINT')); // Ctrl+C
process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); // Termination request

installFileHandler({
protocol: electronProtocol,
userDataPath,
Expand Down Expand Up @@ -859,7 +863,7 @@ async function requestShutdown() {
setTimeout(() => {
console.log('requestShutdown: Response never received; forcing shutdown.');
resolve(undefined);
}, 2 * DURATION.MINUTES);
}, 1 * DURATION.MINUTES);
});

try {
Expand All @@ -869,6 +873,13 @@ async function requestShutdown() {
}
}

async function gracefulShutdown(signal: string) {
console.warn(`Received ${signal}, shutting down...`);
await requestShutdown();
app.quit();
console.warn(`Received ${signal}, shutting down: done`);
}

app.on('before-quit', () => {
console.log('before-quit event', {
readyForShutdown: mainWindow ? readyForShutdown : null,
Expand Down
24 changes: 10 additions & 14 deletions ts/models/conversation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,14 +275,14 @@ export class ConversationModel extends Model<ConversationAttributes> {
const type = this.get('type');
switch (type) {
case ConversationTypeEnum.PRIVATE:
return this.id;
return `priv:${ed25519Str(this.id)}`;
case ConversationTypeEnum.GROUPV2:
return `group(${ed25519Str(this.id)})`;
return `group:${ed25519Str(this.id)}`;
case ConversationTypeEnum.GROUP: {
if (this.isPublic()) {
return this.id;
return `comm:${this.id}`;
}
return `group(${ed25519Str(this.id)})`;
return `group_legacy:${ed25519Str(this.id)}`;
}
default:
assertUnreachable(type, `idForLogging case not handled for type:"${type}"`);
Expand Down Expand Up @@ -954,10 +954,7 @@ export class ConversationModel extends Model<ConversationAttributes> {
const networkTimestamp = NetworkTime.now();

window?.log?.info(
'Sending message to conversation',
this.idForLogging(),
'with networkTimestamp: ',
networkTimestamp
`Sending message to conversation ${this.idForLogging()} with networkTimestamp: ${networkTimestamp}`
);

const attachmentsWithVoiceMessage = attachments
Expand Down Expand Up @@ -1163,13 +1160,12 @@ export class ConversationModel extends Model<ConversationAttributes> {
}
}

// Note: we agreed that a **legacy closed** group ControlMessage message does not expire.
// Group v2 on the other hand, have expiring disappearing control message
// Private chats and group v2 are the two types of conversation
// where this function can be called, and both have expiring
// disappearing control messages.

message.setExpirationType(
this.isClosedGroup() && !this.isClosedGroupV2() ? 'unknown' : expirationType
);
message.setExpireTimer(this.isClosedGroup() && !this.isClosedGroupV2() ? 0 : expireTimer);
message.setExpirationType(expirationType);
message.setExpireTimer(expireTimer);

if (!message.id) {
message.setId(v4());
Expand Down
23 changes: 18 additions & 5 deletions ts/models/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ContentMessage } from '../session/messages/outgoing';
import { ClosedGroupV2VisibleMessage } from '../session/messages/outgoing/visibleMessage/ClosedGroupVisibleMessage';
import { PubKey } from '../session/types';
import {
MessageUtils,
UserUtils,
attachmentIdAsStrFromUrl,
uploadAttachmentsToFileServer,
Expand Down Expand Up @@ -89,7 +90,7 @@ import {
getPromotedGroupUpdateChangeStr,
} from './groupUpdate';
import { NetworkTime } from '../util/NetworkTime';
import { MessageQueue } from '../session/sending';
import { MessageQueue, MessageSender } from '../session/sending';
import { getTimerNotificationStr } from './timerNotifications';
import {
ExpirationTimerUpdate,
Expand Down Expand Up @@ -173,7 +174,7 @@ export class MessageModel extends Model<MessageAttributes> {
}

public idForLogging() {
return `${this.get('source')} ${this.get('sent_at')}`;
return `msg(${this.id}, ${this.getConversation()?.idForLogging()}, ${this.get('sent_at')})`;
}

public isExpirationTimerUpdate() {
Expand Down Expand Up @@ -1113,9 +1114,14 @@ export class MessageModel extends Model<MessageAttributes> {
);

if (syncMessage) {
await MessageQueue.use().sendSyncMessage({
namespace: SnodeNamespaces.Default,
message: syncMessage,
await MessageSender.sendSingleMessage({
isSyncMessage: true,
message: await MessageUtils.toRawMessage(
PubKey.cast(UserUtils.getOurPubKeyStrFromCache()),
syncMessage,
SnodeNamespaces.Default
),
abortSignal: null,
});
}
}
Expand Down Expand Up @@ -1359,6 +1365,13 @@ export class MessageModel extends Model<MessageAttributes> {
this.set({ expirationStartTimestamp });
}

public setExpiresAt(expiresAt: number | undefined) {
if (expiresAt === this.getExpiresAt()) {
return;
}
this.set({ expires_at: expiresAt });
}

public getPreview() {
return this.get('preview');
}
Expand Down
1 change: 1 addition & 0 deletions ts/models/messageFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ function getSharedAttributesForPublicMessage({
messageHash: '', // we do not care of a messageHash for an opengroup message. we have serverId for that
// NOTE Community messages do not support disappearing messages
expirationStartTimestamp: undefined,
expires_at: undefined,
};
}

Expand Down
23 changes: 13 additions & 10 deletions ts/receiver/queuedJob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ async function markConvoAsReadIfOutgoingMessage(
const isOutgoingMessage =
message.get('type') === 'outgoing' || message.get('direction') === 'outgoing';
if (isOutgoingMessage) {
const sentAt = message.get('sent_at') || message.get('serverTimestamp');
const sentAt = message.get('serverTimestamp') || message.get('sent_at');
if (sentAt) {
const expirationType = message.getExpirationType();
const expireTimer = message.getExpireTimerSeconds();
Expand Down Expand Up @@ -432,16 +432,19 @@ export async function handleMessageJob(
messageModel.getExpirationType(),
messageModel.getExpireTimerSeconds()
);

if (expirationMode === 'deleteAfterSend') {
messageModel.setMessageExpirationStartTimestamp(
DisappearingMessages.setExpirationStartTimestamp(
expirationMode,
messageModel.get('sent_at'),
'handleMessageJob',
messageModel.id
)
const expireTimer = messageModel.getExpireTimerSeconds();

if (expirationMode === 'deleteAfterSend' && expireTimer > 0) {
const expirationStartTimestamp = DisappearingMessages.setExpirationStartTimestamp(
expirationMode,
messageModel.get('sent_at'),
'handleMessageJob',
messageModel.id
);
if (expirationStartTimestamp) {
messageModel.setMessageExpirationStartTimestamp(expirationStartTimestamp);
messageModel.setExpiresAt(expirationStartTimestamp + expireTimer * 1000);
}
}
}

Expand Down
Loading
Loading