Skip to content

Commit

Permalink
Upload group conversation iamge (#1067)
Browse files Browse the repository at this point in the history
  • Loading branch information
dalefukami authored Oct 3, 2023
1 parent 56d40e4 commit c89e061
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/lib/chat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface RealtimeChatEvents {
onConversationListChanged: (conversationIds: string[]) => void;
onUserPresenceChanged: (matrixId: string, isOnline: boolean, lastSeenAt: string) => void;
onRoomNameChanged: (channelId: string, name: string) => void;
onRoomAvatarChanged: (roomId: string, url: string) => void;
onOtherUserJoinedChannel: (channelId: string, user: UserModel) => void;
onOtherUserLeftChannel: (channelId: string, user: UserModel) => void;
}
Expand Down
30 changes: 29 additions & 1 deletion src/lib/chat/matrix-client.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { EventType, GuestAccess, Preset, Visibility } from 'matrix-js-sdk';
import { MatrixClient } from './matrix-client';
import { setAsDM } from './matrix/utils';
import { uploadImage as _uploadImage } from '../../store/channels-list/api';
import { when } from 'jest-when';

jest.mock('./matrix/utils', () => ({ setAsDM: jest.fn().mockResolvedValue(undefined) }));

const mockUploadImage = jest.fn();
jest.mock('../../store/channels-list/api', () => {
return { uploadImage: (...args) => mockUploadImage(...args) };
});

const stubRoom = (attrs = {}) => ({
roomId: 'some-id',
getAvatarUrl: () => '',
Expand Down Expand Up @@ -424,14 +431,35 @@ describe('matrix client', () => {
expect(setAsDM).toHaveBeenCalledWith(expect.anything(), 'test-room', '@first.user');
});

it('sets the conversatio name', async () => {
it('sets the conversation name', async () => {
const createRoom = jest.fn().mockResolvedValue({ room_id: 'new-room-id' });
const client = await subject({ createRoom });

await client.createConversation([{ userId: 'id', matrixId: '@somebody.else' }], 'room-name', null, null);

expect(createRoom).toHaveBeenCalledWith(expect.objectContaining({ name: 'room-name' }));
});

it('uploads the image', async () => {
const createRoom = jest.fn().mockResolvedValue({ room_id: 'new-room-id' });
when(mockUploadImage).calledWith(expect.anything()).mockResolvedValue({ url: 'upload-url' });
const client = await subject({ createRoom });

await client.createConversation(
[{ userId: 'id', matrixId: '@somebody.else' }],
null,
{ name: 'test file' } as File,
null
);

expect(createRoom).toHaveBeenCalledWith(
expect.objectContaining({
initial_state: expect.arrayContaining([
{ type: EventType.RoomAvatar, state_key: '', content: { url: 'upload-url' } },
]),
})
);
});
});

describe('sendMessagesByChannelId', () => {
Expand Down
43 changes: 40 additions & 3 deletions src/lib/chat/matrix-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { config } from '../../config';
import { get } from '../api/rest';
import { MemberNetworks } from '../../store/users/types';
import { getFilteredMembersForAutoComplete, setAsDM } from './matrix/utils';
import { uploadImage } from '../../store/channels-list/api';

enum ConnectionStatus {
Connected = 'connected',
Expand Down Expand Up @@ -151,11 +152,17 @@ export class MatrixClient implements IChatClient {
return { messages: mappedMessages as any, hasMore: false };
}

async createConversation(users: User[], name: string = null, _image: File = null, _optimisticId: string) {
async createConversation(users: User[], name: string = null, image: File = null, _optimisticId: string) {
await this.waitForConnection();
const initial_state = [
const coverUrl = await this.uploadCoverImage(image);

const initial_state: any[] = [
{ type: 'm.room.guest_access', state_key: '', content: { guest_access: GuestAccess.Forbidden } },
];
if (coverUrl) {
initial_state.push({ type: EventType.RoomAvatar, state_key: '', content: { url: coverUrl } });
}

const options: ICreateRoomOpts = {
name,
preset: Preset.TrustedPrivateChat,
Expand Down Expand Up @@ -256,6 +263,9 @@ export class MatrixClient implements IChatClient {
if (event.type === 'm.room.create') {
this.roomCreated(event);
}
if (event.type === EventType.RoomAvatar) {
this.publishRoomAvatarChange(event);
}
});
this.matrix.on(RoomMemberEvent.Membership, async (_event, member) => {
if (member.membership === 'invite' && member.userId === this.userId) {
Expand Down Expand Up @@ -354,6 +364,10 @@ export class MatrixClient implements IChatClient {
}
};

private publishRoomAvatarChange = (event) => {
this.events.onRoomAvatarChanged(event.room_id, event.content?.url);
};

private publishMembershipChange = (event: MatrixEvent) => {
if (event.getType() === EventType.RoomMember) {
const user = this.mapUser(event.getStateKey());
Expand All @@ -370,13 +384,14 @@ export class MatrixClient implements IChatClient {
private mapToGeneralChannel(room: Room) {
const otherMembers = this.getOtherMembersFromRoom(room).map((userId) => this.mapUser(userId));
const name = this.getRoomName(room);
const avatarUrl = this.getRoomAvatar(room);

const messages = this.getAllMessagesFromRoom(room);

return {
id: room.roomId,
name,
icon: null,
icon: avatarUrl,
isChannel: true,
// Even if a member leaves they stay in the member list so this will still be correct
// as zOS considers any conversation to have ever had more than 2 people to not be 1 on 1
Expand Down Expand Up @@ -457,6 +472,15 @@ export class MatrixClient implements IChatClient {
return '';
}

private getRoomAvatar(room: Room): string {
const roomAvatarEvent = room
.getLiveTimeline()
.getState(EventTimeline.FORWARDS)
.getStateEvents(EventType.RoomAvatar, '');

return roomAvatarEvent?.getContent()?.url;
}

private getAllMessagesFromRoom(room: Room) {
const timeline = room.getLiveTimeline().getEvents();
const messages = timeline
Expand Down Expand Up @@ -488,4 +512,17 @@ export class MatrixClient implements IChatClient {

return Object.values(content ?? {}).flat();
}

private async uploadCoverImage(image) {
if (image) {
try {
return (await uploadImage(image)).url;
} catch (error) {
// For now, we will just ignore the error and continue to create the room
// No reason for the image upload to block the room creation
console.error(error);
}
}
return null;
}
}
9 changes: 9 additions & 0 deletions src/store/channels-list/saga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ export function* saga() {
yield takeEveryFromBus(chatBus, ChatEvents.UserJoinedChannel, userJoinedChannelAction);
yield takeEveryFromBus(chatBus, ChatEvents.ConversationListChanged, conversationListChangedAction);
yield takeEveryFromBus(chatBus, ChatEvents.RoomNameChanged, roomNameChangedAction);
yield takeEveryFromBus(chatBus, ChatEvents.RoomAvatarChanged, roomAvatarChangedAction);
yield takeEveryFromBus(chatBus, ChatEvents.OtherUserJoinedChannel, otherUserJoinedChannelAction);
yield takeEveryFromBus(chatBus, ChatEvents.OtherUserLeftChannel, otherUserLeftChannelAction);
}
Expand All @@ -328,6 +329,10 @@ function* roomNameChangedAction(action) {
yield roomNameChanged(action.payload.id, action.payload.name);
}

function* roomAvatarChangedAction(action) {
yield roomAvatarChanged(action.payload.id, action.payload.url);
}

function* otherUserJoinedChannelAction({ payload }) {
yield otherUserJoinedChannel(payload.channelId, payload.user);
}
Expand Down Expand Up @@ -355,6 +360,10 @@ export function* roomNameChanged(id: string, name: string) {
yield put(receiveChannel({ id, name }));
}

export function* roomAvatarChanged(id: string, url: string) {
yield put(receiveChannel({ id, icon: url }));
}

export function* otherUserJoinedChannel(roomId: string, user: User) {
const channel = yield select(rawChannel, roomId);
if (!channel) {
Expand Down
3 changes: 3 additions & 0 deletions src/store/chat/bus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export enum Events {
ConversationListChanged = 'chat/conversationListChanged',
UserPresenceChanged = 'chat/user/presenceChanged',
RoomNameChanged = 'chat/roomNameChanged',
RoomAvatarChanged = 'chat/roomAvatarChanged',
OtherUserJoinedChannel = 'chat/channel/otherUserJoined',
OtherUserLeftChannel = 'chat/channel/otherUserLeft',
}
Expand Down Expand Up @@ -52,6 +53,7 @@ export function createChatConnection(userId, chatAccessToken) {
const onUserPresenceChanged = (matrixId, isOnline, lastSeenAt) =>
emit({ type: Events.UserPresenceChanged, payload: { matrixId, isOnline, lastSeenAt } });
const onRoomNameChanged = (roomId, name) => emit({ type: Events.RoomNameChanged, payload: { id: roomId, name } });
const onRoomAvatarChanged = (roomId, url) => emit({ type: Events.RoomAvatarChanged, payload: { id: roomId, url } });
const onOtherUserJoinedChannel = (channelId, user) =>
emit({ type: Events.OtherUserJoinedChannel, payload: { channelId, user } });
const onOtherUserLeftChannel = (channelId, user) =>
Expand All @@ -71,6 +73,7 @@ export function createChatConnection(userId, chatAccessToken) {
onConversationListChanged,
onUserPresenceChanged,
onRoomNameChanged,
onRoomAvatarChanged,
onOtherUserJoinedChannel,
onOtherUserLeftChannel,
});
Expand Down

0 comments on commit c89e061

Please sign in to comment.