Skip to content

Commit

Permalink
Added remember option for VRChat credentials
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphiiko committed Nov 15, 2023
1 parent 743a186 commit ab100c1
Show file tree
Hide file tree
Showing 20 changed files with 297 additions and 53 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- Hardware mode for the overlay mic mute indicator's voice activity, for use with other games than VRChat.
- A credential saving option for remembering your VRChat credentials.

### Changed
- When your VRChat session expires and you've opted to store your credentials, OyasumiVR will automatically attempt to log you back in.

### Fixed
- Updated missing and improved existing Simplified Chinese translations (by [雾雨花精灵](https://twitter.com/u_flower_elf) and [i0nTempest](https://twitter.com/i0ntempest)).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[step]="step"
[snapDistance]="snapDistance"
[snapValues]="snapValues"
[value]="value ?? 0"
[value]="value"
(valueChange)="onSliderChange($event)"
[style]="style"
#slider
Expand All @@ -30,6 +30,6 @@
#inputValue
/>
<div class="unit" *ngIf="unit"
><span>{{ unit }}</span></div
><span>{{ unit }}</span></div
>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,32 @@
<div class="pane-title" translate>comp.vrchat-login-modal.title</div>
<div class="pane-content">
<p class="label" translate>comp.vrchat-login-modal.username</p>
<input type="text" (input)="username = usernameInput.value" maxlength="1024" #usernameInput />
<input type="text" [(ngModel)]="username" maxlength="1024" />
<p class="label" translate>comp.vrchat-login-modal.password</p>
<input
type="password"
(input)="password = passwordInput.value"
maxlength="1024"
#passwordInput
/>
<input type="password" [(ngModel)]="password" maxlength="1024" />
<div class="alert error" @vshrink *ngIf="error">
<div class="alert-bg"></div>
<div class="alert-icon">
<i class="material-icons"> error </i>
</div>
<div class="alert-content">{{ error | translate }}</div>
</div>
<div class="remember-row">
<div class="left">
<span translate>comp.vrchat-login-modal.rememberCredentials</span>
</div>
<div class="right">
<label class="switch-toggle">
<input
[disabled]="loggingIn"
[checked]="rememberCredentials"
(change)="toggleRememberCredentials()"
type="checkbox"
/>
<span class="switch-toggle-slider"></span>
</label>
</div>
</div>
<p translate>settings.vrchat.logIn.disclaimers.use</p>
<p translate>settings.vrchat.logIn.disclaimers.brand</p>
<p translate>settings.vrchat.logIn.disclaimers.liability</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,20 @@ p,
white-space: nowrap;
}
}


