Get dm link
This commit is contained in:
parent
0795c25654
commit
00f95adb09
14 changed files with 422 additions and 26 deletions
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>
|
||||
Loading…
Add table
Add a link
Reference in a new issue