mirror of
https://github.com/bitwarden/browser.git
synced 2025-02-17 01:31:25 +01:00
fixes and move to jslib share component
This commit is contained in:
parent
ce7930bcc4
commit
db5855aa4b
2
jslib
2
jslib
@ -1 +1 @@
|
||||
Subproject commit 2f6426deb470b71838b51c52587929ac64d428bf
|
||||
Subproject commit 89c23522d5697c722dcbe7d074cd12ebb6dc8783
|
@ -554,6 +554,9 @@
|
||||
"sharedItem": {
|
||||
"message": "Shared Item"
|
||||
},
|
||||
"shareDesc": {
|
||||
"message": "Choose an organization that you wish to share this item with. Sharing transfers ownership of the item to the organization. You will no longer be the direct owner of this item once it has been shared."
|
||||
},
|
||||
"learnMore": {
|
||||
"message": "Learn more"
|
||||
},
|
||||
@ -1094,5 +1097,11 @@
|
||||
},
|
||||
"neverLockWarning": {
|
||||
"message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected."
|
||||
},
|
||||
"noOrganizationsList": {
|
||||
"message": "You do not belong to any organizations. Organizations allow you to securely share items with other users."
|
||||
},
|
||||
"noCollectionsInList": {
|
||||
"message": "There are no collections to list."
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +120,9 @@ export const routerTransition = trigger('routerTransition', [
|
||||
transition('add-cipher => generator, edit-cipher => generator', inSlideUp),
|
||||
transition('generator => add-cipher, generator => edit-cipher', outSlideDown),
|
||||
|
||||
transition('edit-cipher => share-cipher', inSlideUp),
|
||||
transition('share-cipher => edit-cipher', outSlideDown),
|
||||
|
||||
transition('edit-cipher => attachments', inSlideLeft),
|
||||
transition('attachments => edit-cipher', outSlideRight),
|
||||
|
||||
|
@ -29,11 +29,11 @@ import { SyncComponent } from './settings/sync.component';
|
||||
import { TabsComponent } from './tabs.component';
|
||||
import { AddEditComponent } from './vault/add-edit.component';
|
||||
import { AttachmentsComponent } from './vault/attachments.component';
|
||||
import { ShareComponent } from './vault/share.component';
|
||||
import { CiphersComponent } from './vault/ciphers.component';
|
||||
import { CurrentTabComponent } from './vault/current-tab.component';
|
||||
import { GroupingsComponent } from './vault/groupings.component';
|
||||
import { PasswordHistoryComponent } from './vault/password-history.component';
|
||||
import { ShareComponent } from './vault/share.component';
|
||||
import { ViewComponent } from './vault/view.component';
|
||||
|
||||
const routes: Routes = [
|
||||
@ -125,10 +125,10 @@ const routes: Routes = [
|
||||
data: { state: 'edit-cipher' },
|
||||
},
|
||||
{
|
||||
path: 'share',
|
||||
path: 'share-cipher',
|
||||
component: ShareComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { state: 'share' },
|
||||
data: { state: 'share-cipher' },
|
||||
},
|
||||
{
|
||||
path: 'attachments',
|
||||
|
@ -36,11 +36,11 @@ import { SyncComponent } from './settings/sync.component';
|
||||
import { TabsComponent } from './tabs.component';
|
||||
import { AddEditComponent } from './vault/add-edit.component';
|
||||
import { AttachmentsComponent } from './vault/attachments.component';
|
||||
import { ShareComponent } from './vault/share.component';
|
||||
import { CiphersComponent } from './vault/ciphers.component';
|
||||
import { CurrentTabComponent } from './vault/current-tab.component';
|
||||
import { GroupingsComponent } from './vault/groupings.component';
|
||||
import { PasswordHistoryComponent } from './vault/password-history.component';
|
||||
import { ShareComponent } from './vault/share.component';
|
||||
import { ViewComponent } from './vault/view.component';
|
||||
|
||||
import { ApiActionDirective } from 'jslib/angular/directives/api-action.directive';
|
||||
|
@ -318,20 +318,14 @@
|
||||
</div>
|
||||
<div class="box list" *ngIf="editMode">
|
||||
<div class="box-content single-line">
|
||||
<a class="box-content-row" href="#" appStopClick appBlurClick
|
||||
(click)="share()" #shareBtn>
|
||||
<div class="row-main">
|
||||
<div class="icon">
|
||||
<i class="fa fa-share fa-lg fa-fw" [hidden]="shareBtn.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!shareBtn.loading"></i>
|
||||
<a class="box-content-row" href="#" appStopClick appBlurClick (click)="share()">
|
||||
<div class="row-main text-primary">
|
||||
<div class="icon text-primary">
|
||||
<i class="fa fa-share-alt fa-lg fa-fw"></i>
|
||||
</div>
|
||||
<span>{{'shareItem' | i18n}}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list" *ngIf="editMode">
|
||||
<div class="box-content single-line">
|
||||
<a class="box-content-row" href="#" appStopClick appBlurClick
|
||||
(click)="delete()" [appApiAction]="deletePromise" #deleteBtn>
|
||||
<div class="row-main text-danger">
|
||||
|
@ -1,8 +1,5 @@
|
||||
import { Location } from '@angular/common';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
@ -10,10 +7,12 @@ import {
|
||||
|
||||
import { AuditService } from 'jslib/abstractions/audit.service';
|
||||
import { CipherService } from 'jslib/abstractions/cipher.service';
|
||||
import { CollectionService } from 'jslib/abstractions/collection.service';
|
||||
import { FolderService } from 'jslib/abstractions/folder.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib/abstractions/state.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
|
||||
import { AddEditComponent as BaseAddEditComponent } from 'jslib/angular/components/add-edit.component';
|
||||
|
||||
@ -21,18 +20,21 @@ import { AddEditComponent as BaseAddEditComponent } from 'jslib/angular/componen
|
||||
selector: 'app-vault-add-edit',
|
||||
templateUrl: 'add-edit.component.html',
|
||||
})
|
||||
export class AddEditComponent extends BaseAddEditComponent implements OnInit {
|
||||
export class AddEditComponent extends BaseAddEditComponent {
|
||||
showAttachments = true;
|
||||
|
||||
constructor(cipherService: CipherService, folderService: FolderService,
|
||||
i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
||||
auditService: AuditService, stateService: StateService,
|
||||
userService: UserService, collectionService: CollectionService,
|
||||
private route: ActivatedRoute, private router: Router,
|
||||
private location: Location) {
|
||||
super(cipherService, folderService, i18nService, platformUtilsService, auditService, stateService);
|
||||
super(cipherService, folderService, i18nService, platformUtilsService, auditService, stateService,
|
||||
userService, collectionService);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
this.showAttachments = !this.platformUtilsService.isEdge();
|
||||
this.route.queryParams.subscribe(async (params) => {
|
||||
if (params.cipherId) {
|
||||
@ -84,7 +86,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit {
|
||||
}
|
||||
|
||||
share() {
|
||||
this.router.navigate(['/share'], { queryParams: { cipherId: this.cipher.id } });
|
||||
this.router.navigate(['/share-cipher'], { queryParams: { cipherId: this.cipher.id } });
|
||||
}
|
||||
|
||||
cancel() {
|
||||
|
@ -4,46 +4,35 @@
|
||||
<button type="button" appBlurClick (click)="cancel()">{{'cancel' | i18n}}</button>
|
||||
</div>
|
||||
<div class="center">
|
||||
<span class="title">{{'share' | i18n}}</span>
|
||||
<span class="title">{{'shareItem' | i18n}}</span>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading || !canSave" *ngIf="organizations && organizations.length">
|
||||
<button type="submit" appBlurClick [disabled]="form.loading || !canSave"
|
||||
*ngIf="organizations && organizations.length">
|
||||
<span [hidden]="form.loading">{{'save' | i18n}}</span>
|
||||
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<content *ngIf="cipher">
|
||||
<content>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'itemInformation' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="name">{{'name' | i18n}}</label>
|
||||
<input id="name" type="text" name="Name" [(ngModel)]="cipher.name">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'organization' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="!organizations || !organizations.length">
|
||||
{{'noOrganizationsList' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="organizations && organizations.length">
|
||||
<p>{{'shareDesc' | i18n}}</p>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<select id="organization" name="OrganizationId" [(ngModel)]="organizationId" (change)="filterCollections()">
|
||||
<label for="organization">{{'organization' | i18n}}</label>
|
||||
<select id="organization" name="OrganizationId" [(ngModel)]="organizationId"
|
||||
(change)="filterCollections()">
|
||||
<option *ngFor="let o of organizations" [ngValue]="o.id">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'shareDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box" *ngIf="organizations && organizations.length">
|
||||
<div class="box-header">
|
||||
{{'collections' | i18n}}
|
||||
</div>
|
||||
@ -51,13 +40,13 @@
|
||||
{{'noCollectionsInList' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="collections && collections.length">
|
||||
<div *ngFor="let c of collections; let i = index" (click)="check(c)">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="collection">{{c.name}}</label>
|
||||
<input id="collection" type="checkbox" [(ngModel)]="c.checked" name="Collection[{{i}}].Checked">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox"
|
||||
*ngFor="let c of collections; let i = index" appBoxRow>
|
||||
<label for="collection_{{i}}">{{c.name}}</label>
|
||||
<input id="collection_{{i}}" type="checkbox" [(ngModel)]="c.checked"
|
||||
name="Collection[{{i}}].Checked">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</content>
|
||||
</form>
|
||||
</form>
|
||||
|
@ -1,123 +1,35 @@
|
||||
import { Location } from '@angular/common';
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
import { Angulartics2 } from 'angulartics2';
|
||||
import { Component } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { CipherService } from 'jslib/abstractions/cipher.service';
|
||||
import { CollectionService } from 'jslib/abstractions/collection.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
import { AuditService } from 'jslib/abstractions/audit.service';
|
||||
import { FolderService } from 'jslib/abstractions/folder.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib/abstractions/state.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
|
||||
import { Organization } from 'jslib/models/domain/organization';
|
||||
import { CipherView } from 'jslib/models/view/cipherView';
|
||||
import { CollectionView } from 'jslib/models/view/collectionView';
|
||||
import { ShareComponent as BaseShareComponent } from 'jslib/angular/components/share.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-share',
|
||||
templateUrl: 'share.component.html',
|
||||
})
|
||||
export class ShareComponent implements OnInit, OnDestroy {
|
||||
formPromise: Promise<any>;
|
||||
cipher: CipherView;
|
||||
cipherId: string;
|
||||
organizationId: string;
|
||||
collections: CollectionView[] = [];
|
||||
organizations: Organization[] = [];
|
||||
|
||||
private writeableCollections: CollectionView[] = [];
|
||||
|
||||
constructor(private cipherService: CipherService, private collectionService: CollectionService,
|
||||
private userService: UserService, private i18nService: I18nService,
|
||||
private route: ActivatedRoute, private location: Location,
|
||||
private toasterService: ToasterService, private analytics: Angulartics2) {
|
||||
export class ShareComponent extends BaseShareComponent {
|
||||
constructor(collectionService: CollectionService, platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService, userService: UserService,
|
||||
cipherService: CipherService, private route: ActivatedRoute,
|
||||
private location: Location) {
|
||||
super(collectionService, platformUtilsService, i18nService, userService, cipherService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.route.queryParams.subscribe(async (params) => {
|
||||
if (params.cipherId) {
|
||||
this.cipherId = params.cipherId;
|
||||
}
|
||||
this.cipherId = params.cipherId;
|
||||
await super.ngOnInit();
|
||||
});
|
||||
|
||||
|
||||
const cipherDomain = await this.cipherService.get(this.cipherId);
|
||||
this.cipher = await cipherDomain.decrypt();
|
||||
const allCollections = await this.collectionService.getAllDecrypted();
|
||||
this.writeableCollections = allCollections.filter((c) => !c.readOnly);
|
||||
this.organizations = await this.userService.getAllOrganizations();
|
||||
if (this.organizationId == null && this.organizations.length > 0) {
|
||||
this.organizationId = this.organizations[0].id;
|
||||
}
|
||||
this.filterCollections();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
}
|
||||
|
||||
filterCollections() {
|
||||
if (this.organizationId == null || this.writeableCollections.length === 0) {
|
||||
this.collections = [];
|
||||
} else {
|
||||
this.collections = this.writeableCollections.filter((c) => c.organizationId === this.organizationId);
|
||||
}
|
||||
}
|
||||
|
||||
async submit() {
|
||||
const cipherDomain = await this.cipherService.get(this.cipherId);
|
||||
const cipherView = await cipherDomain.decrypt();
|
||||
|
||||
const attachmentPromises: Array<Promise<any>> = [];
|
||||
if (cipherView.attachments != null) {
|
||||
for (const attachment of cipherView.attachments) {
|
||||
const promise = this.cipherService.shareAttachmentWithServer(attachment,
|
||||
cipherView.id, this.organizationId);
|
||||
attachmentPromises.push(promise);
|
||||
}
|
||||
}
|
||||
|
||||
const checkedCollectionIds = this.collections.filter((c) => (c as any).checked).map((c) => c.id);
|
||||
try {
|
||||
this.formPromise = Promise.all(attachmentPromises).then(async () => {
|
||||
await this.cipherService.shareWithServer(cipherView, this.organizationId, checkedCollectionIds);
|
||||
this.analytics.eventTrack.next({ action: 'Shared Cipher' });
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('sharedItem'));
|
||||
});
|
||||
await this.formPromise;
|
||||
} catch { }
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.location.back();
|
||||
}
|
||||
|
||||
check(c: CollectionView, select?: boolean) {
|
||||
(c as any).checked = select == null ? !(c as any).checked : select;
|
||||
}
|
||||
|
||||
get canSave() {
|
||||
if (this.collections != null) {
|
||||
for (let i = 0; i < this.collections.length; i++) {
|
||||
if ((this.collections[i] as any).checked) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user