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

Upload group conversation image #1067

Merged
merged 1 commit into from
Oct 3, 2023
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
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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add a helper function for all of these 'get' methods at some point 🙂 will take a look if I happen to work around these

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

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