.remember-row {
margin-top: 1em;
display: flex;
flex-direction: row;
align-items: center;
padding: 1em;
border-radius: var(--surface-border-radius);
background-color: var(--color-surface-1);
.left {
flex: 1;
}
.right {
flex-shrink: 0;
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Component, OnInit } from '@angular/core';
import { Component, DestroyRef, OnInit } from '@angular/core';
import { fadeUp, hshrink, vshrink } from '../../utils/animations';
import { VRChatService } from '../../services/vrchat.service';
import { firstValueFrom, map } from 'rxjs';
import { firstValueFrom, map, take } from 'rxjs';
import { VRChatLoginTFAModalComponent } from '../vrchat-login-tfa-modal/vrchat-login-tfa-modal.component';
import { BaseModalComponent } from '../base-modal/base-modal.component';
import { ModalService } from '../../services/modal.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

interface VRChatLoginModalInputModel {}
interface VRChatLoginModalInputModel {
autoLogin?: boolean;
}

interface VRChatLoginModalOutputModel {}

Expand All @@ -24,12 +27,34 @@ export class VRChatLoginModalComponent
password = '';
loggingIn = false;
error = '';
rememberCredentials = false;
autoLogin = false;

constructor(private vrchat: VRChatService, private modalService: ModalService) {
constructor(
private vrchat: VRChatService,
private modalService: ModalService,
private destroyRef: DestroyRef
) {
super();
}

ngOnInit(): void {}
async ngOnInit(): Promise<void> {
this.vrchat.settings
.pipe(
map((settings) => settings.rememberCredentials),
takeUntilDestroyed(this.destroyRef),
take(1)
)
.subscribe(async (rememberCredentials) => {
this.rememberCredentials = rememberCredentials;
const credentials = await this.vrchat.loadCredentials();
if (credentials) {
this.username = credentials.username;
this.password = credentials.password;
if (this.autoLogin) this.login();
}
});
}

get2FACode(lastCodeInvalid: boolean): Promise<string | null> {
return firstValueFrom(
Expand All @@ -38,6 +63,7 @@ export class VRChatLoginModalComponent
VRChatLoginTFAModalComponent,
{
lastCodeInvalid,
username: this.username,
},
{
closeOnEscape: false,
Expand All @@ -54,6 +80,8 @@ export class VRChatLoginModalComponent
try {
await this.vrchat.login(this.username, this.password);
this.loggingIn = false;
if (this.rememberCredentials)
await this.vrchat.rememberCredentials(this.username, this.password);
await this.close();
} catch (e) {
switch (e) {
Expand All @@ -64,10 +92,7 @@ export class VRChatLoginModalComponent
while (true) {
this.error = '';
const code = await this.get2FACode(lastCodeInvalid);
if (!code) {
await this.close();
return;
}
if (!code) break;
const method: 'totp' | 'otp' | 'emailotp' = {
'2FA_TOTP_REQUIRED': 'totp',
'2FA_EMAILOTP_REQUIRED': 'emailotp',
Expand All @@ -76,6 +101,8 @@ export class VRChatLoginModalComponent
try {
await this.vrchat.verify2FA(code, method);
this.loggingIn = false;
if (this.rememberCredentials)
await this.vrchat.rememberCredentials(this.username, this.password);
await this.close();
} catch (e) {
switch (e) {
Expand Down Expand Up @@ -106,4 +133,11 @@ export class VRChatLoginModalComponent
}
this.loggingIn = false;
}

async toggleRememberCredentials() {
this.rememberCredentials = !this.rememberCredentials;
if (!this.rememberCredentials) {
await this.vrchat.forgetCredentials();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<div class="pane modal" @fadeUp>
<div class="pane-title" translate>comp.vrchat-login-tfa-modal.title</div>
<div class="pane-content">
<p class="label"><span translate>comp.vrchat-login-tfa-modal.logginInAs</span>&nbsp;<b>{{username}}</b></p>
<p class="label" translate>comp.vrchat-login-tfa-modal.description</p>
<input type="text" (input)="code.next(codeInput.value)" maxlength="6" #codeInput />
<div class="alert error" @vshrink *ngIf="error">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

interface VRChatLoginTFAModalInputModel {
lastCodeInvalid: boolean;
username?: string;
}

interface VRChatLoginTFAModalOutputModel {
Expand All @@ -26,6 +27,7 @@ export class VRChatLoginTFAModalComponent
code: BehaviorSubject<string> = new BehaviorSubject<string>('');
codeValid = false;
error = '';
username?: string;

constructor(private destroyRef: DestroyRef) {
super();
Expand Down
1 change: 1 addition & 0 deletions src-ui/app/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const EVENT_LOG_FILE = 'event_log.dat';
export const PULSOID_CLIENT_ID = '1fd0889e-be0c-4be5-ab68-9f750e3975b4';
export const PULSOID_REDIRECT_URI = 'oyasumivr://integration/pulsoid/v1/redirect';
export const PULSOID_REFERRAL_ID = '86680152-707d-4771-8a12-740bad1b4545';
export const STORAGE_MASTER_CRYPTO_KEY = 'mY2BEtChq6dmPS4byAT2Xr1NT+tet5IONT+o7Eni3Vw=';
export const NG_LOCALE_MAP: { [s: string]: string } = {
en: 'en',
nl: 'nl',
Expand Down
4 changes: 4 additions & 0 deletions src-ui/app/models/vrchat-api-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ export interface VRChatApiSettings {
authCookieExpiry?: number;
twoFactorCookie?: string;
twoFactorCookieExpiry?: number;
rememberCredentials: boolean;
rememberedCredentials?: string | null;
credentialCryptoKey?: string | null;
}

export const VRCHAT_API_SETTINGS_DEFAULT: VRChatApiSettings = {
version: 2,
rememberCredentials: false,
};
Loading

0 comments on commit ab100c1

Please sign in to comment.