Page MenuHomePhorge

notif-utils.js
No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None

notif-utils.js

// @flow
import invariant from 'invariant';
import { isUserMentioned } from './mention-utils.js';
import { robotextForMessageInfo } from './message-utils.js';
import type { NotificationTextsParams } from './messages/message-spec.js';
import { messageSpecs } from './messages/message-specs.js';
import { threadNoun } from './thread-utils.js';
import { type PlatformDetails } from '../types/device-types.js';
import { type MessageType, messageTypes } from '../types/message-types-enum.js';
import {
type MessageData,
type MessageInfo,
type RawMessageInfo,
type RobotextMessageInfo,
type SidebarSourceMessageInfo,
} from '../types/message-types.js';
import type { CreateSidebarMessageInfo } from '../types/messages/create-sidebar.js';
import type { TextMessageInfo } from '../types/messages/text.js';
import type { ThreadInfo } from '../types/minimally-encoded-thread-permissions-types.js';
import type {
NotifTexts,
ResolvedNotifTexts,
APNsNotificationTopic,
} from '../types/notif-types.js';
import { type ThreadType, threadTypes } from '../types/thread-types-enum.js';
import type { RelativeUserInfo, UserInfo } from '../types/user-types.js';
import { prettyDate } from '../utils/date-utils.js';
import type { GetENSNames } from '../utils/ens-helpers.js';
import {
type EntityText,
ET,
getEntityTextAsString,
type ThreadEntity,
} from '../utils/entity-text.js';
import type { GetFCNames } from '../utils/farcaster-helpers.js';
import { promiseAll } from '../utils/promises.js';
import { trimText } from '../utils/text-utils.js';
async function notifTextsForMessageInfo(
messageInfos: MessageInfo[],
threadInfo: ThreadInfo,
parentThreadInfo: ?ThreadInfo,
notifTargetUserInfo: UserInfo,
getENSNames: ?GetENSNames,
getFCNames: ?GetFCNames,
): Promise<?ResolvedNotifTexts> {
const fullNotifTexts = await fullNotifTextsForMessageInfo(
messageInfos,
threadInfo,
parentThreadInfo,
notifTargetUserInfo,
getENSNames,
getFCNames,
);
if (!fullNotifTexts) {
return fullNotifTexts;
}
const merged = trimText(fullNotifTexts.merged, 300);
const body = trimText(fullNotifTexts.body, 300);
const title = trimText(fullNotifTexts.title, 100);
if (!fullNotifTexts.prefix) {
return { merged, body, title };
}
const prefix = trimText(fullNotifTexts.prefix, 50);
return { merged, body, title, prefix };
}
function notifTextsForEntryCreationOrEdit(
messageInfos: $ReadOnlyArray<MessageInfo>,
threadInfo: ThreadInfo,
): NotifTexts {
const hasCreateEntry = messageInfos.some(
messageInfo => messageInfo.type === messageTypes.CREATE_ENTRY,
);
const messageInfo = messageInfos[0];
const thread = ET.thread({ display: 'shortName', threadInfo });
const creator = ET.user({ userInfo: messageInfo.creator });
const prefix = ET`${creator}`;
if (!hasCreateEntry) {
invariant(
messageInfo.type === messageTypes.EDIT_ENTRY,
'messageInfo should be messageTypes.EDIT_ENTRY!',
);
const date = prettyDate(messageInfo.date);
let body = ET`updated the text of an event in ${thread}`;
body = ET`${body} scheduled for ${date}: "${messageInfo.text}"`;
const merged = ET`${prefix} ${body}`;
return {
merged,
title: threadInfo.uiName,
body,
prefix,
};
}
invariant(
messageInfo.type === messageTypes.CREATE_ENTRY ||
messageInfo.type === messageTypes.EDIT_ENTRY,
'messageInfo should be messageTypes.CREATE_ENTRY/EDIT_ENTRY!',
);
const date = prettyDate(messageInfo.date);
let body = ET`created an event in ${thread}`;
body = ET`${body} scheduled for ${date}: "${messageInfo.text}"`;
const merged = ET`${prefix} ${body}`;
return {
merged,
title: threadInfo.uiName,
body,
prefix,
};
}
type NotifTextsForSubthreadCreationInput = {
+creator: RelativeUserInfo,
+threadType: ThreadType,
+parentThreadInfo: ThreadInfo,
+childThreadName: ?string,
+childThreadUIName: string | ThreadEntity,
};
function notifTextsForSubthreadCreation(
input: NotifTextsForSubthreadCreationInput,
): NotifTexts {
const {
creator,
threadType,
parentThreadInfo,
childThreadName,
childThreadUIName,
} = input;
const prefix = ET`${ET.user({ userInfo: creator })}`;
let body: string | EntityText = `created a new ${threadNoun(
threadType,
parentThreadInfo.id,
)}`;
if (parentThreadInfo.name && parentThreadInfo.type !== threadTypes.GENESIS) {
body = ET`${body} in ${parentThreadInfo.name}`;
}
let merged = ET`${prefix} ${body}`;
if (childThreadName) {
merged = ET`${merged} called "${childThreadName}"`;
}
return {
merged,
body,
title: childThreadUIName,
prefix,
};
}
type NotifTextsForSidebarCreationInput = {
+createSidebarMessageInfo: CreateSidebarMessageInfo,
+sidebarSourceMessageInfo?: ?SidebarSourceMessageInfo,
+firstSidebarMessageInfo?: ?TextMessageInfo,
+threadInfo: ThreadInfo,
+params: NotificationTextsParams,
};
function notifTextsForSidebarCreation(
input: NotifTextsForSidebarCreationInput,
): NotifTexts {
const {
sidebarSourceMessageInfo,
createSidebarMessageInfo,
firstSidebarMessageInfo,
threadInfo,
params,
} = input;
const creator = ET.user({ userInfo: createSidebarMessageInfo.creator });
const prefix = ET`${creator}`;
const initialName = createSidebarMessageInfo.initialThreadState.name;
const sourceMessageAuthorPossessive = ET.user({
userInfo: createSidebarMessageInfo.sourceMessageAuthor,
possessive: true,
});
let body: string | EntityText = 'started a thread in response to';
body = ET`${body} ${sourceMessageAuthorPossessive} message`;
const { username } = params.notifTargetUserInfo;
if (
username &&
sidebarSourceMessageInfo &&
sidebarSourceMessageInfo.sourceMessage.type === messageTypes.TEXT &&
isUserMentioned(username, sidebarSourceMessageInfo.sourceMessage.text)
) {
body = ET`${body} that tagged you`;
} else if (
username &&
firstSidebarMessageInfo &&
isUserMentioned(username, firstSidebarMessageInfo.text)
) {
body = ET`${body} and tagged you`;
} else if (initialName) {
body = ET`${body} "${initialName}"`;
}
return {
merged: ET`${prefix} ${body}`,
body,
title: threadInfo.uiName,
prefix,
};
}
function mostRecentMessageInfoType(
messageInfos: $ReadOnlyArray<MessageInfo>,
): MessageType {
if (messageInfos.length === 0) {
throw new Error('expected MessageInfo, but none present!');
}
return messageInfos[0].type;
}
async function fullNotifTextsForMessageInfo(
messageInfos: $ReadOnlyArray<MessageInfo>,
threadInfo: ThreadInfo,
parentThreadInfo: ?ThreadInfo,
notifTargetUserInfo: UserInfo,
getENSNames: ?GetENSNames,
getFCNames: ?GetFCNames,
): Promise<?ResolvedNotifTexts> {
const mostRecentType = mostRecentMessageInfoType(messageInfos);
const messageSpec = messageSpecs[mostRecentType];
invariant(
messageSpec.notificationTexts,
`we're not aware of messageType ${mostRecentType}`,
);
const unresolvedNotifTexts = await messageSpec.notificationTexts(
messageInfos,
threadInfo,
{ notifTargetUserInfo, parentThreadInfo },
);
if (!unresolvedNotifTexts) {
return unresolvedNotifTexts;
}
const resolveToString = async (
entityText: string | EntityText,
): Promise<string> => {
if (typeof entityText === 'string') {
return entityText;
}
const notifString = await getEntityTextAsString(
entityText,
{ getENSNames, getFCNames },
{ prefixThisThreadNounWith: 'your' },
);
invariant(
notifString !== null && notifString !== undefined,
'getEntityTextAsString only returns falsey when passed falsey',
);
return notifString;
};
let promises = {
merged: resolveToString(unresolvedNotifTexts.merged),
body: resolveToString(unresolvedNotifTexts.body),
title: resolveToString(ET`${unresolvedNotifTexts.title}`),
};
if (unresolvedNotifTexts.prefix) {
promises = {
...promises,
prefix: resolveToString(unresolvedNotifTexts.prefix),
};
}
return await promiseAll(promises);
}
function notifRobotextForMessageInfo(
messageInfo: RobotextMessageInfo,
threadInfo: ThreadInfo,
parentThreadInfo: ?ThreadInfo,
): EntityText {
const robotext = robotextForMessageInfo(
messageInfo,
threadInfo,
parentThreadInfo,
);
return robotext.map(entity => {
if (
typeof entity !== 'string' &&
entity.type === 'thread' &&
entity.id === threadInfo.id
) {
return ET.thread({
display: 'shortName',
threadInfo,
possessive: entity.possessive,
});
}
return entity;
});
}
function getNotifCollapseKey(
rawMessageInfo: RawMessageInfo,
messageData: MessageData,
): ?string {
const messageSpec = messageSpecs[rawMessageInfo.type];
return (
messageSpec.notificationCollapseKey?.(rawMessageInfo, messageData) ?? null
);
}
type Unmerged = $ReadOnly<{
body: string,
title: string,
prefix?: string,
...
}>;
type Merged = {
body: string,
title: string,
};
function mergePrefixIntoBody(unmerged: Unmerged): Merged {
const { body, title, prefix } = unmerged;
const merged = prefix ? `${prefix} ${body}` : body;
return { body: merged, title };
}
function getAPNsNotificationTopic(
platformDetails: PlatformDetails,
): APNsNotificationTopic {
if (platformDetails.platform === 'macos') {
return 'app.comm.macos';
}
return platformDetails.codeVersion && platformDetails.codeVersion >= 87
? 'app.comm'
: 'org.squadcal.app';
}
export {
notifRobotextForMessageInfo,
notifTextsForMessageInfo,
notifTextsForEntryCreationOrEdit,
notifTextsForSubthreadCreation,
notifTextsForSidebarCreation,
getNotifCollapseKey,
mergePrefixIntoBody,
getAPNsNotificationTopic,
};

File Metadata

Mime Type
text/x-java
Expires
Fri, Jan 9, 12:52 PM (20 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5905036
Default Alt Text
notif-utils.js (9 KB)

Event Timeline