parent
08d30e66f6
commit
7c202c2d2e
6 changed files with 145 additions and 89 deletions
|
|
@ -6,7 +6,18 @@
|
|||
.getlink-loggedin {
|
||||
text-align: center;
|
||||
button {
|
||||
min-width: 200px !important;
|
||||
min-width: 200px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.getlink-image {
|
||||
text-align: center;
|
||||
max-width: 325px;
|
||||
max-height: 257px;
|
||||
width: 100%;
|
||||
.v-icon__component {
|
||||
width: unset;
|
||||
height: unset;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -23,6 +34,19 @@
|
|||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.getlink-info {
|
||||
color: #000;
|
||||
text-align: center;
|
||||
font-feature-settings: "clig" off, "liga" off;
|
||||
font-family: "Inter";
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 117%; /* 18.72px */
|
||||
letter-spacing: 0.4px;
|
||||
margin: 15px 9px 40px 9px;
|
||||
}
|
||||
|
||||
.getlink-subtitle {
|
||||
color: #000;
|
||||
text-align: center;
|
||||
|
|
|
|||
32
src/assets/icons/getlink.vue
Normal file
32
src/assets/icons/getlink.vue
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -189,9 +189,9 @@
|
|||
},
|
||||
"getlink": {
|
||||
"title": "Get a Direct Link",
|
||||
"password": "Set your password",
|
||||
"password_repeat": "Confirm your password",
|
||||
"create": "Create",
|
||||
"info": "Direct links give people a secure line of communication with you. To start, choose a screen name to show when people enter a chat with you.",
|
||||
"username": "Enter a screen name (ex: waku)",
|
||||
"next": "Next",
|
||||
"hello": "Hello {user},\nHere’s your Direct Link",
|
||||
"ready_to_share": "It’s ready to share! A new direct room will open each time someone opens the link.",
|
||||
"scan_title": "Scan this code to start a direct chat",
|
||||
|
|
|
|||
|
|
@ -1,50 +1,26 @@
|
|||
<template>
|
||||
<div class="pa-4 getlink-root fill-height">
|
||||
<div v-if="!loggedIn">
|
||||
<div v-if="!loggedIn" class="text-center">
|
||||
<v-icon class="getlink-image">$vuetify.icons.getlink</v-icon>
|
||||
<div class="getlink-title">{{ $t("getlink.title") }}</div>
|
||||
<div class="getlink-info">{{ $t("getlink.info") }}</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
|
||||
<v-text-field v-model="user.user_id" :label="$t('getlink.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="showPasswordFields" 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="showPasswordFields" 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-btn id="btn-login" :disabled="!isValid || loading || loadingLoginFlows" color="primary" depressed block
|
||||
@click.stop="handleLogin" :loading="loading" class="filled-button mt-4">{{ $t("getlink.next") }}</v-btn>
|
||||
<v-btn color="black" depressed text block @click.stop="goToLoginPage" class="text-button">{{ $t("menu.login")
|
||||
}}</v-btn>
|
||||
</v-form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -59,7 +35,8 @@
|
|||
</copy-link>
|
||||
<div class="getlink-buttons">
|
||||
<v-btn color="black" depressed @click.stop="goHome" class="outlined-button">{{ $t("getlink.continue") }}</v-btn>
|
||||
<v-btn color="black" depressed text block @click.stop="getDifferentLink" class="text-button">{{ $t("getlink.different_link") }}</v-btn>
|
||||
<v-btn color="black" depressed text block @click.stop="getDifferentLink" class="text-button">{{
|
||||
$t("getlink.different_link") }}</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="{ 'toast-at-bottom': true, 'visible': showQRCopiedToast }">{{ $t("getlink.qr_image_copied") }}</div>
|
||||
|
|
@ -98,40 +75,30 @@ export default {
|
|||
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: {
|
||||
defaultData() {
|
||||
return {
|
||||
user: new User(this.$config.defaultServer, "", ""),
|
||||
isValid: false,
|
||||
loading: false,
|
||||
message: "",
|
||||
userErrorMessage: null,
|
||||
passErrorMessage: null,
|
||||
hasError: false,
|
||||
currentLoginServer: "",
|
||||
loadingLoginFlows: false,
|
||||
loginFlows: null,
|
||||
showPasswordFields: true,
|
||||
passwordConfirm: "",
|
||||
showPassword1: false,
|
||||
showPassword2: false,
|
||||
passwordValidation: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{12,20}$/,
|
||||
showQRCopiedToast: false
|
||||
};
|
||||
return {
|
||||
user: new User(this.$config.defaultServer, "", utils.randomPass()),
|
||||
isValid: false,
|
||||
loading: false,
|
||||
message: "",
|
||||
userErrorMessage: null,
|
||||
hasError: false,
|
||||
currentLoginServer: "",
|
||||
loadingLoginFlows: false,
|
||||
loginFlows: null,
|
||||
showQRCopiedToast: false
|
||||
};
|
||||
},
|
||||
goHome() {
|
||||
this.$navigation.push({ name: "Home" }, -1);
|
||||
|
|
@ -144,6 +111,9 @@ return {
|
|||
Object.keys(obj).forEach(k => this[k] = obj[k]);
|
||||
})
|
||||
},
|
||||
goToLoginPage() {
|
||||
this.$navigation.push({ name: "Login", params: { showCreateRoomOption: false, redirect: "GetLink" } }, 1);
|
||||
},
|
||||
handleLogin() {
|
||||
if (this.user.user_id && this.user.password) {
|
||||
// Reset errors
|
||||
|
|
@ -153,18 +123,27 @@ return {
|
|||
const userDisplayName = this.user.user_id;
|
||||
|
||||
var user = Object.assign({}, this.user);
|
||||
user.user_id = utils.randomUser(this.$config.userIdPrefix);
|
||||
|
||||
let prefix = userDisplayName.toLowerCase().replaceAll(" ", "-").replaceAll(utils.invalidUserIdChars(), "");
|
||||
if (prefix.length == 0) {
|
||||
prefix = this.$config.userIdPrefix;
|
||||
user.user_id = utils.randomUser(prefix);
|
||||
} else {
|
||||
// We first try with a username that is just a processed version of the display name.
|
||||
// If it is already taken, try again with random characters appended.
|
||||
user.user_id = prefix;
|
||||
prefix = prefix + "-";
|
||||
}
|
||||
|
||||
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 ||
|
||||
|
|
@ -172,37 +151,42 @@ return {
|
|||
if (error.data && error.data.errcode === 'M_FORBIDDEN') {
|
||||
this.message = this.$i18n.messages[this.$i18n.locale].login.invalid_message;
|
||||
this.hasError = true;
|
||||
} else if (error.data && error.data.errcode === 'M_USER_IN_USE') {
|
||||
// Try again with (other/new) random chars appended
|
||||
user.user_id = utils.randomUser(prefix);
|
||||
return this.$store.dispatch("createUser", { user, registrationFlowHandler: this.$refs.interactiveAuth.registrationFlowHandler }).then(
|
||||
(ignoreduser) => {
|
||||
this.$matrix.setUserDisplayName(userDisplayName);
|
||||
},
|
||||
(error) => {
|
||||
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);
|
||||
}
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
})
|
||||
}
|
||||
},
|
||||
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.$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.showPasswordFields = false;
|
||||
|
||||
this.currentLoginServer = server;
|
||||
this.loadingLoginFlows = true;
|
||||
|
||||
|
|
@ -217,12 +201,6 @@ return {
|
|||
} else {
|
||||
this.message = "";
|
||||
this.hasError = false;
|
||||
this.showPasswordFields = this.loginFlows.some(f => f.type == "m.login.password");
|
||||
if (this.showPasswordFields) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.password.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@
|
|||
class="filled-button mt-4"
|
||||
>{{ $t("login.login") }}</v-btn
|
||||
>
|
||||
<div class="mt-2 overline">{{ $t("login.or") }}</div>
|
||||
<div class="mt-2 overline" v-if="showCreateRoomOption">{{ $t("login.or") }}</div>
|
||||
<v-btn
|
||||
id="btn-create-room"
|
||||
color="primary"
|
||||
|
|
@ -94,6 +94,7 @@
|
|||
block
|
||||
@click.stop="handleCreateRoom"
|
||||
class="filled-button mt-2"
|
||||
v-if="showCreateRoomOption"
|
||||
>{{ $t("login.create_room") }}</v-btn
|
||||
>
|
||||
</v-form>
|
||||
|
|
@ -111,6 +112,20 @@ import logoMixin from "./logoMixin";
|
|||
export default {
|
||||
name: "Login",
|
||||
mixins:[rememberMeMixin, logoMixin],
|
||||
props: {
|
||||
showCreateRoomOption: {
|
||||
type: Boolean,
|
||||
default: function () {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
redirect: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return null;
|
||||
},
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
user: new User(this.$config.defaultServer, "", ""),
|
||||
|
|
@ -171,7 +186,10 @@ export default {
|
|||
this.loading = true;
|
||||
this.$store.dispatch("login", { user }).then(
|
||||
() => {
|
||||
if (this.$matrix.currentRoomId) {
|
||||
if (this.redirect) {
|
||||
this.$navigation.push({ name: this.redirect }, -1);
|
||||
}
|
||||
else if (this.$matrix.currentRoomId) {
|
||||
this.$navigation.push(
|
||||
{
|
||||
name: "Chat",
|
||||
|
|
|
|||
|
|
@ -580,6 +580,10 @@ class Util {
|
|||
return null;
|
||||
}
|
||||
|
||||
invalidUserIdChars() {
|
||||
return /[^0-9a-z-.=_/]+/g;
|
||||
}
|
||||
|
||||
getFirstVisibleElement(parentNode, where) {
|
||||
let visible = this.findVisibleElements(parentNode);
|
||||
if (visible) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue