diff --git a/html/src/app.js b/html/src/app.js
index f7416849..b074434d 100644
--- a/html/src/app.js
+++ b/html/src/app.js
@@ -386,6 +386,9 @@ import gameLogService from './service/gamelog.js'
}
return data;
}
+ if ((status === 401) && (data.error.message === '"Missing Credentials"') && (this.pendingGetRequests.size <= 1)) {
+ this.$emit('AUTOLOGIN');
+ }
if (data.error === Object(data.error)) {
this.$throw(
data.error.status_code || status,
@@ -880,10 +883,14 @@ import gameLogService from './service/gamelog.js'
}
*/
API.login = function (params) {
- var { username, password } = params;
+ var { username, password, saveCredentials } = params;
username = encodeURIComponent(username);
password = encodeURIComponent(password);
var auth = btoa(`${username}:${password}`);
+ if (saveCredentials) {
+ delete params.saveCredentials;
+ $app.saveCredentials = params;
+ }
return this.call(`auth/user?apiKey=${this.cachedConfig.clientApiKey}`, {
method: 'GET',
headers: {
@@ -3394,20 +3401,6 @@ import gameLogService from './service/gamelog.js'
if (--this.nextRefresh <= 0) {
this.nextRefresh = 60;
API.getCurrentUser().catch((err1) => {
- if (err1.status_code === 401) {
- API.getConfig().then((args) => {
- API.login({
- username: this.loginForm.username,
- password: this.loginForm.password
- }).catch((err2) => {
- if (err2.status_code === 401) {
- API.logout();
- }
- throw err2;
- });
- return args;
- });
- }
throw err1;
});
}
@@ -3627,10 +3620,81 @@ import gameLogService from './service/gamelog.js'
$app.resetGameLog();
});
+ API.$on('LOGIN', function (args) {
+ var savedCredentialsArray = {};
+ if (configRepository.getString('savedCredentials') !== null) {
+ var savedCredentialsArray = JSON.parse(configRepository.getString('savedCredentials'));
+ }
+ if ($app.saveCredentials) {
+ var credentialsToSave = { user: args.ref, loginParmas: $app.saveCredentials };
+ savedCredentialsArray[args.ref.username] = credentialsToSave;
+ delete $app.saveCredentials;
+ } else {
+ if (savedCredentialsArray[args.ref.username] !== undefined) {
+ savedCredentialsArray[args.ref.username].user = args.ref;
+ }
+ }
+ $app.loginForm.savedCredentials = savedCredentialsArray;
+ var jsonCredentialsArray = JSON.stringify(savedCredentialsArray);
+ configRepository.setString('savedCredentials', jsonCredentialsArray);
+ $app.loginForm.lastUserLoggedIn = args.ref.username;
+ configRepository.setString('lastUserLoggedIn', args.ref.username);
+ });
+
+ $app.methods.relogin = function (loginParmas) {
+ this.loginForm.loading = true;
+ return API.getConfig().catch((err) => {
+ this.loginForm.loading = false;
+ throw err;
+ }).then((args) => {
+ API.login({
+ username: loginParmas.username,
+ password: loginParmas.password
+ }).catch((err2) => {
+ API.logout();
+ throw err2;
+ }).finally(() => {
+ this.loginForm.loading = false;
+ });
+ });
+ };
+
+ $app.methods.deleteSavedLogin = function (username) {
+ var savedCredentialsArray = JSON.parse(configRepository.getString('savedCredentials'));
+ delete savedCredentialsArray[username];
+ $app.loginForm.savedCredentials = savedCredentialsArray;
+ var jsonCredentialsArray = JSON.stringify(savedCredentialsArray);
+ configRepository.setString('savedCredentials', jsonCredentialsArray);
+ new Noty({
+ type: 'success',
+ text: 'Account removed.'
+ }).show();
+ };
+
+ API.$on('AUTOLOGIN', function () {
+ if ($app.isAutoLogin) {
+ var user = $app.loginForm.savedCredentials[$app.loginForm.lastUserLoggedIn]
+ if (user !== undefined) {
+ $app.relogin({
+ username: user.loginParmas.username,
+ password: user.loginParmas.password
+ }).then((args) => {
+ new Noty({
+ type: 'success',
+ text: 'Automatically logged in.'
+ }).show();
+ });
+ }
+ }
+ });
+
$app.data.loginForm = {
loading: true,
username: '',
password: '',
+ saveCredentials: false,
+ savedCredentials: ((configRepository.getString('lastUserLoggedIn') !== null) ? JSON.parse(configRepository.getString('savedCredentials')) : {}),
+ lastUserLoggedIn: configRepository.getString('lastUserLoggedIn'),
rules: {
username: [
{
@@ -3658,8 +3722,11 @@ import gameLogService from './service/gamelog.js'
}).then((args) => {
API.login({
username: this.loginForm.username,
- password: this.loginForm.password
+ password: this.loginForm.password,
+ saveCredentials: this.loginForm.saveCredentials
}).finally(() => {
+ this.loginForm.username = '';
+ this.loginForm.password = '';
this.loginForm.loading = false;
});
return args;
@@ -5640,15 +5707,18 @@ import gameLogService from './service/gamelog.js'
$app.data.isStartAtWindowsStartup = configRepository.getBool('VRCX_StartAtWindowsStartup');
$app.data.isStartAsMinimizedState = (VRCXStorage.Get('VRCX_StartAsMinimizedState') === 'true');
$app.data.isCloseToTray = configRepository.getBool('VRCX_CloseToTray');
+ $app.data.isAutoLogin= configRepository.getBool('VRCX_AutoLogin');
var saveVRCXWindowOption = function () {
configRepository.setBool('VRCX_StartAtWindowsStartup', this.isStartAtWindowsStartup);
VRCXStorage.Set('VRCX_StartAsMinimizedState', this.isStartAsMinimizedState.toString());
configRepository.setBool('VRCX_CloseToTray', this.isCloseToTray);
AppApi.SetStartup(this.isStartAtWindowsStartup);
+ configRepository.setBool('VRCX_AutoLogin', this.isAutoLogin);
};
$app.watch.isStartAtWindowsStartup = saveVRCXWindowOption;
$app.watch.isStartAsMinimizedState = saveVRCXWindowOption;
$app.watch.isCloseToTray = saveVRCXWindowOption;
+ $app.watch.isAutoLogin = saveVRCXWindowOption;
if (!configRepository.getString('VRCX_notificationTimeout')) {
$app.data.notificationTimeout = 3000;
configRepository.setString('VRCX_notificationTimeout', $app.data.notificationTimeout);
diff --git a/html/src/index.pug b/html/src/index.pug
index bade183c..7f86dc47 100644
--- a/html/src/index.pug
+++ b/html/src/index.pug
@@ -16,21 +16,36 @@ html
//- login
.x-login-container(v-show="!API.isLoggedIn")
- div(style="width:300px;margin:auto")
- el-form(ref="loginForm" :model="loginForm" :rules="loginForm.rules" v-loading="loginForm.loading" @submit.native.prevent="login()")
- el-form-item(label="Username or Email" prop="username" required)
- el-input(v-model="loginForm.username" name="username" placeholder="Username or Email" clearable)
- el-form-item(label="Password" prop="password" required)
- el-input(type="password" v-model="loginForm.password" name="password" placeholder="Password" clearable show-password)
- el-form-item(style="margin-top:35px")
- el-button(native-type="submit" type="primary" :loading="loginForm.loading" style="width:100%") Login
- el-form-item
- el-button(:loading="loginForm.loading" style="width:100%" @click="loginWithSteam()") Login with Steam
- div(style="text-align:center;font-size:12px")
- p © 2019-2020 #[a(href="https://github.com/pypy-vrc" target="_blank") pypy] (mina#5656)
- p VRCX is an assistant application for provide information about manage friendship. this application uses unofficial VRChat API (VRCSDK).
- p VRCX isn't endorsed by VRChat and doesn't reflect the views or opinions of VRChat or anyone officially involved in producing or managing VRChat. VRChat is trademark of VRChat Inc. VRChat © VRChat Inc.
- p pypy is not responsible for any problems caused by VRCX. Use at your own risk!
+ div(style="width:300px;margin:auto" v-loading="loginForm.loading")
+ div(style="margin:15px" v-if="Object.keys(loginForm.savedCredentials).length !== 0")
+ h2(style="font-weight:bold;text-align:center;margin:0") Saved Accounts
+ .x-friend-list(style="margin-top:10px")
+ .x-friend-item(v-for="user in loginForm.savedCredentials" :key="user.user.id")
+ .x-friend-item(@click="relogin(user.loginParmas)" style="width:202px;padding:0")
+ .avatar
+ img(v-if="displayVRCPlusIconsAsAvatar && user.user.userIcon" v-lazy="user.user.userIcon")
+ img(v-else v-lazy="user.user.currentAvatarThumbnailImageUrl")
+ .detail
+ span.name(v-text="user.user.displayName")
+ span.extra(v-text="user.user.username")
+ el-button(type="default" @click="deleteSavedLogin(user.user.username)" size="mini" icon="el-icon-delete" circle)
+ div(style="margin:15px")
+ h2(style="font-weight:bold;text-align:center;margin:0") Login
+ el-form(ref="loginForm" :model="loginForm" :rules="loginForm.rules" @submit.native.prevent="login()")
+ el-form-item(label="Username or Email" prop="username" required)
+ el-input(v-model="loginForm.username" name="username" placeholder="Username or Email" clearable)
+ el-form-item(label="Password" prop="password" required)
+ el-input(type="password" v-model="loginForm.password" name="password" placeholder="Password" clearable show-password)
+ el-checkbox(v-model="loginForm.saveCredentials") Save Credentials
+ el-form-item(style="margin-top:35px")
+ el-button(native-type="submit" type="primary" :loading="loginForm.loading" style="width:100%") Login
+ el-form-item
+ el-button(:loading="loginForm.loading" style="width:100%" @click="loginWithSteam()") Login with Steam
+ div(style="text-align:center;font-size:12px")
+ p © 2019-2020 #[a(href="https://github.com/pypy-vrc" target="_blank") pypy] (mina#5656)
+ p VRCX is an assistant application for provide information about manage friendship. this application uses unofficial VRChat API (VRCSDK).
+ p VRCX isn't endorsed by VRChat and doesn't reflect the views or opinions of VRChat or anyone officially involved in producing or managing VRChat. VRChat is trademark of VRChat Inc. VRChat © VRChat Inc.
+ p pypy is not responsible for any problems caused by VRCX. Use at your own risk!
//- menu
.x-menu-container
@@ -572,7 +587,7 @@ html
el-radio(label="Friends" v-model="notificationOnlineOfflineFilter") Friends
el-radio(label="Off" v-model="notificationOnlineOfflineFilter") Off
div(style="margin-top:30px")
- span(style="font-weight:bold") Window
+ span(style="font-weight:bold") Application
div(style="font-size:12px;margin-top:5px")
span(style="display:inline-block;min-width:150px") Start at Windows startup
el-switch(v-model="isStartAtWindowsStartup")
@@ -582,6 +597,9 @@ html
div(style="font-size:12px;margin-top:5px")
span(style="display:inline-block;min-width:150px") Close to tray
el-switch(v-model="isCloseToTray")
+ div(style="font-size:12px;margin-top:5px")
+ span(style="display:inline-block;min-width:150px") Auto login
+ el-switch(v-model="isAutoLogin")
div(style="margin-top:45px;border-top:1px solid #eee;padding-top:30px")
span(style="font-weight:bold") Legal Notice
div(style="margin-top:5px;font-size:12px")