Merge branch '588-remove-experimental-features-from-room-details' into 'dev'
Implement new "read only" room flag See merge request keanuapp/keanuapp-weblite!290
This commit is contained in:
commit
ac184de2b2
10 changed files with 112 additions and 68 deletions
|
|
@ -1052,6 +1052,8 @@ body {
|
|||
|
||||
.option-text {
|
||||
font-size: 13 * $chat-text-size;
|
||||
border-top: 1px solid #e1e1e1;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.option-title {
|
||||
|
|
|
|||
13
src/assets/icons/ic_eye.vue
Normal file
13
src/assets/icons/ic_eye.vue
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<svg width="21" height="15" viewBox="0 0 21 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M10.592 14.2365H10.5916L10.492 14.2358L10.4083 14.2365C7.13997 14.2365 3.92668 12.2232 0.296132 7.90096C0.101322 7.66909 0.101322 7.33083 0.296132 7.09895C3.92687 2.77668 7.13997 0.763603 10.4081 0.763603L10.5081 0.764277L10.592 0.763603C13.8601 0.763603 17.0734 2.77687 20.704 7.09895C20.8988 7.33083 20.8988 7.66909 20.704 7.90096C17.0732 12.2234 13.8601 14.2365 10.5922 14.2365H10.592ZM10.4919 12.9889L10.5917 12.9896C13.3659 12.9896 16.1761 11.2425 19.4076 7.5001C16.1761 3.7578 13.3663 2.01076 10.5919 2.01076L10.508 2.01144L10.4079 2.01076C7.63368 2.01076 4.82366 3.75784 1.59216 7.5001C4.82366 11.2428 7.63349 12.9896 10.4081 12.9896L10.4919 12.9889Z"
|
||||
fill="black" />
|
||||
<path
|
||||
d="M10.5 11.4495C8.32223 11.4495 6.55066 9.67775 6.55066 7.5C6.55066 5.32209 8.32231 3.55051 10.5 3.55051C12.6779 3.55051 14.4494 5.32224 14.4494 7.5C14.4494 9.67791 12.6776 11.4495 10.5 11.4495ZM10.5 4.7975C9.00982 4.7975 7.79745 6.00987 7.79745 7.5C7.79745 8.99013 9.00982 10.2025 10.5 10.2025C11.9901 10.2025 13.2025 8.99013 13.2025 7.5C13.2025 6.00987 11.9901 4.7975 10.5 4.7975Z"
|
||||
fill="black" />
|
||||
<path
|
||||
d="M8.61532 7.91553C8.38576 7.91553 8.19971 7.72948 8.19971 7.49992C8.19971 6.23147 9.23149 5.19954 10.4999 5.19954C10.7295 5.19954 10.9155 5.38559 10.9155 5.61515C10.9155 5.84471 10.7295 6.03076 10.4999 6.03076C9.6898 6.03076 9.03087 6.68969 9.03087 7.49979C9.03087 7.72956 8.84482 7.91561 8.61526 7.91561L8.61532 7.91553Z"
|
||||
fill="black" />
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -313,6 +313,7 @@
|
|||
"export_room": "Export chat",
|
||||
"user_admin": "Administrator",
|
||||
"user_moderator": "Moderator",
|
||||
"moderation": "Moderation",
|
||||
"experimental_features": "Experimental Features",
|
||||
"room_type": "Room type",
|
||||
"room_type_default": "Default",
|
||||
|
|
@ -321,8 +322,8 @@
|
|||
"file_mode": "File mode",
|
||||
"file_mode_info": "Switches the chat interface to a 'file drop' mode",
|
||||
"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",
|
||||
"read_only_room": "Read Only",
|
||||
"read_only_room_info": "Only admins and moderators are allowed to send to the room.",
|
||||
"message_retention": "Message History",
|
||||
"message_retention_info": "Messages sent within this time frame are viewable by anyone with the link.",
|
||||
"message_retention_none": "Off",
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
canRecordAudio() {
|
||||
return !this.$matrix.currentRoomIsReadOnlyForUser && util.browserCanRecordAudio();
|
||||
return this.$matrix.userCanSendMessageInCurrentRoom && util.browserCanRecordAudio();
|
||||
},
|
||||
currentTime() {
|
||||
return util.formatDuration(this.info ? this.info.currentTime : 0);
|
||||
|
|
@ -414,7 +414,7 @@ export default {
|
|||
},
|
||||
|
||||
micButtonClicked() {
|
||||
if (this.$matrix.currentRoomIsReadOnlyForUser) {
|
||||
if (!this.$matrix.userCanSendMessageInCurrentRoom) {
|
||||
this.showReadOnlyToast = true;
|
||||
setTimeout(() => {
|
||||
this.showReadOnlyToast = false;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@
|
|||
isEmojiQuickReaction= true
|
||||
showMoreMessageOperations({event: selectedEvent, anchor: $event.anchor})
|
||||
" :originalEvent="selectedEvent" :timelineSet="timelineSet"
|
||||
:readOnlyRoom="$matrix.currentRoomIsReadOnlyForUser"
|
||||
:userCanSendReactionAndAnswerPoll="$matrix.userCanSendReactionAndAnswerPollInCurrentRoom"
|
||||
:userCanSendMessage="$matrix.userCanSendMessageInCurrentRoom"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -133,7 +134,7 @@
|
|||
{{ typingMembersString }}
|
||||
</div>
|
||||
</v-row>
|
||||
<v-row class="input-area-inner align-center" v-show="!showRecorder" v-if="!$matrix.currentRoomIsReadOnlyForUser">
|
||||
<v-row class="input-area-inner align-center" v-show="!showRecorder" v-if="$matrix.userCanSendMessageInCurrentRoom">
|
||||
<v-col class="flex-grow-1 flex-shrink-1 ma-0 pa-0">
|
||||
<v-textarea height="undefined" ref="messageInput" full-width auto-grow rows="1" v-model="currentInput"
|
||||
no-resize class="input-area-text" :placeholder="$t('message.your_message')" hide-details
|
||||
|
|
@ -204,7 +205,7 @@
|
|||
<VoiceRecorder :micButtonRef="$refs.mic_button" :ptt="showRecorderPTT" :show="showRecorder"
|
||||
v-on:close="showRecorder = false" v-on:file="onVoiceRecording" />
|
||||
</div>
|
||||
<div v-if="!useVoiceMode && room && $matrix.currentRoomIsReadOnlyForUser" class="input-area-read-only">{{ $t("message.not_allowed_to_send") }}</div>
|
||||
<div v-if="!useVoiceMode && room && !$matrix.userCanSendMessageInCurrentRoom" class="input-area-read-only">{{ $t("message.not_allowed_to_send") }}</div>
|
||||
</v-container>
|
||||
|
||||
<input ref="attachment" type="file" name="attachment" @change="handlePickedAttachment($event)"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ import InteractiveAuth from './InteractiveAuth.vue';
|
|||
import util, { ROOM_TYPE_CHANNEL } from "../plugins/utils";
|
||||
import YouAre from "../components/YouAre.vue";
|
||||
import User from "../models/user";
|
||||
import { CHANNEL_POWER_LEVELS } from "../services/matrix.service";
|
||||
|
||||
export default {
|
||||
name: "CreateChannel",
|
||||
|
|
@ -194,16 +195,10 @@ export default {
|
|||
powerLevels[this.$matrix.currentUserId] = 100;
|
||||
let powerLevelContent = {
|
||||
users: powerLevels,
|
||||
events_default: 50
|
||||
events_default: 50,
|
||||
state_default: 50,
|
||||
events: CHANNEL_POWER_LEVELS
|
||||
}
|
||||
powerLevelContent.events = {
|
||||
"m.room.encrypted": 0, // NOTE! Since practically all events in encrypted rooms get sent as "m.room.encrypted" we need to set
|
||||
// power to 0 here. Otherwise we would not be able to send quick reactions or poll responses...
|
||||
"m.poll.response": 0,
|
||||
"org.matrix.msc3381.poll.response": 0,
|
||||
"m.reaction": 0,
|
||||
"m.room.redaction": 0,
|
||||
};
|
||||
createRoomOptions.initial_state.push(
|
||||
{
|
||||
type: "m.room.power_levels",
|
||||
|
|
|
|||
|
|
@ -168,35 +168,15 @@
|
|||
|
||||
</v-card>
|
||||
|
||||
<v-card class="account ma-3" flat v-if="(iAmAdmin() && availableRoomTypes.length > 1) || canChangeReadOnly() || canViewRetentionPolicy">
|
||||
<v-card-title class="h2 with-right-label"><div>{{ $t("room_info.experimental_features") }}</div><div></div></v-card-title>
|
||||
<v-card-text v-if="iAmAdmin() && availableRoomTypes.length > 1">
|
||||
<div class="d-flex flex-wrap">
|
||||
<div class="col-12 col-md-6 mr-auto pa-0">
|
||||
<div class="option-title">{{ $t('room_info.room_type') }}</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 pa-0">
|
||||
<RoomTypeSelector v-model="roomType" />
|
||||
</div>
|
||||
<v-card class="account ma-3" flat v-if="canChangeReadOnly()">
|
||||
<v-card-title class="h2">{{ $t("room_info.moderation") }}</v-card-title>
|
||||
<v-card-text class="" v-if="canChangeReadOnly()">
|
||||
<div class="with-right-label" style="align-items:center;height:36px">
|
||||
<div class="option-title"><v-icon>$vuetify.icons.ic_eye</v-icon> {{ $t('room_info.read_only_room') }}</div>
|
||||
<v-switch class="ma-0" v-model="readOnlyRoom"></v-switch>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-text class="with-right-label" v-if="canChangeReadOnly()">
|
||||
<div>
|
||||
<div class="option-title">{{ $t('room_info.read_only_room') }}</div>
|
||||
<div class="option-text">{{ $t('room_info.read_only_room_info') }}</div>
|
||||
</div>
|
||||
<v-switch
|
||||
v-model="readOnlyRoom"
|
||||
></v-switch>
|
||||
</v-card-text>
|
||||
<v-card-text class="with-right-label" style="align-items:center" v-if="canViewRetentionPolicy">
|
||||
<div>
|
||||
<div class="option-title">{{ $t('room_info.message_retention') }}</div>
|
||||
<div class="option-text">{{ $t('room_info.message_retention_info') }}</div>
|
||||
</div>
|
||||
<div class="text-right">{{ messageRetentionDisplay }}</div>
|
||||
<v-btn v-on:click="showMessageRetentionDialog = true" v-if="canChangeRetentionPolicy" icon size="x-small"><v-icon color="black">edit</v-icon></v-btn>
|
||||
</v-card-text>
|
||||
<div class="option-text">{{ $t('room_info.read_only_room_info') }}</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-card class="members ma-3" flat>
|
||||
|
|
@ -308,7 +288,6 @@ import MessageRetentionDialog from "../components/MessageRetentionDialog";
|
|||
import RoomExport from "../components/RoomExport";
|
||||
import RoomAvatarPicker from "../components/RoomAvatarPicker";
|
||||
import CopyLink from "../components/CopyLink.vue"
|
||||
import RoomTypeSelector from "./RoomTypeSelector.vue";
|
||||
import UserProfileDialog from "./UserProfileDialog.vue"
|
||||
import roomInfoMixin from "./roomInfoMixin";
|
||||
import roomTypeMixin from "./roomTypeMixin";
|
||||
|
|
@ -324,7 +303,6 @@ export default {
|
|||
UserProfileDialog,
|
||||
RoomExport,
|
||||
RoomAvatarPicker,
|
||||
RoomTypeSelector,
|
||||
CopyLink
|
||||
},
|
||||
data() {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
<template>
|
||||
<div :class="{'message-operations':true,'incoming':incoming,'outgoing':!incoming}">
|
||||
<template v-for="(item,index) in getEmojis">
|
||||
<v-btn v-if="index < maxRecents" :key="item.data" id="btn-quick-reaction" icon @click.stop="addQuickReaction(item.data)" class="ma-0 pa-0" dense>
|
||||
<v-btn v-if="userCanSendReactionAndAnswerPoll && index < maxRecents" :key="item.data" id="btn-quick-reaction" icon @click.stop="addQuickReaction(item.data)" class="ma-0 pa-0" dense>
|
||||
<span class="recent-emoji">{{ item.data }}</span>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-btn id="btn-more" icon @click.stop="more" class="ma-0 pa-0">
|
||||
<v-btn v-if="userCanSendReactionAndAnswerPoll" id="btn-more" icon @click.stop="more" class="ma-0 pa-0">
|
||||
<v-icon small> $vuetify.icons.addReaction </v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-if="incoming && !readOnlyRoom" id="btn-reply" icon @click.stop="addReply" class="ma-0 pa-0">
|
||||
<v-btn v-if="userCanSendMessage" id="btn-reply" icon @click.stop="addReply" class="ma-0 pa-0">
|
||||
<v-icon>reply</v-icon>
|
||||
</v-btn>
|
||||
<v-btn id="btn-edit" icon @click.stop="edit" class="ma-0 pa-0" v-if="isEditable">
|
||||
|
|
@ -41,10 +41,16 @@ export default {
|
|||
return []
|
||||
}
|
||||
},
|
||||
readOnlyRoom: {
|
||||
userCanSendMessage: {
|
||||
type: Boolean,
|
||||
default: function () {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
},
|
||||
userCanSendReactionAndAnswerPoll: {
|
||||
type: Boolean,
|
||||
default: function() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<div class="status">{{ event.status }}</div>
|
||||
</div>
|
||||
|
||||
<div class="op-button" ref="opbutton" v-if="!event.isRedacted() && !$matrix.currentRoomIsReadOnlyForUser">
|
||||
<div class="op-button" ref="opbutton" v-if="!event.isRedacted() && $matrix.userCanSendMessageInCurrentRoom">
|
||||
<v-btn id="btn-show-menu" icon @click.stop="showContextMenu($refs.opbutton)">
|
||||
<v-icon>more_vert</v-icon>
|
||||
</v-btn>
|
||||
|
|
|
|||
|
|
@ -2,12 +2,21 @@ import olm from "@matrix-org/olm/olm";
|
|||
global.Olm = olm;
|
||||
import * as sdk from "matrix-js-sdk";
|
||||
import { TimelineWindow, EventTimeline, EventStatus } from "matrix-js-sdk";
|
||||
import util, { STATE_EVENT_ROOM_DELETED } from "../plugins/utils";
|
||||
import util, { STATE_EVENT_ROOM_DELETED, STATE_EVENT_ROOM_TYPE, ROOM_TYPE_CHANNEL, ROOM_TYPE_FILE_MODE, ROOM_TYPE_VOICE_MODE, ROOM_TYPE_DEFAULT } from "../plugins/utils";
|
||||
import User from "../models/user";
|
||||
|
||||
const LocalStorageCryptoStore =
|
||||
require("matrix-js-sdk/lib/crypto/store/localStorage-crypto-store").LocalStorageCryptoStore;
|
||||
|
||||
export const CHANNEL_POWER_LEVELS = {
|
||||
"m.room.encrypted": 0, // NOTE! Since practically all events in encrypted rooms get sent as "m.room.encrypted" we need to set
|
||||
// power to 0 here. Otherwise we would not be able to send quick reactions or poll responses...
|
||||
"m.poll.response": 0,
|
||||
"org.matrix.msc3381.poll.response": 0,
|
||||
"m.reaction": 0,
|
||||
"m.room.redaction": 0,
|
||||
};
|
||||
|
||||
export default {
|
||||
install(Vue, options) {
|
||||
if (!options || !options.store) {
|
||||
|
|
@ -37,7 +46,8 @@ export default {
|
|||
userDisplayName: null,
|
||||
userAvatar: null,
|
||||
currentRoom: null,
|
||||
currentRoomIsReadOnlyForUser: false,
|
||||
userCanSendMessageInCurrentRoom: true,
|
||||
userCanSendReactionAndAnswerPollInCurrentRoom: true,
|
||||
currentRoomBeingPurged: false,
|
||||
notificationCount: 0,
|
||||
};
|
||||
|
|
@ -106,9 +116,11 @@ export default {
|
|||
immediate: true,
|
||||
handler(room) {
|
||||
if (room) {
|
||||
this.currentRoomIsReadOnlyForUser = this.isReadOnlyRoomForUser(room.roomId, this.currentUserId);
|
||||
this.userCanSendMessageInCurrentRoom = this.userCanSendMessageInRoom(room.roomId, this.currentUserId);
|
||||
this.userCanSendReactionAndAnswerPollInCurrentRoom = this.userCanSendReactionAndAnswerPollInRoom(room.roomId, this.currentUserId);
|
||||
} else {
|
||||
this.currentRoomIsReadOnlyForUser = false;
|
||||
this.userCanSendMessageInCurrentRoom = true;
|
||||
this.userCanSendReactionAndAnswerPollInCurrentRoom = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -388,7 +400,8 @@ export default {
|
|||
case "m.room.power_levels":
|
||||
{
|
||||
if (this.currentRoom && event.getRoomId() == this.currentRoom.roomId) {
|
||||
this.currentRoomIsReadOnlyForUser = this.isReadOnlyRoomForUser(event.getRoomId(), this.currentUserId);
|
||||
this.userCanSendMessageInCurrentRoom = this.userCanSendMessageInRoom(event.getRoomId(), this.currentUserId);
|
||||
this.userCanSendReactionAndAnswerPollInCurrentRoom = this.userCanSendReactionAndAnswerPollInRoom(event.getRoomId(), this.currentUserId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -673,6 +686,9 @@ export default {
|
|||
if (room && room.currentState) {
|
||||
const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
|
||||
if (powerLevelEvent) {
|
||||
if (this.roomType(roomId) == ROOM_TYPE_CHANNEL) {
|
||||
return Object.keys(powerLevelEvent.getContent().events).length == 0;
|
||||
}
|
||||
return powerLevelEvent.getContent().events_default > 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -680,16 +696,6 @@ export default {
|
|||
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);
|
||||
|
|
@ -697,13 +703,55 @@ export default {
|
|||
const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
|
||||
if (powerLevelEvent) {
|
||||
let content = powerLevelEvent.getContent();
|
||||
content.events_default = readOnly ? 50 : 0;
|
||||
if (this.roomType(roomId) == ROOM_TYPE_CHANNEL) {
|
||||
content.events = readOnly ? {} : CHANNEL_POWER_LEVELS
|
||||
} else {
|
||||
content.events_default = readOnly ? 50 : 0;
|
||||
}
|
||||
this.matrixClient.sendStateEvent(room.roomId, "m.room.power_levels", content);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
userCanSendMessageInRoom(roomId, userId) {
|
||||
if (this.matrixClient && roomId && userId) {
|
||||
const room = this.getRoom(roomId);
|
||||
if (room && room.currentState) {
|
||||
let isAdmin = room.currentState.maySendEvent("m.room.power_levels", this.currentUserId);
|
||||
return isAdmin || (this.roomType(roomId) != ROOM_TYPE_CHANNEL && !this.isReadOnlyRoom(roomId));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
userCanSendReactionAndAnswerPollInRoom(roomId, userId) {
|
||||
if (this.matrixClient && roomId && userId) {
|
||||
const room = this.getRoom(roomId);
|
||||
if (room && room.currentState) {
|
||||
let isAdmin = room.currentState.maySendEvent("m.room.power_levels", this.currentUserId);
|
||||
return isAdmin || !this.isReadOnlyRoom(roomId);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
roomType(roomId) {
|
||||
if (this.matrixClient && roomId) {
|
||||
const room = this.getRoom(roomId);
|
||||
if (room && room.currentState) {
|
||||
const roomTypeEvent = room.currentState.getStateEvents(STATE_EVENT_ROOM_TYPE, "") || room.currentState.getStateEvents("m.room.create", "");
|
||||
if (roomTypeEvent) {
|
||||
const roomType = roomTypeEvent.getContent().type;
|
||||
if ([ROOM_TYPE_FILE_MODE, ROOM_TYPE_VOICE_MODE, ROOM_TYPE_CHANNEL].includes(roomType)) {
|
||||
return roomType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ROOM_TYPE_DEFAULT;
|
||||
},
|
||||
|
||||
/**
|
||||
* Purge the room with the given id! This means:
|
||||
* - Make room invite only
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue