Merge branch 'dev'
This commit is contained in:
commit
cbad68ecec
21 changed files with 194 additions and 50 deletions
|
|
@ -54,7 +54,6 @@ The app loads runtime configutation from the server at "./config.json" and merge
|
|||
* **show_status_messages** - Whether to show only user joins/leaves and display name updates, or the full range of room status updates. Possible values are "never" (only the above), "moderators" (moderators will see all status updates) or "always" (everyone will see all status updates). Defaults to "always".
|
||||
* **maxSizeAutoDownloads** - Attachments smaller than this will be auto downloaded. Default is 10Mb.
|
||||
|
||||
|
||||
### Sticker short codes - To enable sticker short codes, follow these steps:
|
||||
* Run the "create sticker config" script using "npm run create-sticker-config <path-to-sticker-packs>"
|
||||
* Insert the resulting config blob into the "shortCodeStickers" value of the config file (assets/config.json)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "keanuapp-weblite",
|
||||
"version": "0.1.39",
|
||||
"version": "0.1.40",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "keanuapp-weblite",
|
||||
"version": "0.1.38",
|
||||
"version": "0.1.39",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
type="list-item-avatar-two-line, divider, list-item-three-line, card-heading"
|
||||
v-if="showLoadingScreen"
|
||||
></v-skeleton-loader>
|
||||
<unsupported-browser-alert />
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
|
@ -34,10 +35,14 @@ import stickers from "./plugins/stickers";
|
|||
import { registerServiceWorker, notificationCount, windowNotificationPermission } from "./plugins/notificationAndServiceWorker.js"
|
||||
import logoMixin from "./components/logoMixin";
|
||||
import { mapState } from 'vuex'
|
||||
import UnsupportedBrowserAlert from "./components/UnsupportedBrowserAlert.vue";
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
mixins: [logoMixin],
|
||||
components: {
|
||||
UnsupportedBrowserAlert
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@
|
|||
"days": "1 day ago | {n} days ago"
|
||||
},
|
||||
"close": "close",
|
||||
"notify": "Notify"
|
||||
"notify": "Notify",
|
||||
"different_browser_title": "Try different browser",
|
||||
"different_browser_content": "Some features may break. Copy and open link in a different browser."
|
||||
},
|
||||
"menu": {
|
||||
"start_private_chat": "Direct Message with this user",
|
||||
|
|
@ -142,6 +144,7 @@
|
|||
"join_channel": "All set! Invite people to join you: {link}",
|
||||
"info_retention": "🕓 Messages sent within {time} are viewable by anyone with the link.",
|
||||
"info_retention_user": "🕓 Messages older than {time} will be deleted from the history.",
|
||||
"info_auto_join": "Welcome to {room}.\nYou are joining as {you}.",
|
||||
"change": "Change"
|
||||
},
|
||||
"new_room": {
|
||||
|
|
|
|||
|
|
@ -174,7 +174,8 @@
|
|||
"message_retention_1_day": "1 päivä",
|
||||
"message_retention_8_hours": "8 tuntia",
|
||||
"message_retention_1_hour": "1 tunti",
|
||||
"read_only_room": "Lue Ainoastaan"
|
||||
"read_only_room": "Lue Ainoastaan",
|
||||
"moderation": "Moderointi"
|
||||
},
|
||||
"power_level": {
|
||||
"restricted": "rajoitettu",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@
|
|||
"delete": "Supprimer",
|
||||
"done": "Terminé",
|
||||
"user_kick_and_ban": "Exclure",
|
||||
"user_make_admin": "Rendre administrateur"
|
||||
"user_make_admin": "Rendre administrateur",
|
||||
"direct_chat": "Discussion directe"
|
||||
},
|
||||
"language_display_name": "français",
|
||||
"message": {
|
||||
|
|
@ -222,7 +223,8 @@
|
|||
"message_retention_1_day": "1 jour",
|
||||
"message_retention_8_hours": "8 heures",
|
||||
"message_retention_1_hour": "1 heure",
|
||||
"read_only_room": "Lecture seule"
|
||||
"read_only_room": "Lecture seule",
|
||||
"moderation": "Modération"
|
||||
},
|
||||
"room_info_sheet": {
|
||||
"this_room": "Ce salon",
|
||||
|
|
|
|||
|
|
@ -125,7 +125,8 @@
|
|||
"send_verification": "Invia email di verifica",
|
||||
"invalid_message": "Nome utente o password non validi",
|
||||
"accept_terms": "Accetto",
|
||||
"resend_verification": "Rispedisci email di verifica"
|
||||
"resend_verification": "Rispedisci email di verifica",
|
||||
"token_not_valid": "Token non valido"
|
||||
},
|
||||
"profile": {
|
||||
"title": "Il mio profilo",
|
||||
|
|
@ -218,7 +219,8 @@
|
|||
"message_retention_1_day": "1 giorno",
|
||||
"message_retention_8_hours": "8 ore",
|
||||
"message_retention_1_hour": "1 ora",
|
||||
"read_only_room": "Sola lettura"
|
||||
"read_only_room": "Sola lettura",
|
||||
"moderation": "Moderazione"
|
||||
},
|
||||
"voice_recorder": {
|
||||
"failed_to_record": "Impossibile registrare l’audio",
|
||||
|
|
|
|||
|
|
@ -155,7 +155,8 @@
|
|||
"change": "Mudança",
|
||||
"join_channel": "Tudo pronto! Convide pessoas para se juntarem a você: {link}",
|
||||
"info_retention": "🕓 As mensagens enviadas dentro de {time} podem ser visualizadas por qualquer pessoa com o link.",
|
||||
"info_retention_user": "As mensagens mais antigas que {time} serão excluídas do histórico."
|
||||
"info_retention_user": "As mensagens mais antigas que {time} serão excluídas do histórico.",
|
||||
"info_auto_join": "Bem-vindo à {room}.\nVocê está entrando como {you}."
|
||||
},
|
||||
"new_room": {
|
||||
"new_room": "Nova sala",
|
||||
|
|
|
|||
|
|
@ -268,7 +268,8 @@
|
|||
"info_permissions": "Вы можете в любой момент изменить \"разрешение на присоединение\" в настройках комнаты.",
|
||||
"info": "Добро пожаловать! Вот несколько вещей, которые нужно знать о вашей комнате:",
|
||||
"direct_private_chat": "Личное сообщение",
|
||||
"info_retention_user": "🕓 Сообщения старше {time} будут удалены из истории."
|
||||
"info_retention_user": "🕓 Сообщения старше {time} будут удалены из истории.",
|
||||
"info_auto_join": "Добро пожаловать в {room}.\nВы присоединяетесь как {you}."
|
||||
},
|
||||
"device_list": {
|
||||
"blocked": "Заблокированный",
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@
|
|||
"message_retention_8_hours": "8 小时",
|
||||
"message_retention_1_hour": "1 小时",
|
||||
"message_retention_2_week": "2周",
|
||||
"message_retention_1_week": "1周"
|
||||
"message_retention_1_week": "1周",
|
||||
"moderation": "调节面板"
|
||||
},
|
||||
"leave": {
|
||||
"leave": "离开",
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@
|
|||
<div ref="messageOperationsStrut" class="message-operations-strut">
|
||||
<message-operations ref="messageOperations" :style="opStyle" :emojis="recentEmojis" v-on:close="
|
||||
showContextMenu = false;
|
||||
showContextMenuAnchor = null;
|
||||
" v-if="showMessageOperations" v-on:addreaction="addReaction" v-on:addquickreaction="addQuickReaction"
|
||||
v-on:addreply="addReply(selectedEvent)" v-on:edit="edit(selectedEvent)" v-on:redact="redact(selectedEvent)"
|
||||
v-on:download="download(selectedEvent)" v-on:more="
|
||||
|
|
@ -55,7 +54,7 @@
|
|||
<component :is="roomWelcomeHeader" v-on:close="closeRoomWelcomeHeader"></component>
|
||||
|
||||
<!-- If we have a retention timer, it means we have active message retention. Show header. -->
|
||||
<WelcomeHeaderChannelUser v-if="retentionTimer && !roomWelcomeHeader" />
|
||||
<WelcomeHeaderChannelUser v-if="retentionTimer && !roomWelcomeHeader && newlyJoinedRoom" />
|
||||
|
||||
<div v-for="(event, index) in filteredEvents" :key="event.getId()" :eventId="event.getId()">
|
||||
<!-- DAY Marker, shown for every new day in the timeline -->
|
||||
|
|
@ -479,6 +478,7 @@ export default {
|
|||
|
||||
/** If we just created this room, show a small welcome header with info */
|
||||
hideRoomWelcomeHeader: false,
|
||||
newlyJoinedRoom: false,
|
||||
|
||||
/** An array of recent emojis. Used in the "message operations" popup. */
|
||||
recentEmojis: [],
|
||||
|
|
@ -831,6 +831,7 @@ export default {
|
|||
this.typingMembers = [];
|
||||
this.initialLoadDone = false;
|
||||
this.hideRoomWelcomeHeader = false;
|
||||
this.newlyJoinedRoom = false;
|
||||
|
||||
// Stop RR timer
|
||||
this.stopRRTimer();
|
||||
|
|
@ -862,8 +863,8 @@ export default {
|
|||
this.onRoomJoined(this.readMarker);
|
||||
}
|
||||
},
|
||||
showMessageOperations() {
|
||||
if (this.showMessageOperations) {
|
||||
showMessageOperations(show) {
|
||||
if (show) {
|
||||
this.$nextTick(() => {
|
||||
// Calculate where to show the context menu.
|
||||
//
|
||||
|
|
@ -958,6 +959,16 @@ export default {
|
|||
},
|
||||
|
||||
onRoomJoined(initialEventId) {
|
||||
// If our own join event is less than a minute old, consider this a "newly joined" room.
|
||||
//
|
||||
// Previously tried to look at initialEventId, but it seems like "this.room.getEventReadUpTo(this.$matrix.currentUserId, false)"
|
||||
// always returns an event id? Strange. I would expect it to be null on a fresh room.
|
||||
//
|
||||
const joinEvent = this.room && this.room.currentState.getStateEvents("m.room.member", this.$matrix.currentUserId);
|
||||
if (joinEvent) {
|
||||
this.newlyJoinedRoom = joinEvent.getLocalAge() < 1 * 60000 /* 1 minute */;
|
||||
}
|
||||
|
||||
// Listen to events
|
||||
this.$matrix.on("Room.timeline", this.onEvent);
|
||||
this.$matrix.on("RoomMember.typing", this.onUserTyping);
|
||||
|
|
@ -1044,7 +1055,7 @@ export default {
|
|||
this.$navigation.push(
|
||||
{
|
||||
name: "Join",
|
||||
params: { roomId: util.sanitizeRoomId(this.roomAliasOrId) },
|
||||
params: { roomId: util.sanitizeRoomId(this.roomAliasOrId), join: this.$route.params.join },
|
||||
},
|
||||
0
|
||||
);
|
||||
|
|
@ -1740,10 +1751,17 @@ export default {
|
|||
|
||||
showContextMenuForEvent(e) {
|
||||
const event = e.event;
|
||||
this.selectedEvent = event;
|
||||
this.updateRecentEmojis();
|
||||
this.showContextMenu = !this.showContextMenu;
|
||||
this.showContextMenuAnchor = e.anchor;
|
||||
if (this.selectedEvent == event) {
|
||||
this.showContextMenu = !this.showContextMenu;
|
||||
} else {
|
||||
this.showContextMenu = false;
|
||||
this.$nextTick(() => {
|
||||
this.selectedEvent = event;
|
||||
this.updateRecentEmojis();
|
||||
this.showContextMenu = true;
|
||||
this.showContextMenuAnchor = e.anchor;
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
showAvatarMenuForEvent(e) {
|
||||
|
|
@ -1761,6 +1779,7 @@ export default {
|
|||
if (this.showContextMenu) {
|
||||
this.showContextMenu = false;
|
||||
this.showContextMenuAnchor = null;
|
||||
this.selectedEvent = null;
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -105,6 +105,9 @@
|
|||
|
||||
<input id="room-avatar-picker" ref="avatar" type="file" name="avatar" @change="handlePickedAvatar($event)"
|
||||
accept="image/*" class="d-none" />
|
||||
|
||||
<input id="user-avatar-picker" ref="useravatar" type="file" name="user-avatar" @change="handlePickedUserAvatar($event)" accept="image/*" class="d-none" />
|
||||
|
||||
<v-dialog v-model="enterRoomDialog" :width="$vuetify.breakpoint.smAndUp ? '50%' : '90%'">
|
||||
<v-card>
|
||||
<v-container v-if="canEditProfile" class="pa-10">
|
||||
|
|
@ -112,9 +115,11 @@
|
|||
<v-col class="py-0">
|
||||
<div class="text-start font-weight-bold">{{ $t("join.choose_name") }}</div>
|
||||
<v-select ref="avatar" :items="availableAvatars" cache-items outlined dense @change="selectAvatar"
|
||||
:value="availableAvatars[0]" single-line autofocus>
|
||||
:value="selectedProfile">
|
||||
<template v-slot:selection>
|
||||
<v-text-field background-color="transparent" solo flat hide-details @click.native.stop="{}"
|
||||
<v-text-field background-color="transparent" solo flat hide-details
|
||||
@click.native.stop="(event) => event.target.focus()"
|
||||
@focus="$event.target.select()"
|
||||
v-model="selectedProfile.name"></v-text-field>
|
||||
</template>
|
||||
<template v-slot:item="data">
|
||||
|
|
@ -126,7 +131,7 @@
|
|||
</v-select>
|
||||
</v-col>
|
||||
<v-col cols="2" class="py-0">
|
||||
<v-avatar @click="showAvatarPickerList">
|
||||
<v-avatar @click="showUserAvatarPicker">
|
||||
<v-img v-if="selectedProfile" :src="selectedProfile.image" />
|
||||
</v-avatar>
|
||||
</v-col>
|
||||
|
|
@ -586,6 +591,21 @@ export default {
|
|||
showAvatarPickerList() {
|
||||
this.$refs.avatar.$refs.input.click();
|
||||
},
|
||||
|
||||
/**
|
||||
* Show picker to select user avatar file
|
||||
*/
|
||||
showUserAvatarPicker() {
|
||||
if (this.step == steps.INITIAL) {
|
||||
this.$refs.useravatar.click();
|
||||
}
|
||||
},
|
||||
|
||||
handlePickedUserAvatar(event) {
|
||||
util.loadAvatarFromFile(event, (image) => {
|
||||
this.selectedProfile.image = image;
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
</div>
|
||||
|
||||
<hr class="my-10 join-line" />
|
||||
<div class="font-weight-black mb-4" v-if="!currentUser">Choose a name to use.</div>
|
||||
<div class="font-weight-black mb-4" v-if="!currentUser">{{ $t("join.choose_name") }}</div>
|
||||
|
||||
<v-row v-if="canEditProfile">
|
||||
<v-col cols="10" sm="7" class="py-0">
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
outlined
|
||||
dense
|
||||
@change="selectAvatar"
|
||||
:value="availableAvatars[0]"
|
||||
:value="selectedProfile"
|
||||
single-line
|
||||
autofocus
|
||||
>
|
||||
|
|
@ -39,10 +39,8 @@
|
|||
solo
|
||||
flat
|
||||
hide-details
|
||||
@click.native.stop="
|
||||
{
|
||||
}
|
||||
"
|
||||
@click.native.stop="(event) => event.target.focus()"
|
||||
@focus="$event.target.select()"
|
||||
v-model="selectedProfile.name"
|
||||
></v-text-field>
|
||||
</template>
|
||||
|
|
@ -308,7 +306,14 @@ export default {
|
|||
return roomName ? roomName : "";
|
||||
},
|
||||
getRoomInfo() {
|
||||
if (this.roomId.startsWith("#")) {
|
||||
if (this.$route.params.join) {
|
||||
// Auto-join room
|
||||
this.waitingForRoomCreation = true;
|
||||
this.$nextTick(() => {
|
||||
this.handleJoin();
|
||||
});
|
||||
}
|
||||
else if (this.roomId.startsWith("#")) {
|
||||
this.$matrix
|
||||
.getPublicRoomInfo(this.roomId)
|
||||
.then((room) => {
|
||||
|
|
|
|||
53
src/components/UnsupportedBrowserAlert.vue
Normal file
53
src/components/UnsupportedBrowserAlert.vue
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<template>
|
||||
<v-dialog
|
||||
class="ma-0 pa-0"
|
||||
v-model="isUnSupportedBrowser"
|
||||
persistent
|
||||
:width="$vuetify.breakpoint.smAndUp ? '50%' : '90%'"
|
||||
>
|
||||
<div class="dialog-content text-center">
|
||||
<h2 class="dialog-title">{{ $t("global.different_browser_title") }}</h2>
|
||||
<div class="dialog-text">{{ $t("global.different_browser_content") }}</div>
|
||||
<v-card-actions class="pb-0">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
:color="locationUrlCopied ? '#DEE6FF' : 'black'"
|
||||
depressed
|
||||
@click.stop="copyRoomLink1"
|
||||
:class="{'filled-button' : true, 'link-copied-in-place' : locationUrlCopied}"
|
||||
>{{ $t(`room_info.${locationUrlCopied ? 'link_copied' : 'copy_link'}`) }}</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</div>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const UNSUPPORTED_USER_AGENT = [
|
||||
'MicroMessenger' // WeChat
|
||||
]
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
locationUrlCopied: false,
|
||||
locationUrl: window.location.href
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isUnSupportedBrowser() {
|
||||
return UNSUPPORTED_USER_AGENT.some((userAgent) => window.navigator.userAgent.includes(userAgent));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
copyRoomLink1() {
|
||||
if(this.locationUrlCopied) return
|
||||
navigator.clipboard.writeText(this.locationUrl)
|
||||
this.locationUrlCopied = true;
|
||||
setInterval(() => {
|
||||
this.locationUrlCopied = false;
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import utils from "../plugins/utils";
|
||||
import utils, { ROOM_TYPE_CHANNEL } from "../plugins/utils";
|
||||
import roomTypeMixin from "./roomTypeMixin";
|
||||
|
||||
export default {
|
||||
|
|
@ -96,7 +96,8 @@ export default {
|
|||
this.room.getCanonicalAlias(),
|
||||
this.room.roomId,
|
||||
this.room.name,
|
||||
utils.roomDisplayTypeToQueryParam(this.room, this.roomDisplayType)
|
||||
utils.roomDisplayTypeToQueryParam(this.room, this.roomDisplayType),
|
||||
this.roomDisplayType == ROOM_TYPE_CHANNEL /* Auto join for channels */
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -20,12 +20,8 @@
|
|||
<template v-slot:time>
|
||||
<b>{{ messageRetentionDisplay }}</b>
|
||||
</template>
|
||||
</i18n>
|
||||
</div>
|
||||
<div class="mt-2" v-if="roomMessageRetention() > 0">
|
||||
<a href="#" text @click.prevent="showMessageRetentionDialog = true">
|
||||
{{ $t("room_welcome.change") }}
|
||||
</a>
|
||||
</i18n>
|
||||
<a href="#" text @click.prevent="showMessageRetentionDialog = true" style="white-space: pre-line;">{{ $t("room_welcome.change") }}</a>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<v-btn id="btn-got-it" text @click.stop="$emit('close')" class="text-transform-0">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
<template>
|
||||
<div class="created-room-welcome-header">
|
||||
<div style="white-space: pre-line;">
|
||||
{{ $t('room_welcome.info_auto_join',{room: room ? room.name : "",you: $matrix.currentUserDisplayName}) }}
|
||||
<a href="#" text @click.prevent="viewProfile">
|
||||
{{ $t("room_welcome.change") }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="mt-2" v-if="roomMessageRetention() > 0">
|
||||
<i18n path="room_welcome.info_retention_user" tag="span">
|
||||
<template v-slot:time>
|
||||
|
|
@ -25,6 +31,9 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
viewProfile() {
|
||||
this.$navigation.push({ name: "Profile" }, 1);
|
||||
},
|
||||
onMessageRetention(ignoredretention) {
|
||||
this.updateMessageRetention();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -920,7 +920,10 @@ class Util {
|
|||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.target = "_blank";
|
||||
link.download = event.getContent().body || this.$t("fallbacks.download_name");
|
||||
if (!this.isFileTypePDF(event)) {
|
||||
// PDFs are shown inline, not downloaded
|
||||
link.download = event.getContent().body || this.$t("fallbacks.download_name");
|
||||
}
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
setTimeout(function () {
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ const routes = [
|
|||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/room/:roomId?',
|
||||
path: '/room/:join(join/)?:roomId?',
|
||||
name: 'Chat',
|
||||
component: Chat,
|
||||
meta: {
|
||||
includeRoom: true,
|
||||
includeFavicon: true
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/info',
|
||||
|
|
@ -73,7 +73,7 @@ const routes = [
|
|||
props: true
|
||||
},
|
||||
{
|
||||
path: '/join/:roomId?',
|
||||
path: '/join/:join(join/)?:roomId?',
|
||||
name: 'Join',
|
||||
component: Join
|
||||
},
|
||||
|
|
@ -170,7 +170,7 @@ router.beforeEach((to, from, next) => {
|
|||
}
|
||||
});
|
||||
|
||||
router.getRoomLink = function (alias, roomId, roomName, mode) {
|
||||
router.getRoomLink = function (alias, roomId, roomName, mode, autojoin) {
|
||||
let params = {};
|
||||
if ((!alias || roomName.replace(/\s/g, "").toLowerCase() !== util.getRoomNameFromAlias(alias)) && roomName) {
|
||||
// There is no longer a correlation between alias and room name, probably because room name has
|
||||
|
|
@ -181,13 +181,14 @@ router.getRoomLink = function (alias, roomId, roomName, mode) {
|
|||
// Optional mode given, append as "m" query param
|
||||
params["m"] = mode;
|
||||
}
|
||||
const autoJoinSegment = autojoin ? "join/" : "";
|
||||
if (Object.entries(params).length > 0) {
|
||||
const queryString = Object.entries(params)
|
||||
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
||||
.join('&')
|
||||
return window.location.origin + window.location.pathname + "?" + queryString + "#/room/" + encodeURIComponent(util.sanitizeRoomId(alias || roomId));
|
||||
return window.location.origin + window.location.pathname + "?" + queryString + "#/room/" + autoJoinSegment + encodeURIComponent(util.sanitizeRoomId(alias || roomId));
|
||||
}
|
||||
return window.location.origin + window.location.pathname + "#/room/" + encodeURIComponent(util.sanitizeRoomId(alias || roomId));
|
||||
return window.location.origin + window.location.pathname + "#/room/" + autoJoinSegment + encodeURIComponent(util.sanitizeRoomId(alias || roomId));
|
||||
}
|
||||
|
||||
router.getDMLink = function (user, config) {
|
||||
|
|
|
|||
|
|
@ -406,6 +406,17 @@ export default {
|
|||
}
|
||||
break;
|
||||
|
||||
case "m.room.join_rules":
|
||||
{
|
||||
const room = this.matrixClient.getRoom(event.getRoomId());
|
||||
if (room && room.getJoinRule() == "private" && room.selfMembership == "invite") {
|
||||
// We have an invite to a room that's now "private"? This is most probably a deleted DM room.
|
||||
// Reject the invite, i.e. call "leave" on it.
|
||||
this.matrixClient.leave(room.roomId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_EVENT_ROOM_DELETED:
|
||||
{
|
||||
const room = this.matrixClient.getRoom(event.getRoomId());
|
||||
|
|
@ -494,7 +505,7 @@ export default {
|
|||
// each time!
|
||||
var updatedRooms = this.matrixClient.getVisibleRooms();
|
||||
updatedRooms = updatedRooms.filter((room) => {
|
||||
return room.selfMembership && (room.selfMembership == "invite" || room.selfMembership == "join");
|
||||
return room.selfMembership && (room.selfMembership == "invite" || room.selfMembership == "join") && room.currentState.getStateEvents(STATE_EVENT_ROOM_DELETED).length == 0;
|
||||
});
|
||||
updatedRooms.forEach((room) => {
|
||||
if (!room.avatar) {
|
||||
|
|
@ -580,7 +591,7 @@ export default {
|
|||
},
|
||||
|
||||
leaveRoom(roomId) {
|
||||
return this.matrixClient.leave(roomId, undefined).then(() => {
|
||||
return this.matrixClient.leave(roomId).then(() => {
|
||||
this.$store.commit("setCurrentRoomId", null);
|
||||
this.rooms = this.rooms.filter((room) => {
|
||||
room.roomId != roomId;
|
||||
|
|
@ -805,7 +816,7 @@ export default {
|
|||
|
||||
//console.log("Purge: set invite only");
|
||||
statusCallback(this.$t("room.purge_set_room_state"));
|
||||
withRetry(() => this.matrixClient.sendStateEvent(roomId, "m.room.join_rules", { join_rule: "invite" }, ""))
|
||||
withRetry(() => this.matrixClient.sendStateEvent(roomId, "m.room.join_rules", { join_rule: "private" }, ""))
|
||||
.then(() => {
|
||||
//console.log("Purge: forbid guest access");
|
||||
return withRetry(() => this.matrixClient.sendStateEvent(
|
||||
|
|
@ -887,6 +898,8 @@ export default {
|
|||
var invited = room.getMembersWithMembership("invite");
|
||||
var allMembers = joined.concat(invited);
|
||||
|
||||
const me = allMembers.find((m) => m.userId == self.currentUserId);
|
||||
|
||||
const kickFirstMember = (members) => {
|
||||
//console.log(`Kicking ${members.length} members`);
|
||||
statusCallback(
|
||||
|
|
@ -904,7 +917,16 @@ export default {
|
|||
} else {
|
||||
// Slight pause to avoid rate limiting.
|
||||
return sleep(0.1)
|
||||
.then(() => withRetry(() => this.matrixClient.kick(roomId, member.userId, "Room Deleted")))
|
||||
.then(() => withRetry(() => {
|
||||
if (member.membership == "invite" && me && me.powerLevel <= member.powerLevel) {
|
||||
// The user is invited, but we can't kick them because of power levels.
|
||||
// Send a new invite with reason set to "Room Deleted".
|
||||
// The client will be sent stripped room state, and can from that see the
|
||||
// join_rule of "private". It will then "leave", i.e. reject the invite.
|
||||
return this.matrixClient.invite(roomId, member.userId, "Room Deleted");
|
||||
}
|
||||
return this.matrixClient.kick(roomId, member.userId, "Room Deleted")
|
||||
}))
|
||||
.then(() => kickFirstMember(members.slice(1)));
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue