Skip to content
Draft
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
73 changes: 73 additions & 0 deletions packages/client/src/clients/feed/feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ class Feed {
}

async markAsSeen(itemOrItems: FeedItemOrItems) {
if (!this.knock.isAuthenticated()) {
this.knock.log("[Feed] Skipping markAsSeen - user not authenticated");
return { entries: [] };
}

const now = new Date().toISOString();
this.optimisticallyPerformStatusUpdate(
itemOrItems,
Expand All @@ -188,6 +193,11 @@ class Feed {
}

async markAllAsSeen() {
if (!this.knock.isAuthenticated()) {
this.knock.log("[Feed] Skipping markAllAsSeen - user not authenticated");
return { entries: [] };
}

// To mark all of the messages as seen we:
// 1. Optimistically update *everything* we have in the store
// 2. We decrement the `unseen_count` to zero optimistically
Expand Down Expand Up @@ -230,6 +240,11 @@ class Feed {
}

async markAsUnseen(itemOrItems: FeedItemOrItems) {
if (!this.knock.isAuthenticated()) {
this.knock.log("[Feed] Skipping markAsUnseen - user not authenticated");
return { entries: [] };
}

this.optimisticallyPerformStatusUpdate(
itemOrItems,
"unseen",
Expand All @@ -241,6 +256,11 @@ class Feed {
}

async markAsRead(itemOrItems: FeedItemOrItems) {
if (!this.knock.isAuthenticated()) {
this.knock.log("[Feed] Skipping markAsRead - user not authenticated");
return { entries: [] };
}

const now = new Date().toISOString();
this.optimisticallyPerformStatusUpdate(
itemOrItems,
Expand All @@ -253,6 +273,11 @@ class Feed {
}

async markAllAsRead() {
if (!this.knock.isAuthenticated()) {
this.knock.log("[Feed] Skipping markAllAsRead - user not authenticated");
return { entries: [] };
}

// To mark all of the messages as read we:
// 1. Optimistically update *everything* we have in the store
// 2. We decrement the `unread_count` to zero optimistically
Expand Down Expand Up @@ -295,6 +320,11 @@ class Feed {
}

async markAsUnread(itemOrItems: FeedItemOrItems) {
if (!this.knock.isAuthenticated()) {
this.knock.log("[Feed] Skipping markAsUnread - user not authenticated");
return { entries: [] };
}

this.optimisticallyPerformStatusUpdate(
itemOrItems,
"unread",
Expand All @@ -309,6 +339,13 @@ class Feed {
itemOrItems: FeedItemOrItems,
metadata?: Record<string, string>,
) {
if (!this.knock.isAuthenticated()) {
this.knock.log(
"[Feed] Skipping markAsInteracted - user not authenticated",
);
return { entries: [] };
}

const now = new Date().toISOString();
this.optimisticallyPerformStatusUpdate(
itemOrItems,
Expand All @@ -332,6 +369,11 @@ class Feed {
TODO: how do we handle rollbacks?
*/
async markAsArchived(itemOrItems: FeedItemOrItems) {
if (!this.knock.isAuthenticated()) {
this.knock.log("[Feed] Skipping markAsArchived - user not authenticated");
return { entries: [] };
}

const state = this.store.getState();

const shouldOptimisticallyRemoveItems =
Expand Down Expand Up @@ -403,6 +445,13 @@ class Feed {
}

async markAllAsArchived() {
if (!this.knock.isAuthenticated()) {
this.knock.log(
"[Feed] Skipping markAllAsArchived - user not authenticated",
);
return { entries: [] };
}

// Note: there is the potential for a race condition here because the bulk
// update is an async method, so if a new message comes in during this window before
// the update has been processed we'll effectively reset the `unseen_count` to be what it was.
Expand Down Expand Up @@ -430,6 +479,13 @@ class Feed {
}

async markAllReadAsArchived() {
if (!this.knock.isAuthenticated()) {
this.knock.log(
"[Feed] Skipping markAllReadAsArchived - user not authenticated",
);
return { entries: [] };
}

// Note: there is the potential for a race condition here because the bulk
// update is an async method, so if a new message comes in during this window before
// the update has been processed we'll effectively reset the `unseen_count` to be what it was.
Expand Down Expand Up @@ -472,6 +528,13 @@ class Feed {
}

async markAsUnarchived(itemOrItems: FeedItemOrItems) {
if (!this.knock.isAuthenticated()) {
this.knock.log(
"[Feed] Skipping markAsUnarchived - user not authenticated",
);
return { entries: [] };
}

const state = this.store.getState();

const items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
Expand Down Expand Up @@ -518,6 +581,11 @@ class Feed {

/* Fetches the feed content, appending it to the store */
async fetch(options: FetchFeedOptions = {}) {
if (!this.knock.isAuthenticated()) {
this.knock.log("[Feed] Skipping fetch - user not authenticated");
return;
}

const { networkStatus, ...state } = this.store.getState();

// If the user is not authenticated, then do nothing
Expand Down Expand Up @@ -608,6 +676,11 @@ class Feed {
}

async fetchNextPage(options: FetchFeedOptions = {}) {
if (!this.knock.isAuthenticated()) {
this.knock.log("[Feed] Skipping fetchNextPage - user not authenticated");
return;
}

// Attempts to fetch the next page of results (if we have any)
const { pageInfo } = this.store.getState();

Expand Down
35 changes: 33 additions & 2 deletions packages/client/src/clients/guide/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,14 @@ export class KnockGuideClient {

async fetch(opts?: { filters?: QueryFilterParams }) {
this.knock.log("[Guide] .fetch");
this.knock.failIfNotAuthenticated();

if (!this.knock.isAuthenticated()) {
this.knock.log("[Guide] Skipping fetch - user not authenticated");
return {
status: "error" as const,
error: new Error("Not authenticated"),
};
}

const queryParams = this.buildQueryParams(opts?.filters);
const queryKey = this.formatQueryKey(queryParams);
Expand Down Expand Up @@ -400,7 +407,12 @@ export class KnockGuideClient {

subscribe() {
if (!this.socket) return;
this.knock.failIfNotAuthenticated();

if (!this.knock.isAuthenticated()) {
this.knock.log("[Guide] Skipping subscribe - user not authenticated");
return;
}

this.knock.log("[Guide] Subscribing to real time updates");

// Ensure a live socket connection if not yet connected.
Expand Down Expand Up @@ -846,6 +858,11 @@ export class KnockGuideClient {
async markAsSeen(guide: GuideData, step: GuideStepData) {
if (step.message.seen_at) return;

if (!this.knock.isAuthenticated()) {
this.knock.log("[Guide] Skipping markAsSeen - user not authenticated");
return;
}

this.knock.log(
`[Guide] Marking as seen (Guide key: ${guide.key}, Step ref:${step.ref})`,
);
Expand Down Expand Up @@ -874,6 +891,13 @@ export class KnockGuideClient {
step: GuideStepData,
metadata?: GenericData,
) {
if (!this.knock.isAuthenticated()) {
this.knock.log(
"[Guide] Skipping markAsInteracted - user not authenticated",
);
return;
}

this.knock.log(
`[Guide] Marking as interacted (Guide key: ${guide.key}; Step ref:${step.ref})`,
);
Expand Down Expand Up @@ -901,6 +925,13 @@ export class KnockGuideClient {
async markAsArchived(guide: GuideData, step: GuideStepData) {
if (step.message.archived_at) return;

if (!this.knock.isAuthenticated()) {
this.knock.log(
"[Guide] Skipping markAsArchived - user not authenticated",
);
return;
}

this.knock.log(
`[Guide] Marking as archived (Guide key: ${guide.key}, Step ref:${step.ref})`,
);
Expand Down
28 changes: 28 additions & 0 deletions packages/client/src/clients/messages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ class MessageClient {
status: MessageEngagementStatus,
options?: UpdateMessageStatusOptions,
): Promise<Message> {
if (!this.knock.isAuthenticated()) {
this.knock.log(
"[Messages] Skipping updateStatus - user not authenticated",
);
throw new Error("Not authenticated");
}

// Metadata is only required for the "interacted" status
const payload =
status === "interacted" && options
Expand All @@ -49,6 +56,13 @@ class MessageClient {
messageId: string,
status: Exclude<MessageEngagementStatus, "interacted">,
): Promise<Message> {
if (!this.knock.isAuthenticated()) {
this.knock.log(
"[Messages] Skipping removeStatus - user not authenticated",
);
throw new Error("Not authenticated");
}

const result = await this.knock.client().makeRequest({
method: "DELETE",
url: `/v1/messages/${messageId}/${status}`,
Expand All @@ -62,6 +76,13 @@ class MessageClient {
status: MessageEngagementStatus | "unseen" | "unread" | "unarchived",
options?: UpdateMessageStatusOptions,
): Promise<Message[]> {
if (!this.knock.isAuthenticated()) {
this.knock.log(
"[Messages] Skipping batchUpdateStatuses - user not authenticated",
);
return [];
}

// Metadata is only required for the "interacted" status
const additionalPayload =
status === "interacted" && options ? { metadata: options.metadata } : {};
Expand All @@ -80,6 +101,13 @@ class MessageClient {
status,
options,
}: BulkUpdateMessagesInChannelProperties): Promise<BulkOperation> {
if (!this.knock.isAuthenticated()) {
this.knock.log(
"[Messages] Skipping bulkUpdateAllStatusesInChannel - user not authenticated",
);
throw new Error("Not authenticated");
}

const result = await this.knock.client().makeRequest({
method: "POST",
url: `/v1/channels/${channelId}/messages/bulk/${status}`,
Expand Down
28 changes: 28 additions & 0 deletions packages/client/src/clients/ms-teams/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ class MsTeamsClient {
}

async authCheck({ tenant: tenantId, knockChannelId }: AuthCheckInput) {
if (!this.instance.isAuthenticated()) {
this.instance.log(
"[MS Teams] Skipping authCheck - user not authenticated",
);
return { status: "not_connected" };
}

const result = await this.instance.client().makeRequest({
method: "GET",
url: `/v1/providers/ms-teams/${knockChannelId}/auth_check`,
Expand All @@ -36,6 +43,13 @@ class MsTeamsClient {
async getTeams(
input: GetMsTeamsTeamsInput,
): Promise<GetMsTeamsTeamsResponse> {
if (!this.instance.isAuthenticated()) {
this.instance.log(
"[MS Teams] Skipping getTeams - user not authenticated",
);
return { ms_teams_teams: [], skip_token: null };
}

const { knockChannelId, tenant: tenantId } = input;
const queryOptions = input.queryOptions || {};

Expand All @@ -62,6 +76,13 @@ class MsTeamsClient {
async getChannels(
input: GetMsTeamsChannelsInput,
): Promise<GetMsTeamsChannelsResponse> {
if (!this.instance.isAuthenticated()) {
this.instance.log(
"[MS Teams] Skipping getChannels - user not authenticated",
);
return { ms_teams_channels: [] };
}

const { knockChannelId, teamId, tenant: tenantId } = input;
const queryOptions = input.queryOptions || {};

Expand All @@ -88,6 +109,13 @@ class MsTeamsClient {
tenant: tenantId,
knockChannelId,
}: RevokeAccessTokenInput) {
if (!this.instance.isAuthenticated()) {
this.instance.log(
"[MS Teams] Skipping revokeAccessToken - user not authenticated",
);
return { status: "not_connected" };
}

const result = await this.instance.client().makeRequest({
method: "PUT",
url: `/v1/providers/ms-teams/${knockChannelId}/revoke_access`,
Expand Down
19 changes: 19 additions & 0 deletions packages/client/src/clients/slack/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ class SlackClient {
}

async authCheck({ tenant, knockChannelId }: AuthCheckInput) {
if (!this.instance.isAuthenticated()) {
this.instance.log("[Slack] Skipping authCheck - user not authenticated");
return { status: "not_connected" };
}

const result = await this.instance.client().makeRequest({
method: "GET",
url: `/v1/providers/slack/${knockChannelId}/auth_check`,
Expand All @@ -31,6 +36,13 @@ class SlackClient {
async getChannels(
input: GetSlackChannelsInput,
): Promise<GetSlackChannelsResponse> {
if (!this.instance.isAuthenticated()) {
this.instance.log(
"[Slack] Skipping getChannels - user not authenticated",
);
return { slack_channels: [], next_cursor: null };
}

const { knockChannelId, tenant } = input;
const queryOptions = input.queryOptions || {};

Expand All @@ -57,6 +69,13 @@ class SlackClient {
}

async revokeAccessToken({ tenant, knockChannelId }: RevokeAccessTokenInput) {
if (!this.instance.isAuthenticated()) {
this.instance.log(
"[Slack] Skipping revokeAccessToken - user not authenticated",
);
return { status: "not_connected" };
}

const result = await this.instance.client().makeRequest({
method: "PUT",
url: `/v1/providers/slack/${knockChannelId}/revoke_access`,
Expand Down
Loading
Loading