diff --git a/src/assets/config.json b/src/assets/config.json
index a816ef7..1d12445 100644
--- a/src/assets/config.json
+++ b/src/assets/config.json
@@ -40,5 +40,6 @@
}
}
],
- "experimental_voice_mode": true
+ "experimental_voice_mode": true,
+ "experimental_read_only_room": true
}
\ No newline at end of file
diff --git a/src/assets/css/chat.scss b/src/assets/css/chat.scss
index c0efad1..41f8419 100644
--- a/src/assets/css/chat.scss
+++ b/src/assets/css/chat.scss
@@ -295,6 +295,19 @@ body {
}
}
+ .input-area-read-only {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(white, 0.8);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 40;
+ }
+
@media #{map-get($display-breakpoints, 'sm-and-down')} {
position: fixed;
bottom: 0px;
@@ -1466,6 +1479,21 @@ body {
.mic-button {
z-index: 0;
}
+ .mic-button.dimmed {
+ opacity: 0.5;
+ }
+ .toast-read-only {
+ position: fixed;
+ left: 10px;
+ right: 10px;
+ bottom: 10px;
+ background-color: rgba(black, 0.7);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 40;
+ color: white;
+ }
}
.audio-layout.voice-recorder {
diff --git a/src/assets/translations/en.json b/src/assets/translations/en.json
index 6b6924e..b7ce9d2 100644
--- a/src/assets/translations/en.json
+++ b/src/assets/translations/en.json
@@ -82,7 +82,8 @@
"reply_poll": "Poll",
"time_ago": "Today | Yesterday | {count} days ago",
"outgoing_message_deleted_text": "You deleted this message.",
- "incoming_message_deleted_text": "This message was deleted."
+ "incoming_message_deleted_text": "This message was deleted.",
+ "not_allowed_to_send": "Only admins and moderators are allowed to send to the room"
},
"room": {
"invitations": "You have no invitations | You have 1 invitation | You have {count} invitations",
@@ -249,7 +250,9 @@
"experimental_features": "Experimental Features",
"voice_mode": "Voice mode",
"voice_mode_info": "Switches the chat interface to a 'listen and record' mode",
- "download_chat": "Download chat"
+ "download_chat": "Download chat",
+ "read_only_room": "Read only room",
+ "read_only_room_info": "Only admins and moderators are allowed to send to the room"
},
"room_info_sheet": {
"this_room": "This room",
diff --git a/src/components/AudioLayout.vue b/src/components/AudioLayout.vue
index d3b9920..735573e 100644
--- a/src/components/AudioLayout.vue
+++ b/src/components/AudioLayout.vue
@@ -56,12 +56,14 @@
-
+
mic
expand_more
+
+ {{ $t("message.not_allowed_to_send") }}
@@ -111,6 +113,7 @@ export default {
playing: false,
analyzer: null,
analyzerDataArray: null,
+ showReadOnlyToast: false,
};
},
mounted() {
@@ -162,12 +165,7 @@ export default {
},
computed: {
canRecordAudio() {
- if (this.room) {
- const myUserId = this.$matrix.currentUserId;
- const me = this.room.getMember(myUserId);
- return me && me.powerLevelNorm > 0 && util.browserCanRecordAudio();
- }
- return false;
+ return !this.$matrix.currentRoomIsReadOnlyForUser && util.browserCanRecordAudio();
},
currentTime() {
return util.formatDuration(this.playTime);
@@ -448,6 +446,17 @@ export default {
}
return null;
},
+
+ micButtonClicked() {
+ if (this.$matrix.currentRoomIsReadOnlyForUser) {
+ this.showReadOnlyToast = true;
+ setTimeout(() => {
+ this.showReadOnlyToast = false;
+ }, 3000);
+ } else {
+ this.$emit('start-recording');
+ }
+ }
}
};
diff --git a/src/components/Chat.vue b/src/components/Chat.vue
index e95919f..a286a2c 100644
--- a/src/components/Chat.vue
+++ b/src/components/Chat.vue
@@ -6,7 +6,7 @@
:timelineSet="timelineSet"
:readMarker="readMarker"
:recordingMembers="typingMembers"
- v-on:start-recording="showRecorder = true"
+ v-on:start-recording="setShowRecorder()"
v-on:loadnext="handleScrolledToBottom(false)"
v-on:loadprevious="handleScrolledToTop()"
v-on:mark-read="sendRR"
@@ -177,6 +177,7 @@
+ {{ $t("message.not_allowed_to_send") }}
{
this.chatContainer.parentElement.removeChild(div);
}, 3000);
+ },
+ setShowRecorder() {
+ if (this.canRecordAudio) {
+ this.showRecorder = true;
+ } else {
+ this.showNoRecordingAvailableDialog = true;
+ }
}
+
},
};
diff --git a/src/components/CreateRoom.vue b/src/components/CreateRoom.vue
index 85d08ea..ebf4288 100644
--- a/src/components/CreateRoom.vue
+++ b/src/components/CreateRoom.vue
@@ -43,13 +43,13 @@
v-on:keyup.enter="$refs.create.focus()" :disabled="step > steps.INITIAL" solo>
-
+
{{ $t("new_room.options") }}
expand_more
expand_less
-
+
{{ $t('room_info.voice_mode') }}
@@ -58,6 +58,15 @@
+
+
+
+
{{ $t('room_info.read_only_room') }}
+
{{ $t('room_info.read_only_room_info') }}
+
+
+
+
{{ roomCreationErrorMsg }}
@@ -259,6 +268,7 @@ export default {
roomCreationErrorMsg: "",
showOptions: false,
useVoiceMode: false,
+ readOnlyRoom: false,
};
},
@@ -401,7 +411,7 @@ export default {
content: {
history_visibility: "joined"
}
- }
+ },
],
};
} else {
@@ -516,6 +526,20 @@ export default {
}
})
.then(() => {
+
+ // Set power level event. Need to do that here, because we might not have the userId when the options object is created.
+ const powerLevels = {};
+ powerLevels[this.$matrix.currentUserId] = 100;
+ createRoomOptions.initial_state.push(
+ {
+ type: "m.room.power_levels",
+ state_key: "",
+ content: {
+ users: powerLevels,
+ events_default: this.readOnlyRoom ? 50 : 0
+ }
+ });
+
return this.$matrix.matrixClient
.createRoom(createRoomOptions)
.then(({ room_id, room_alias }) => {
diff --git a/src/components/RoomInfo.vue b/src/components/RoomInfo.vue
index 41591f4..df17c22 100644
--- a/src/components/RoomInfo.vue
+++ b/src/components/RoomInfo.vue
@@ -165,9 +165,9 @@
-
+
{{ $t("room_info.experimental_features") }}
-
+
{{ $t('room_info.voice_mode') }}
{{ $t('room_info.voice_mode_info') }}
@@ -176,6 +176,15 @@
v-model="useVoiceMode"
>
+
+
+
{{ $t('room_info.read_only_room') }}
+
{{ $t('room_info.read_only_room_info') }}
+
+
+
@@ -389,6 +398,14 @@ export default {
this.$matrix.matrixClient.setRoomTag(this.room.roomId, "ui_options", options);
}
},
+ },
+ readOnlyRoom: {
+ get: function () {
+ return this.$matrix.isReadOnlyRoom(this.room.roomId);
+ },
+ set: function (readOnly) {
+ this.$matrix.setReadOnlyRoom(this.room.roomId, readOnly);
+ },
}
},
@@ -591,6 +608,16 @@ export default {
}
return false;
},
+ /**
+ * Return true if we can change power levels in the room, i.e. make read only room
+ */
+ canChangeReadOnly() {
+ if (!this.$config.experimental_read_only_room) { return false; }
+ if (this.room) {
+ return this.room.currentState && this.room.currentState.maySendStateEvent("m.room.power_levels", this.$matrix.currentUserId);
+ }
+ return false;
+ },
// TODO - following power level comparisons assume that default power levels are used in the room!
isAdmin(member) {
return member.powerLevelNorm > 50;
diff --git a/src/services/matrix.service.js b/src/services/matrix.service.js
index 810d15c..56f186d 100644
--- a/src/services/matrix.service.js
+++ b/src/services/matrix.service.js
@@ -37,6 +37,7 @@ export default {
userDisplayName: null,
userAvatar: null,
currentRoom: null,
+ currentRoomIsReadOnlyForUser: false,
currentRoomBeingPurged: false,
notificationCount: 0,
};
@@ -93,6 +94,7 @@ export default {
immediate: true,
handler(roomId) {
this.currentRoom = this.getRoom(roomId);
+ this.currentRoomIsReadOnlyForUser = this.isReadOnlyRoomForUser(roomId, this.currentUserId);
},
},
},
@@ -355,6 +357,14 @@ export default {
}
}
break;
+
+ case "m.room.power_levels":
+ {
+ if (this.currentRoom && event.getRoomId() == this.currentRoom.roomId) {
+ this.currentRoomIsReadOnlyForUser = this.isReadOnlyRoomForUser(event.getRoomId(), this.currentUserId);
+ }
+ }
+ break;
}
this.updateNotificationCount();
},
@@ -550,6 +560,47 @@ export default {
}
},
+ isReadOnlyRoom(roomId) {
+ if (this.matrixClient && roomId) {
+ const room = this.getRoom(roomId);
+ if (room && room.currentState) {
+ const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
+ if (powerLevelEvent) {
+ return powerLevelEvent.getContent().events_default > 0
+ }
+ }
+ }
+ return false;
+ },
+
+ isReadOnlyRoomForUser(roomId, userId) {
+ if (this.matrixClient && roomId && userId) {
+ const room = this.getRoom(roomId);
+ if (room && room.currentState) {
+ return !room.currentState.maySendMessage(userId)
+ }
+ }
+ return false;
+ },
+
+ setReadOnlyRoom(roomId, readOnly) {
+ if (this.matrixClient && roomId) {
+ const room = this.getRoom(roomId);
+ if (room && room.currentState) {
+ const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
+ if (powerLevelEvent) {
+ let content = powerLevelEvent.getContent();
+ content.events_default = readOnly ? 50 : 0;
+ this.matrixClient.sendStateEvent(
+ room.roomId,
+ "m.room.power_levels",
+ content
+ );
+ }
+ }
+ }
+ },
+
/**
* Purge the room with the given id! This means:
* - Make room invite only