Get dm link
This commit is contained in:
parent
0795c25654
commit
00f95adb09
14 changed files with 422 additions and 26 deletions
|
|
@ -78,7 +78,7 @@
|
|||
<v-icon class="clickable" @click="loadNext" color="white" size="28">expand_more</v-icon>
|
||||
</div>
|
||||
|
||||
<div v-if="showReadOnlyToast" class="toast-read-only">{{ $t("message.not_allowed_to_send") }}</div>
|
||||
<div v-if="showReadOnlyToast" class="toast-at-bottom visible">{{ $t("message.not_allowed_to_send") }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,12 @@
|
|||
<v-row cols="12" class="qr-container ma-3">
|
||||
<v-col cols="auto">
|
||||
<canvas
|
||||
@click.stop="showFullScreenQR = true"
|
||||
v-longTap:250="[
|
||||
()=> {
|
||||
showFullScreenQR = true;
|
||||
},
|
||||
(el) => { $emit('long-tap', el); }
|
||||
]"
|
||||
ref="roomQr"
|
||||
class="qr"
|
||||
id="room-qr"
|
||||
|
|
@ -32,7 +37,7 @@
|
|||
</v-container>
|
||||
</v-card>
|
||||
</v-expand-transition>
|
||||
<QRCodePopup :show="showFullScreenQR" :message="locationLink" @close="showFullScreenQR = false" />
|
||||
<QRCodePopup :show="showFullScreenQR" :message="locationLink" :title="popupTitle" @close="showFullScreenQR = false" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -51,7 +56,12 @@ export default {
|
|||
i18nCopyLinkKey: {
|
||||
type: String,
|
||||
default: 'copy_link'
|
||||
},
|
||||
i18nQrPopupTitleKey: {
|
||||
type: String,
|
||||
default: 'room_info.scan_code'
|
||||
}
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -59,6 +69,11 @@ export default {
|
|||
showFullScreenQR: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
popupTitle() {
|
||||
return this.$t(this.i18nQrPopupTitleKey);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
copyRoomLink() {
|
||||
if(this.locationLinkCopied) return
|
||||
|
|
@ -87,7 +102,7 @@ export default {
|
|||
{
|
||||
type: "image/png",
|
||||
margin: 1,
|
||||
width: 60,
|
||||
width: canvas.getBoundingClientRect().width,
|
||||
},
|
||||
function (error) {
|
||||
if (error) console.error(error);
|
||||
|
|
|
|||
254
src/components/GetLink.vue
Normal file
254
src/components/GetLink.vue
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
<template>
|
||||
<div class="pa-4 getlink-root fill-height">
|
||||
<div v-if="!loggedIn">
|
||||
<div class="getlink-title">{{ $t("getlink.title") }}</div>
|
||||
<div color="rgba(255,255,255,0.1)" class="text-center">
|
||||
<v-form v-model="isValid">
|
||||
<v-text-field v-model="user.user_id" :label="$t('login.username')" color="black" background-color="white" solo
|
||||
:rules="[(v) => !!v || $t('login.username_required')]" :error="userErrorMessage != null"
|
||||
:error-messages="userErrorMessage" required v-on:keyup.enter="onUsernameEnter" v-on:keydown="hasError = false"
|
||||
v-on:blur="onUsernameBlur"></v-text-field>
|
||||
|
||||
<div class="error--text" v-if="loadingLoginFlows">Loading login flows...</div>
|
||||
|
||||
<v-text-field v-show="showPasswordField" solo v-model="user.password"
|
||||
:append-icon="showPassword1 ? 'visibility' : 'visibility_off'" :hint="$t('global.password_hint')"
|
||||
:rules="[(v) => v ? !!v.match(passwordValidation) || $t('global.password_hint') : true]"
|
||||
:label="$t('getlink.password')" counter="20" maxlength="20" :type="showPassword1 ? 'text' : 'password'"
|
||||
@click:append="showPassword1 = !showPassword1" ref="password" :error="passErrorMessage != null"
|
||||
:error-messages="passErrorMessage" required v-on:keydown="hasError = false"
|
||||
v-on:keyup.enter="onPasswordEntered" v-on:blur="onPasswordEntered" />
|
||||
<v-text-field v-show="showPasswordConfirmField" solo v-model="passwordConfirm"
|
||||
:append-icon="showPassword2 ? 'visibility' : 'visibility_off'"
|
||||
:rules="[(v) => v === user.password || $t('global.password_didnot_match')]"
|
||||
:label="$t('getlink.password_repeat')" counter="20" maxlength="20"
|
||||
:type="showPassword2 ? 'text' : 'password'" @click:append="showPassword2 = !showPassword2"
|
||||
ref="passwordConfirm" :error="passErrorMessage != null" :error-messages="passErrorMessage" required
|
||||
v-on:keydown="hasError = false" v-on:keyup.enter="() => {
|
||||
if (isValid && !loading) {
|
||||
handleLogin();
|
||||
}
|
||||
}
|
||||
" />
|
||||
|
||||
<div class="error--text" v-if="hasError">{{ this.message }}</div>
|
||||
|
||||
<interactive-auth ref="interactiveAuth" />
|
||||
<!--
|
||||
<v-checkbox
|
||||
id="chk-remember-me"
|
||||
class="mt-0"
|
||||
v-model="rememberMe"
|
||||
@change="onRememberMe"
|
||||
:label="$t('join.remember_me')"
|
||||
/>
|
||||
-->
|
||||
<v-btn id="btn-login" :disabled="!isValid || !passwordsSetAndMatching || loading || loadingLoginFlows" color="primary" depressed block
|
||||
@click.stop="handleLogin" :loading="loading" class="filled-button mt-4">{{ $t("getlink.create") }}</v-btn>
|
||||
</v-form>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else style="position:relative" class="getlink-loggedin">
|
||||
<!-- Logged in/account created -->
|
||||
<div class="getlink-title">{{ $t("getlink.hello", { user: $matrix.currentUserDisplayName }) }}</div>
|
||||
<div class="getlink-subtitle">{{ $t("getlink.ready_to_share") }}</div>
|
||||
<copy-link ref="qr" :locationLink="directMessageLink" i18nQrPopupTitleKey="getlink.scan_title"
|
||||
v-on:long-tap="copyQRImage">
|
||||
<v-img v-if="shareSupported" class="clickable getlink-share" src="@/assets/icons/ic_share.svg"
|
||||
@click="shareLink" />
|
||||
</copy-link>
|
||||
<v-btn color="black" depressed @click.stop="goHome" class="outlined-button">{{ $t("getlink.continue") }}</v-btn>
|
||||
|
||||
</div>
|
||||
<div :class="{ 'toast-at-bottom': true, 'visible': showQRCopiedToast }">{{ $t("getlink.qr_image_copied") }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import User from "../models/user";
|
||||
import rememberMeMixin from "./rememberMeMixin";
|
||||
import * as sdk from "matrix-js-sdk";
|
||||
import logoMixin from "./logoMixin";
|
||||
import InteractiveAuth from './InteractiveAuth.vue';
|
||||
import CopyLink from "./CopyLink.vue"
|
||||
import utils from "../plugins/utils";
|
||||
|
||||
export default {
|
||||
name: "GetLink",
|
||||
mixins: [rememberMeMixin, logoMixin],
|
||||
components: { InteractiveAuth, CopyLink },
|
||||
data() {
|
||||
return {
|
||||
user: new User(this.$config.defaultServer, "", ""),
|
||||
isValid: false,
|
||||
loading: false,
|
||||
message: "",
|
||||
userErrorMessage: null,
|
||||
passErrorMessage: null,
|
||||
hasError: false,
|
||||
currentLoginServer: "",
|
||||
loadingLoginFlows: false,
|
||||
loginFlows: null,
|
||||
showPasswordField: false,
|
||||
showPasswordConfirmField: false,
|
||||
passwordConfirm: "",
|
||||
showPassword1: false,
|
||||
showPassword2: false,
|
||||
passwordValidation: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{12,20}$/,
|
||||
showQRCopiedToast: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
loggedIn() {
|
||||
return this.$store.state.auth.status.loggedIn;
|
||||
},
|
||||
currentUser() {
|
||||
return this.$store.state.auth.user;
|
||||
},
|
||||
showCloseButton() {
|
||||
return this.$navigation && this.$navigation.canPop();
|
||||
},
|
||||
directMessageLink() {
|
||||
return `${window.location.origin + window.location.pathname}#/user/${encodeURIComponent(this.$matrix.currentUser.user_id)}`
|
||||
},
|
||||
shareSupported() {
|
||||
return !!navigator.share;
|
||||
},
|
||||
passwordsSetAndMatching() {
|
||||
return this.user.password.match(this.passwordValidation) && this.user.password == this.passwordConfirm;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
user: {
|
||||
handler() {
|
||||
// Reset manual errors
|
||||
this.userErrorMessage = null;
|
||||
this.passErrorMessage = null;
|
||||
},
|
||||
deep: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goHome() {
|
||||
this.$navigation.push({ name: "Home" }, -1);
|
||||
},
|
||||
handleLogin() {
|
||||
if (this.user.user_id && this.user.password) {
|
||||
// Reset errors
|
||||
this.message = null;
|
||||
|
||||
// Is it a full matrix user id? Modify a copy, so that the UI will still show the full ID.
|
||||
const userDisplayName = this.user.user_id;
|
||||
|
||||
var user = Object.assign({}, this.user);
|
||||
user.user_id = utils.randomUser(this.$config.userIdPrefix);
|
||||
user.normalize();
|
||||
|
||||
this.loading = true;
|
||||
this.$store.dispatch("createUser", { user, registrationFlowHandler: this.$refs.interactiveAuth.registrationFlowHandler }).then(
|
||||
(ignoreduser) => {
|
||||
this.$matrix.setUserDisplayName(userDisplayName);
|
||||
this.loading = false;
|
||||
},
|
||||
(error) => {
|
||||
console.error(error);
|
||||
this.loading = false;
|
||||
this.message =
|
||||
(error.data && error.data.error) ||
|
||||
error.message ||
|
||||
error.toString();
|
||||
if (error.data && error.data.errcode === 'M_FORBIDDEN') {
|
||||
this.message = this.$i18n.messages[this.$i18n.locale].login.invalid_message;
|
||||
this.hasError = true;
|
||||
}
|
||||
console.log("Message set to ", this.message);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
handleCreateRoom() {
|
||||
this.$navigation.push({ name: "CreateRoom" });
|
||||
},
|
||||
onUsernameEnter() {
|
||||
this.$refs.password.focus();
|
||||
this.onUsernameBlur();
|
||||
},
|
||||
validPassword(pass) {
|
||||
return !!pass;
|
||||
},
|
||||
onPasswordEntered() {
|
||||
if (this.validPassword(this.user.password)) {
|
||||
this.showPasswordConfirmField = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.passwordConfirm.focus();
|
||||
});
|
||||
}
|
||||
},
|
||||
onUsernameBlur() {
|
||||
var user = Object.assign({}, this.user);
|
||||
user.normalize();
|
||||
const server = user.home_server || this.$config.defaultServer;
|
||||
if (server !== this.currentLoginServer) {
|
||||
|
||||
this.showPasswordField = false;
|
||||
this.showPasswordConfirmField = false;
|
||||
|
||||
this.currentLoginServer = server;
|
||||
this.loadingLoginFlows = true;
|
||||
|
||||
const matrixClient = sdk.createClient({ baseUrl: server });
|
||||
matrixClient.loginFlows().then((response) => {
|
||||
console.log("FLOWS", response.flows);
|
||||
this.loginFlows = response.flows.filter(this.supportedLoginFlow);
|
||||
this.loadingLoginFlows = false;
|
||||
if (this.loginFlows.length == 0) {
|
||||
this.message = this.$t('login.no_supported_flow')
|
||||
this.hasError = true;
|
||||
} else {
|
||||
this.message = "";
|
||||
this.hasError = false;
|
||||
this.showPasswordField = this.loginFlows.some(f => f.type == "m.login.password");
|
||||
if (this.showPasswordField) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.password.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
supportedLoginFlow(flow) {
|
||||
return ["m.login.password"].includes(flow.type);
|
||||
},
|
||||
shareLink() {
|
||||
const canvas = this.$refs.qr.$refs.roomQr;
|
||||
if (canvas) {
|
||||
canvas.toBlob((blob) => {
|
||||
let file = new File([blob], encodeURIComponent(this.$matrix.currentUserDisplayName || User.localPart(this.currentUser.user_id)) + ".png", { type: "image/png" })
|
||||
const shareData = { files: [file] };
|
||||
if (navigator.canShare && navigator.canShare(shareData)) {
|
||||
navigator.share(shareData);
|
||||
}
|
||||
}, 'image/png');
|
||||
}
|
||||
},
|
||||
copyQRImage(canvas) {
|
||||
if (canvas && typeof window.ClipboardItem !== "undefined" && navigator.clipboard) {
|
||||
canvas.toBlob(blob => {
|
||||
const clipboardItemInput = new window.ClipboardItem({ "image/png": blob });
|
||||
navigator.clipboard.write([clipboardItemInput]).then(() => {
|
||||
this.showQRCopiedToast = true;
|
||||
setTimeout(() => {
|
||||
this.showQRCopiedToast = false;
|
||||
}, 3000);
|
||||
}).catch(err => console.error(err));
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/getlink.scss";
|
||||
</style>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
<v-card-text class="pa-0">
|
||||
<RoomList
|
||||
showInvites
|
||||
showCreate
|
||||
:showCreate="!$config.hide_add_room_on_home"
|
||||
v-on:newroom="createRoom"
|
||||
/>
|
||||
</v-card-text>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<div class="d-flex justify-center">
|
||||
<canvas ref="qr" class="qr" id="qr" :style="qrStyle"></canvas>
|
||||
</div>
|
||||
<div>{{ $t("room_info.scan_code") }}</div>
|
||||
<div>{{ title }}</div>
|
||||
</div>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
|
@ -32,6 +32,12 @@ export default {
|
|||
return null;
|
||||
},
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: function () {
|
||||
return "";
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue