1
0
mirror of https://github.com/bitwarden/jslib synced 2026-01-05 18:13:14 +00:00
This commit is contained in:
Hinton
2022-04-25 15:44:03 +02:00
parent a0f6150c09
commit 3be59322db
11 changed files with 219 additions and 5 deletions

View File

@@ -19,6 +19,8 @@
"@angular/platform-browser-dynamic": "^12.2.13",
"@angular/router": "^12.2.13",
"@bitwarden/jslib-common": "file:../common",
"@ngrx/entity": "^12.5.1",
"@ngrx/store": "^12.5.1",
"duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git",
"rxjs": "^7.4.0",
"tldjs": "^2.3.1",
@@ -204,6 +206,31 @@
"resolved": "../common",
"link": true
},
"node_modules/@ngrx/entity": {
"version": "12.5.1",
"resolved": "https://registry.npmjs.org/@ngrx/entity/-/entity-12.5.1.tgz",
"integrity": "sha512-bjRMMe83onhrhxu5rJo+FhaS0gFY4HbMulSjUpuh0/LJckd0Z3QUDs+UcqYW/tjG/2o2rbNDxkws6w1D0c5ksA==",
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/core": "^12.0.0",
"@ngrx/store": "12.5.1",
"rxjs": "^6.5.3 || ^7.0.0"
}
},
"node_modules/@ngrx/store": {
"version": "12.5.1",
"resolved": "https://registry.npmjs.org/@ngrx/store/-/store-12.5.1.tgz",
"integrity": "sha512-NLVkHLVeZc7IboXSDZlFoq1QrupmwYTYKRHS6se7ZasAv/lrIjHWsVVdICKSVRBsHZYu3+dmCXmu+YgulP7iHw==",
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/core": "^12.0.0",
"rxjs": "^6.5.3 || ^7.0.0"
}
},
"node_modules/@types/duo_web_sdk": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/@types/duo_web_sdk/-/duo_web_sdk-2.7.1.tgz",
@@ -490,6 +517,22 @@
"zxcvbn": "^4.4.2"
}
},
"@ngrx/entity": {
"version": "12.5.1",
"resolved": "https://registry.npmjs.org/@ngrx/entity/-/entity-12.5.1.tgz",
"integrity": "sha512-bjRMMe83onhrhxu5rJo+FhaS0gFY4HbMulSjUpuh0/LJckd0Z3QUDs+UcqYW/tjG/2o2rbNDxkws6w1D0c5ksA==",
"requires": {
"tslib": "^2.0.0"
}
},
"@ngrx/store": {
"version": "12.5.1",
"resolved": "https://registry.npmjs.org/@ngrx/store/-/store-12.5.1.tgz",
"integrity": "sha512-NLVkHLVeZc7IboXSDZlFoq1QrupmwYTYKRHS6se7ZasAv/lrIjHWsVVdICKSVRBsHZYu3+dmCXmu+YgulP7iHw==",
"requires": {
"tslib": "^2.0.0"
}
},
"@types/duo_web_sdk": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/@types/duo_web_sdk/-/duo_web_sdk-2.7.1.tgz",

View File

@@ -33,6 +33,8 @@
"@angular/platform-browser-dynamic": "^12.2.13",
"@angular/router": "^12.2.13",
"@bitwarden/jslib-common": "file:../common",
"@ngrx/entity": "^12.5.1",
"@ngrx/store": "^12.5.1",
"duo_web_sdk": "git+https://github.com/duosecurity/duo_web_sdk.git",
"rxjs": "^7.4.0",
"tldjs": "^2.3.1",

View File

