blob: 9107b31d20755180e9fa702b4d8524ec964e5f88 [file] [log] [blame]
/**
* @license
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {from, of, BehaviorSubject, Observable, Subscription} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {
DiffPreferencesInfo as DiffPreferencesInfoAPI,
DiffViewMode,
} from '../../api/diff';
import {
AccountCapabilityInfo,
AccountDetailInfo,
PreferencesInfo,
} from '../../types/common';
import {
createDefaultPreferences,
createDefaultDiffPrefs,
} from '../../constants/constants';
import {RestApiService} from '../gr-rest-api/gr-rest-api';
import {DiffPreferencesInfo} from '../../types/diff';
import {Finalizable} from '../registry';
import {select} from '../../utils/observable-util';
export interface UserState {
/**
* Keeps being defined even when credentials have expired.
*/
account?: AccountDetailInfo;
preferences: PreferencesInfo;
diffPreferences: DiffPreferencesInfo;
capabilities?: AccountCapabilityInfo;
}
export class UserModel implements Finalizable {
private readonly privateState$: BehaviorSubject<UserState> =
new BehaviorSubject({
preferences: createDefaultPreferences(),
diffPreferences: createDefaultDiffPrefs(),
});
readonly account$: Observable<AccountDetailInfo | undefined> = select(
this.privateState$,
userState => userState.account
);
/** Note that this may still be true, even if credentials have expired. */
readonly loggedIn$: Observable<boolean> = select(
this.account$,
account => !!account
);
readonly capabilities$: Observable<AccountCapabilityInfo | undefined> =
select(this.userState$, userState => userState.capabilities);
readonly isAdmin$: Observable<boolean> = select(
this.capabilities$,
capabilities => capabilities?.administrateServer ?? false
);
readonly preferences$: Observable<PreferencesInfo> = select(
this.privateState$,
userState => userState.preferences
);
readonly diffPreferences$: Observable<DiffPreferencesInfo> = select(
this.privateState$,
userState => userState.diffPreferences
);
readonly preferenceDiffViewMode$: Observable<DiffViewMode> = select(
this.preferences$,
preference => preference.diff_view ?? DiffViewMode.SIDE_BY_SIDE
);
private readonly subscriptions: Subscription[] = [];
get userState$(): Observable<UserState> {
return this.privateState$;
}
constructor(readonly restApiService: RestApiService) {
this.subscriptions = [
from(this.restApiService.getAccount()).subscribe(
(account?: AccountDetailInfo) => {
this.setAccount(account);
}
),
this.account$
.pipe(
switchMap(account => {
if (!account) return of(createDefaultPreferences());
return from(this.restApiService.getPreferences());
})
)
.subscribe((preferences?: PreferencesInfo) => {
this.setPreferences(preferences ?? createDefaultPreferences());
}),
this.account$
.pipe(
switchMap(account => {
if (!account) return of(createDefaultDiffPrefs());
return from(this.restApiService.getDiffPreferences());
})
)
.subscribe((diffPrefs?: DiffPreferencesInfoAPI) => {
this.setDiffPreferences(diffPrefs ?? createDefaultDiffPrefs());
}),
this.account$
.pipe(
switchMap(account => {
if (!account) return of(undefined);
return from(this.restApiService.getAccountCapabilities());
})
)
.subscribe((capabilities?: AccountCapabilityInfo) => {
this.setCapabilities(capabilities);
}),
];
}
finalize() {
for (const s of this.subscriptions) {
s.unsubscribe();
}
this.subscriptions.splice(0, this.subscriptions.length);
}
updatePreferences(prefs: Partial<PreferencesInfo>) {
this.restApiService
.savePreferences(prefs)
.then((newPrefs: PreferencesInfo | undefined) => {
if (!newPrefs) return;
this.setPreferences(newPrefs);
});
}
updateDiffPreference(diffPrefs: DiffPreferencesInfo) {
return this.restApiService
.saveDiffPreferences(diffPrefs)
.then((response: Response) => {
this.restApiService.getResponseObject(response).then(obj => {
const newPrefs = obj as unknown as DiffPreferencesInfo;
if (!newPrefs) return;
this.setDiffPreferences(newPrefs);
});
});
}
getDiffPreferences() {
return this.restApiService.getDiffPreferences().then(prefs => {
if (!prefs) return;
this.setDiffPreferences(prefs);
});
}
setPreferences(preferences: PreferencesInfo) {
const current = this.privateState$.getValue();
this.privateState$.next({...current, preferences});
}
setDiffPreferences(diffPreferences: DiffPreferencesInfo) {
const current = this.privateState$.getValue();
this.privateState$.next({...current, diffPreferences});
}
setCapabilities(capabilities?: AccountCapabilityInfo) {
const current = this.privateState$.getValue();
this.privateState$.next({...current, capabilities});
}
private setAccount(account?: AccountDetailInfo) {
const current = this.privateState$.getValue();
this.privateState$.next({...current, account});
}
}