feat(login): add public users

This commit is contained in:
MrTimscampi 2020-11-26 08:39:36 +01:00 committed by Julien Machiels
parent e5452db4c1
commit 692f58939f
8 changed files with 219 additions and 11 deletions

View File

@ -7,6 +7,7 @@
@submit.prevent="userLogin"
>
<v-text-field
v-if="isEmpty(user)"
v-model="login.username"
outlined
:label="$t('username')"
@ -23,8 +24,11 @@
></v-text-field>
<v-row align="center" no-gutters>
<v-col class="mr-2">
<v-btn to="/selectServer" block large
>{{ $t('changeServer') }}
<v-btn v-if="!user" to="/selectServer" nuxt block large>
{{ $t('changeServer') }}
</v-btn>
<v-btn v-else block large @click="$emit('change')">
{{ $t('changeUser') }}
</v-btn>
</v-col>
<v-col class="mr-2">
@ -47,10 +51,20 @@
</template>
<script lang="ts">
import { isEmpty } from 'lodash';
import Vue from 'vue';
import { mapActions } from 'vuex';
import { UserDto } from '~/api';
export default Vue.extend({
props: {
user: {
type: Object as () => UserDto,
default() {
return {};
}
}
},
data() {
return {
login: {
@ -67,10 +81,17 @@ export default Vue.extend({
...mapActions('deviceProfile', ['setDeviceProfile']),
...mapActions('snackbar', ['pushSnackbarMessage']),
async userLogin() {
if (!isEmpty(this.user)) {
// If we have a user from the public user selector, set it as login
this.login.username = this.user.Name || '';
}
this.loading = true;
this.setDeviceProfile();
await this.loginRequest(this.login);
this.loading = false;
},
isEmpty(value: Record<any, any>) {
return isEmpty(value);
}
}
});

View File

@ -44,6 +44,7 @@ export default Vue.extend({
...mapActions('snackbar', ['pushSnackbarMessage']),
...mapActions('servers', ['connectServer', 'removeServer']),
async setServer() {
// TODO: Merge with the identical method in AddServerForm
this.loading = true;
await this.connectServer(this.serverInfo.address);
this.loading = false;

105
components/UserCard.vue Normal file
View File

@ -0,0 +1,105 @@
<template>
<div class="portrait-card">
<div class="card-content">
<v-card
class="mx-auto d-flex flex-column"
height="100%"
max-height="325px"
max-width="200px"
>
<div class="user-image primary darken-4">
<v-responsive :aspect-ratio="1 / 1">
<v-img
v-if="user.PrimaryImageTag"
:src="`${$axios.defaults.baseURL}/Users/${user.Id}/Images/Primary?tag=${user.PrimaryImageTag}&quality=90`"
/>
<div
v-if="!user.PrimaryImageTag"
class="empty-picture d-flex align-center justify-center"
>
<v-icon size="96"> mdi-account </v-icon>
</div>
</v-responsive>
</div>
<v-card-title>
{{ user.Name }}
</v-card-title>
<v-card-subtitle class="pb-0 text-capitalize-first-letter">
{{ formatDistance(user.LastActivityDate) }}
</v-card-subtitle>
<v-spacer />
<v-card-actions>
<v-btn
text
color="primary"
width="100%"
@click="$emit('connect', user)"
>
{{ $t('connect') }}
</v-btn>
</v-card-actions>
</v-card>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import imageHelper from '~/mixins/imageHelper';
import { UserDto } from '~/api';
export default Vue.extend({
mixins: [imageHelper],
props: {
user: {
type: Object as () => UserDto,
required: true
}
},
methods: {
formatDistance(value: string) {
if (value) {
return this.$dateFns.formatDistanceToNow(new Date(value), {
addSuffix: true
});
} else {
return '';
}
}
}
});
</script>
<style lang="scss" scoped>
.portrait-card {
display: inline-block;
width: 200px;
height: 325px;
position: relative;
contain: strict;
border-radius: 0.3em;
margin: 0.6em;
}
.card-content {
overflow: hidden;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 0 !important;
height: 100%;
width: 100%;
contain: strict;
-webkit-tap-highlight-color: transparent;
}
.empty-picture {
height: 100%;
}
.text-capitalize-first-letter::first-letter {
text-transform: uppercase;
}
</style>

View File

@ -9,6 +9,7 @@
"biography": "Biography",
"browserNotSupported": "Your browser is not supported for playing this file.",
"changeServer": "Change server",
"changeUser": "Change user",
"connect": "Connect",
"byArtist": "By",
"collections": "Collections",
@ -34,7 +35,9 @@
"liked": "Liked",
"likes": "Likes",
"login": "Login",
"loginAs": "Login as {name}",
"logout": "Logout",
"manualLogin": "Manual login",
"more": "More",
"moreLikeArtist": "More like {artist}",
"moreLikeThis": "More like this",
@ -53,6 +56,7 @@
"resumable": "Resumable",
"selectServer": "Select server",
"series": "Series",
"selectUser": "Select a user",
"serverAddress": "Server address",
"serverAddressMustBeUrl": "Server address must be a valid URL",
"serverAddressRequired": "Server address is required",

View File

@ -1,7 +1,14 @@
import { Context } from '@nuxt/types';
export default function (context: Context) {
if (!context.$axios.defaults.baseURL)
/**
* Middleware providing a redirect to the server selection page in case the
* Axios base URL is the default (non-working) one.
*
* @param {Context} context Nuxt application context
* @returns {void}
*/
export default function (context: Context): void {
if (!context.$axios.defaults.baseURL) {
return context.redirect('/selectserver');
if (context.$auth?.user?.Id) return context.redirect('/');
}
}

View File

@ -109,7 +109,9 @@ const config: NuxtConfig = {
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {},
axios: {
baseURL: ''
},
/*
** Axios-based Authentication
** See https://auth.nuxtjs.org/schemes/local.html#options

View File

@ -23,7 +23,6 @@ import { mapActions } from 'vuex';
export default Vue.extend({
layout: 'fullpage',
auth: false,
middleware: 'serverMiddleware',
head() {
return {
title: this.$store.state.page.title

View File

@ -1,21 +1,62 @@
<template>
<v-container fill-height>
<v-row align="center" justify="center">
<v-col md="4">
<h1 class="text-h4 mb-6 text-center">{{ $t('login') }}</h1>
<login-form />
<v-col v-if="isEmpty(currentUser) && !loginAsOther && publicUsers.length">
<h1 class="text-h4 mb-6 text-center">{{ $t('selectUser') }}</h1>
<v-row align="center" justify="center">
<v-col md="10">
<div class="d-flex align-center justify-center">
<user-card
v-for="publicUser in publicUsers"
:key="publicUser.Id"
:user="publicUser"
@connect="setCurrentUser"
/>
</div>
</v-col>
</v-row>
<v-row align="center" justify="center" no-gutters>
<v-col md="4" class="d-flex flex-row">
<v-btn class="flex-grow-1 mr-2" large @click="loginAsOther = true">
{{ $t('manualLogin') }}
</v-btn>
<v-btn class="flex-grow-1 mr-2" to="/selectServer" nuxt large>
{{ $t('changeServer') }}
</v-btn>
<locale-switcher />
</v-col>
</v-row>
</v-col>
<v-col
v-else-if="!isEmpty(currentUser) || loginAsOther || !publicUsers.length"
md="4"
>
<h1 v-if="!isEmpty(currentUser)" class="text-h4 mb-6 text-center">
{{ $t('loginAs', { name: currentUser.Name }) }}
</h1>
<h1 v-else class="text-h4 mb-6 text-center">{{ $t('login') }}</h1>
<login-form :user="currentUser" @change="resetCurrentUser" />
</v-col>
</v-row>
</v-container>
</template>
<script lang="ts">
import { isEmpty } from 'lodash';
import Vue from 'vue';
import { mapActions } from 'vuex';
import { UserDto } from '~/api';
export default Vue.extend({
layout: 'fullpage',
middleware: 'serverMiddleware',
data() {
return {
loginAsOther: false,
currentUser: {} as UserDto,
publicUsers: [] as Array<UserDto>
};
},
head() {
return {
title: this.$store.state.page.title
@ -24,8 +65,36 @@ export default Vue.extend({
created() {
this.setPageTitle({ title: this.$t('login') });
},
async beforeMount() {
try {
this.publicUsers = (await this.$api.user.getPublicUsers({})).data;
} catch (error) {
console.error('Unable to get public users:', error);
}
},
methods: {
...mapActions('page', ['setPageTitle'])
...mapActions('page', ['setPageTitle']),
...mapActions('deviceProfile', ['setDeviceProfile']),
isEmpty(value: Record<any, any>) {
return isEmpty(value);
},
setCurrentUser(user: UserDto) {
console.dir(user);
if (!user.HasPassword) {
// If the user doesn't have a password, avoid showing the password form
this.setDeviceProfile();
this.$auth.loginWith('jellyfin', {
username: user.Name,
password: ''
});
return; // Avoid changing the form
}
this.currentUser = user;
},
resetCurrentUser() {
this.currentUser = {};
this.loginAsOther = false;
}
}
});
</script>