@@ -1,12 +1,16 @@
import { Directive, EventEmitter, Input, Output } from "@angular/core";
import { Store } from "@ngrx/store";
import { Observable } from "rxjs";
import { CollectionService } from "jslib-common/abstractions/collection.service";
import { FolderService } from "jslib-common/abstractions/folder.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { CipherType } from "jslib-common/enums/cipherType";
import { Folder } from "jslib-common/models/domain/folder";
import { TreeNode } from "jslib-common/models/domain/treeNode";
import { CollectionView } from "jslib-common/models/view/collectionView";
import { FolderDecrypted } from "jslib-common/models/view/folderDecrypted";
import * as fromFolders from "jslib-common/state";
@Directive()
export class GroupingsComponent {
@@ -24,6 +28,8 @@ export class GroupingsComponent {
@Output() onEditFolder = new EventEmitter<FolderDecrypted>();
@Output() onCollectionClicked = new EventEmitter<CollectionView>();
folders$: Observable<Folder[]>;
folders: FolderDecrypted[];
nestedFolders: TreeNode<FolderDecrypted>[];
collections: CollectionView[];
@@ -43,8 +49,12 @@ export class GroupingsComponent {
constructor(
protected collectionService: CollectionService,
protected folderService: FolderService,
protected stateService: StateService
) {}
protected stateService: StateService,
store: Store
) {
this.folders$ = store.select(fromFolders.selectFolderCollection);
console.log(this.folders$);
}
async load(setLoaded = true) {
const collapsedGroupings = await this.stateService.getCollapsedGroupings();

View File

@@ -1,4 +1,5 @@
import { Injector, LOCALE_ID, NgModule } from "@angular/core";
import { Store } from "@ngrx/store";
import { ApiService as ApiServiceAbstraction } from "jslib-common/abstractions/api.service";
import { AppIdService as AppIdServiceAbstraction } from "jslib-common/abstractions/appId.service";
@@ -73,6 +74,7 @@ import { UserVerificationService } from "jslib-common/services/userVerification.
import { UsernameGenerationService } from "jslib-common/services/usernameGeneration.service";
import { VaultTimeoutService } from "jslib-common/services/vaultTimeout.service";
import { WebCryptoFunctionService } from "jslib-common/services/webCryptoFunction.service";
import { State } from "jslib-common/state";
import { AuthGuardService } from "./auth-guard.service";
import { BroadcasterService } from "./broadcaster.service";
@@ -250,7 +252,8 @@ import { ValidationService } from "./validation.service";
keyConnectorService: KeyConnectorServiceAbstraction,
stateService: StateServiceAbstraction,
organizationService: OrganizationServiceAbstraction,
providerService: ProviderServiceAbstraction
providerService: ProviderServiceAbstraction,
store: Store
) =>
new SyncService(
apiService,
@@ -267,6 +270,7 @@ import { ValidationService } from "./validation.service";
stateService,
organizationService,
providerService,
store,
async (expired: boolean) => messagingService.send("logout", { expired: expired })
),
deps: [
@@ -284,6 +288,7 @@ import { ValidationService } from "./validation.service";
StateServiceAbstraction,
OrganizationServiceAbstraction,
ProviderServiceAbstraction,
Store,
],
},
{ provide: BroadcasterServiceAbstraction, useClass: BroadcasterService },

View File

@@ -1,6 +1,7 @@
import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { Folder } from "../domain/folder";
import { SymmetricCryptoKey } from "../domain/symmetricCryptoKey";
import { ITreeNodeObject } from "../domain/treeNode";
export class FolderDecrypted implements ITreeNodeObject {
@@ -8,10 +9,10 @@ export class FolderDecrypted implements ITreeNodeObject {
name: string = null;
revisionDate: Date = null;
async encrypt(cryptoService: CryptoService): Promise<Folder> {
async encrypt(cryptoService: CryptoService, key?: SymmetricCryptoKey): Promise<Folder> {
return {
id: this.id,
name: await cryptoService.encrypt(this.name),
name: await cryptoService.encrypt(this.name, key),
revisionDate: null,
};
}

View File

@@ -1,3 +1,5 @@
import { Store } from "@ngrx/store";
import { ApiService } from "../abstractions/api.service";
import { CipherService } from "../abstractions/cipher.service";
import { CollectionService } from "../abstractions/collection.service";
@@ -33,6 +35,8 @@ import {
import { PolicyResponse } from "../models/response/policyResponse";
import { ProfileResponse } from "../models/response/profileResponse";
import { SendResponse } from "../models/response/sendResponse";
import * as FoldersActions from "../state/folders.actions";
export class SyncService implements SyncServiceAbstraction {
syncInProgress = false;
@@ -52,6 +56,7 @@ export class SyncService implements SyncServiceAbstraction {
private stateService: StateService,
private organizationService: OrganizationService,
private providerService: ProviderService,
private store: Store,
private logoutCallback: (expired: boolean) => Promise<void>
) {}
@@ -340,6 +345,12 @@ export class SyncService implements SyncServiceAbstraction {
response.forEach((f) => {
folders[f.id] = new FolderData(f.toFolder());
});
console.log("hi");
this.store.dispatch(
FoldersActions.loadBooksSuccess({ folders: response.map((f) => f.toFolder()) })
);
return await this.folderService.replace(folders);
}

View File

@@ -0,0 +1,51 @@
import { createReducer, on } from "@ngrx/store";
import * as FolderActions from "./folders.actions";
export const collectionFeatureKey = "collection";
export interface State {
loaded: boolean;
ids: string[];
}
const initialState: State = {
loaded: false,
ids: [],
};
export const reducer = createReducer(
initialState,
on(FolderActions.loadBooksSuccess, (_state, { folders }) => ({
loaded: true,
ids: folders.map((f) => f.id),
})),
/**
* Optimistically add book to collection.
* If this succeeds there's nothing to do.
* If this fails we revert state by removing the book.
*
* `on` supports handling multiple types of actions
*/
on(FolderActions.addFolder, (state, { folder }) => {
if (state.ids.indexOf(folder.id) > -1) {
return state;
}
return {
...state,
ids: [...state.ids, folder.id],
};
}),
/**
* Optimistically remove book from collection.
* If addBook fails, we "undo" adding the book.
*/
on(FolderActions.removeFolder, (state, { folder }) => ({
...state,
ids: state.ids.filter((id) => id !== folder.id),
}))
);
export const getLoaded = (state: State) => state.loaded;
export const getIds = (state: State) => state.ids;

View File

@@ -0,0 +1,7 @@
import { EncString } from "jslib-common/models/domain/encString";
export interface Folder {
id: string;
name: EncString;
revisionDate: Date;
}

View File

@@ -0,0 +1,17 @@
import { createAction, props } from "@ngrx/store";
import { Folder } from "./folder";
export const addFolder = createAction("[AddEdit Folder] Add Folder", props<{ folder: Folder }>());
export const editFolder = createAction("[AddEdit Folder] Edit Folder", props<{ folder: Folder }>());
export const removeFolder = createAction(
"[AddEdit Folder] Remove Folder",
props<{ folder: Folder }>()
);
export const loadBooksSuccess = createAction(
"[Sync API] Loaded Folders Success",
props<{ folders: Folder[] }>()
);

View File

@@ -0,0 +1,20 @@
import { createEntityAdapter, EntityAdapter, EntityState } from "@ngrx/entity";
import { createReducer, on } from "@ngrx/store";
import { Folder } from "./folder";
import * as FolderActions from "./folders.actions";
export const foldersFeatureKey = "folders";
export type State = EntityState<Folder>
export const adapter: EntityAdapter<Folder> = createEntityAdapter<Folder>({
sortComparer: false,
});
export const initialState: State = adapter.getInitialState({});
export const reducer = createReducer(
initialState,
on(FolderActions.loadBooksSuccess, (state, { folders }) => adapter.addMany(folders, state))
);

47
common/src/state/index.ts Normal file
View File

@@ -0,0 +1,47 @@
import { createSelector, createFeatureSelector } from "@ngrx/store";
import * as fromCollection from "./collection.reducer";
import { Folder } from "./folder";
import * as fromBooks from "./folders.reducer";
export interface State {
[fromBooks.foldersFeatureKey]: fromBooks.State;
[fromCollection.collectionFeatureKey]: fromCollection.State;
}
export const reducers = {
[fromBooks.foldersFeatureKey]: fromBooks.reducer,
[fromCollection.collectionFeatureKey]: fromCollection.reducer,
};
export const selectFoldersState = createFeatureSelector<State>(fromBooks.foldersFeatureKey);
export const selectFolderEntitiesState = createSelector(
selectFoldersState,
(state) => state.folders
);
export const {
selectIds: selectFolderIds,
selectEntities: selectFolderEntities,
selectAll: selectAllFolders,
selectTotal: selectTotalFolders,
} = fromBooks.adapter.getSelectors(selectFolderEntitiesState);
export const selectCollectionState = createSelector(
selectFoldersState,
(state) => state.collection
);
export const selectCollectionFolderIds = createSelector(
selectCollectionState,
fromCollection.getIds
);
export const selectFolderCollection = createSelector(
selectFolderEntities,
selectCollectionFolderIds,
(entities, ids) => {
return ids.map((id) => entities[id]).filter((folder): folder is Folder => folder != null);
}
);