1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

[PM-5609] passphrase settings component & services (#10535)

This commit is contained in:
✨ Audrey ✨
2024-08-28 09:26:17 -04:00
committed by GitHub
parent 819c312ce2
commit 8c4b8d71ea
34 changed files with 1147 additions and 144 deletions

View File

@@ -257,8 +257,8 @@ describe("UserStateSubject", () => {
let actual: TestType = null;
subject.subscribe({
error: (value) => {
actual = value;
error: (value: unknown) => {
actual = value as any;
},
});
subject.error(expected);
@@ -275,8 +275,8 @@ describe("UserStateSubject", () => {
let actual: TestType = null;
subject.subscribe({
error: (value) => {
actual = value;
error: (value: unknown) => {
actual = value as any;
},
});
subject.error("expectedError");
@@ -415,8 +415,8 @@ describe("UserStateSubject", () => {
let error = false;
subject.subscribe({
error: (e) => {
error = e;
error: (e: unknown) => {
error = e as any;
},
});
singleUserId$.next(errorUserId);
@@ -434,8 +434,8 @@ describe("UserStateSubject", () => {
let actual = false;
subject.subscribe({
error: (e) => {
actual = e;
error: (e: unknown) => {
actual = e as any;
},
});
singleUserId$.error(expected);
@@ -454,8 +454,8 @@ describe("UserStateSubject", () => {
let actual = false;
subject.subscribe({
error: (e) => {
actual = e;
error: (e: unknown) => {
actual = e as any;
},
});
when$.error(expected);
@@ -464,4 +464,14 @@ describe("UserStateSubject", () => {
expect(actual).toEqual(expected);
});
});
describe("userId", () => {
it("returns the userId to which the subject is bound", () => {
const state = new FakeSingleUserState<TestType>(SomeUser, { foo: "init" });
const singleUserId$ = new Subject<UserId>();
const subject = new UserStateSubject(state, { singleUserId$ });
expect(subject.userId).toEqual(SomeUser);
});
});
});

View File

@@ -15,6 +15,8 @@ import {
ignoreElements,
endWith,
startWith,
Observable,
Subscription,
} from "rxjs";
import { Simplify } from "type-fest";
@@ -59,7 +61,10 @@ export type UserStateSubjectDependencies<State, Dependency> = Simplify<
* @template State the state stored by the subject
* @template Dependencies use-specific dependencies provided by the user.
*/
export class UserStateSubject<State, Dependencies = null> implements SubjectLike<State> {
export class UserStateSubject<State, Dependencies = null>
extends Observable<State>
implements SubjectLike<State>
{
/**
* Instantiates the user state subject
* @param state the backing store of the subject
@@ -76,6 +81,8 @@ export class UserStateSubject<State, Dependencies = null> implements SubjectLike
private state: SingleUserState<State>,
private dependencies: UserStateSubjectDependencies<State, Dependencies>,
) {
super();
// normalize dependencies
const when$ = (this.dependencies.when$ ?? new BehaviorSubject(true)).pipe(
distinctUntilChanged(),
@@ -114,6 +121,12 @@ export class UserStateSubject<State, Dependencies = null> implements SubjectLike
});
}
/** The userId to which the subject is bound.
*/
get userId() {
return this.state.userId;
}
next(value: State) {
this.input?.next(value);
}
@@ -130,7 +143,7 @@ export class UserStateSubject<State, Dependencies = null> implements SubjectLike
* @param observer listening for events
* @returns the subscription
*/
subscribe(observer: Partial<Observer<State>> | ((value: State) => void)): Unsubscribable {
subscribe(observer?: Partial<Observer<State>> | ((value: State) => void) | null): Subscription {
return this.output.subscribe(observer);
}

View File

@@ -0,0 +1,48 @@
import { Simplify } from "type-fest";
/** Constraints that are shared by all primitive field types */
type PrimitiveConstraint = {
/** presence indicates the field is required */
required?: true;
};
/** Constraints that are shared by string fields */
type StringConstraints = {
/** minimum string length. When absent, min length is 0. */
minLength?: number;
/** maximum string length. When absent, max length is unbounded. */
maxLength?: number;
};
/** Constraints that are shared by number fields */
type NumberConstraints = {
/** minimum number value. When absent, min value is unbounded. */
min?: number;
/** maximum number value. When absent, min value is unbounded. */
max?: number;
/** presence indicates the field only accepts integer values */
integer?: true;
/** requires the number be a multiple of the step value */
step?: number;
};
/** Utility type that transforms keys of T into their supported
* validators.
*/
export type Constraints<T> = {
[Key in keyof T]: Simplify<
PrimitiveConstraint &
(T[Key] extends string
? StringConstraints
: T[Key] extends number
? NumberConstraints
: never)
>;
};
/** utility type for methods that evaluate constraints generically. */
export type AnyConstraint = PrimitiveConstraint & StringConstraints & NumberConstraints;