Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🌟 Implement media filter and both download and upload origin #2206

Merged
merged 6 commits into from
Jun 1, 2022
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
3 changes: 3 additions & 0 deletions twake/backend/node/src/services/channels/entities/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ChannelMember } from "./channel-member";
import { UserObject } from "../../user/web/types";
import { merge } from "lodash";
import search from "./channel.search";
import { ChannelActivity } from "./channel-activity";

@Entity("channels", {
primaryKey: [["company_id", "workspace_id"], "id"],
Expand Down Expand Up @@ -89,6 +90,8 @@ export class Channel {

export class UserChannel extends Channel {
user_member: ChannelMember;
last_activity: ChannelActivity["last_activity"];
last_message: ChannelActivity["last_message"];
}

export class UsersIncludedChannel extends Channel {
Expand Down
13 changes: 13 additions & 0 deletions twake/backend/node/src/services/channels/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ChannelTabPrimaryKey,
DefaultChannel,
DefaultChannelPrimaryKey,
UserChannel,
UsersIncludedChannel,
} from "./entities";
import { ChannelExecutionContext, WorkspaceExecutionContext } from "./types";
Expand Down Expand Up @@ -87,6 +88,18 @@ export interface ChannelService
*/
getDirectChannelsForUsersInCompany(companyId: string, userId: string): Promise<DirectChannel[]>;

/**
* Get all the direct channels in a company for the given user
*
* @param companyId
* @param userId
*/
getChannelsForUsersInWorkspace(
companyId: string,
workspaceId: string,
userId: string,
): Promise<ListResult<UserChannel>>;

/**
* Mark the channel as read for the given user
*
Expand Down
128 changes: 80 additions & 48 deletions twake/backend/node/src/services/channels/services/channel/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,6 @@ export class ChannelServiceImpl implements ChannelService {
context: WorkspaceExecutionContext,
): Promise<ListResult<Channel | UserChannel>> {
let channels: ListResult<Channel | UserChannel>;
let activityPerChannel: Map<string, ChannelActivity>;
const isDirectWorkspace = isDirectChannel(context.workspace);
const isWorkspaceAdmin =
!isDirectWorkspace && userIsWorkspaceAdmin(context.user, context.workspace);
Expand All @@ -447,52 +446,6 @@ export class ChannelServiceImpl implements ChannelService {
}

if ((options?.mine || isDirectWorkspace) && user_id) {
const userChannels = await gr.services.channels.members.listUserChannels(
{ id: user_id },
pagination,
context,
);

channels = await this.channelRepository.find(findFilters, {
$in: [["id", userChannels.getEntities().map(channelMember => channelMember.channel_id)]],
});

if (!channels.isEmpty()) {
const activities = await this.activityRepository.find(findFilters, {
$in: [["channel_id", channels.getEntities().map(channel => channel.id)]],
});

activityPerChannel = new Map<string, ChannelActivity>(
activities.getEntities().map(activity => [activity.channel_id, activity]),
);
} else {
activityPerChannel = new Map<string, ChannelActivity>();
}

channels.mapEntities(<UserChannel>(channel: Channel) => {
const userChannel = find(userChannels.getEntities(), { channel_id: channel.id });

const channelActivity = activityPerChannel.get(channel.id);

return {
...channel,
...{ user_member: userChannel },
last_activity: channelActivity?.last_activity || 0,
last_message: channelActivity?.last_message || {},
} as unknown as UserChannel;
});

if (isDirectWorkspace) {
channels.mapEntities(<UserDirectChannel>(channel: UserChannel) => {
return {
...channel,
...{
members: channel.members || [],
},
} as unknown as UserDirectChannel;
});
}

if (!context.user.application_id && !context.user.server_request) {
localEventBus.publish<ResourceEventsPayload>("channel:list", {
user: context.user,
Expand All @@ -502,7 +455,12 @@ export class ChannelServiceImpl implements ChannelService {
});
}

return new ListResult(channels.type, channels.getEntities(), userChannels.nextPage);
return this.getChannelsForUsersInWorkspace(
context.workspace.company_id,
context.workspace.workspace_id,
context.user.id,
pagination,
);
}

channels = await this.channelRepository.find(findFilters, { pagination });
Expand All @@ -515,6 +473,80 @@ export class ChannelServiceImpl implements ChannelService {
return channels;
}

async getChannelsForUsersInWorkspace(
companyId: string,
workspaceId: string | "direct",
userId: string,
pagination?: Pagination,
): Promise<ListResult<UserChannel>> {
const isDirectWorkspace = isDirectChannel({ workspace_id: workspaceId });
const findFilters: FindFilter = {
company_id: companyId,
workspace_id: workspaceId,
};

const userChannels = await gr.services.channels.members.listUserChannels(
{ id: userId },
pagination,
{
user: {
id: userId,
},
workspace: {
workspace_id: workspaceId,
company_id: companyId,
},
},
);

let activityPerChannel: Map<string, ChannelActivity>;
let channels = await this.channelRepository.find(findFilters, {
$in: [["id", userChannels.getEntities().map(channelMember => channelMember.channel_id)]],
});

if (!channels.isEmpty()) {
const activities = await this.activityRepository.find(findFilters, {
$in: [["channel_id", channels.getEntities().map(channel => channel.id)]],
});

activityPerChannel = new Map<string, ChannelActivity>(
activities.getEntities().map(activity => [activity.channel_id, activity]),
);
} else {
activityPerChannel = new Map<string, ChannelActivity>();
}

channels.mapEntities(<UserChannel>(channel: Channel) => {
const userChannel = find(userChannels.getEntities(), { channel_id: channel.id });

const channelActivity = activityPerChannel.get(channel.id);

return {
...channel,
...{ user_member: userChannel },
last_activity: channelActivity?.last_activity || 0,
last_message: channelActivity?.last_message || {},
} as unknown as UserChannel;
});

if (isDirectWorkspace) {
channels.mapEntities(<UserDirectChannel>(channel: UserChannel) => {
return {
...channel,
...{
members: channel.members || [],
},
} as unknown as UserDirectChannel;
});
}

return new ListResult(
channels.type,
channels.getEntities() as UserChannel[],
userChannels.nextPage,
);
}

async createDirectChannel(directChannel: DirectChannel): Promise<DirectChannel> {
const instance = getDirectChannelInstance(directChannel);
await this.directChannelRepository.save(instance);
Expand Down
57 changes: 25 additions & 32 deletions twake/backend/node/src/services/channels/web/controllers/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { checkCompanyAndWorkspaceForUser } from "../middleware";
import { checkUserBelongsToCompany } from "../../../../utils/company";
import { Message } from "../../../messages/entities/messages";
import { orderBy } from "lodash";
import { DirectChannel } from "../../entities/direct-channel";

const logger = getLogger("channel.controller");

Expand Down Expand Up @@ -496,45 +497,37 @@ export class ChannelCrudController
Querystring: { limit: 100 };
Params: BaseChannelsParameters;
}>,
): Promise<ResourceListResponse<ChannelObject>> {
): Promise<ResourceListResponse<UsersIncludedChannel>> {
const context = getExecutionContext(request);
const companyId = context.workspace.company_id;
const userId = context.user.id;

const workspaces = (
await gr.services.workspaces.getAllForCompany(context.workspace.company_id)
).map(a => a.id);

workspaces.unshift("direct");

const channels = await Promise.all(
workspaces.map(id =>
gr.services.channels.channels
.getAllChannelsInWorkspace(context.workspace.company_id, id)
.then(channels => {
if (id == "direct") {
return Promise.all(
channels.map(
channel =>
gr.services.channels.channels.includeUsersInDirectChannel(
channel,
context.user.id,
) as Promise<UsersIncludedChannel>,
),
);
}
return channels;
}),
),
);
await gr.services.workspaces.getAllForUser({ userId }, { id: companyId })
).map(a => a.workspaceId);

let channels: UserChannel[] = await gr.services.channels.channels
.getChannelsForUsersInWorkspace(companyId, "direct", userId)
.then(list => list.getEntities());

for (const workspaceId of workspaces) {
const workspaceChannels = await gr.services.channels.channels
.getChannelsForUsersInWorkspace(companyId, workspaceId, userId)
.then(list => list.getEntities());
channels = [...channels, ...workspaceChannels];
}

let res: Channel[] = [];
channels.forEach(ch => {
res = res.concat(ch);
});
channels = channels.sort((a, b) => b.last_activity - a.last_activity);
channels = channels.slice(0, 100);

const filledChannels = await gr.services.channels.channels.fillChannelActivities(res);
const userIncludedChannels: UsersIncludedChannel[] = await Promise.all(
channels.map(channel => {
return gr.services.channels.channels.includeUsersInDirectChannel(channel, userId);
}),
);

return {
resources: orderBy(filledChannels, "last_activity", "desc").slice(0, request.query.limit),
resources: userIncludedChannels,
};
}
}
Expand Down
7 changes: 4 additions & 3 deletions twake/backend/node/src/services/files/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Readable } from "stream";
import { Multipart } from "fastify-multipart";
import { Initializable, TwakeServiceProvider } from "../../core/platform/framework";
import { CompanyExecutionContext } from "./web/types";
import { File } from "./entities/file";
import { File, PublicFile } from "./entities/file";
import { PubsubHandler } from "../../core/platform/services/pubsub/api";
import {
DeleteResult,
Expand Down Expand Up @@ -83,10 +83,11 @@ export interface FileServiceAPI extends TwakeServiceProvider, Initializable {

listUserMarkedFiles(
userId: string,
type: "user_upload" | "user_download",
type: "user_upload" | "user_download" | "both",
media: "file_only" | "media_only" | "both",
context: CompanyExecutionContext,
pagination: Pagination,
): Promise<ListResult<File>>;
): Promise<ListResult<PublicFile>>;
}

export interface FilePubsubHandler<InputMessage, OutputMessage>
Expand Down
Loading