mirror of
https://github.com/bitwarden/desktop.git
synced 2024-11-30 12:54:31 +01:00
finish implementing view page
This commit is contained in:
parent
15868ab541
commit
8f9cc29661
@ -3,7 +3,7 @@ import {
|
|||||||
PipeTransform,
|
PipeTransform,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { I18nService } from '../../services/i18n.service';
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'i18n',
|
name: 'i18n',
|
||||||
|
@ -41,6 +41,7 @@ import {
|
|||||||
CryptoService as CryptoServiceAbstraction,
|
CryptoService as CryptoServiceAbstraction,
|
||||||
EnvironmentService as EnvironmentServiceAbstraction,
|
EnvironmentService as EnvironmentServiceAbstraction,
|
||||||
FolderService as FolderServiceAbstraction,
|
FolderService as FolderServiceAbstraction,
|
||||||
|
I18nService as I18nServiceAbstraction,
|
||||||
LockService as LockServiceAbstraction,
|
LockService as LockServiceAbstraction,
|
||||||
MessagingService as MessagingServiceAbstraction,
|
MessagingService as MessagingServiceAbstraction,
|
||||||
PasswordGenerationService as PasswordGenerationServiceAbstraction,
|
PasswordGenerationService as PasswordGenerationServiceAbstraction,
|
||||||
@ -58,7 +59,7 @@ webFrame.registerURLSchemeAsPrivileged('file');
|
|||||||
|
|
||||||
const i18nService = new I18nService(window.navigator.language, './locales');
|
const i18nService = new I18nService(window.navigator.language, './locales');
|
||||||
const utilsService = new UtilsService();
|
const utilsService = new UtilsService();
|
||||||
const platformUtilsService = new DesktopPlatformUtilsService();
|
const platformUtilsService = new DesktopPlatformUtilsService(i18nService);
|
||||||
const messagingService = new DesktopMessagingService();
|
const messagingService = new DesktopMessagingService();
|
||||||
const storageService: StorageServiceAbstraction = new DesktopStorageService();
|
const storageService: StorageServiceAbstraction = new DesktopStorageService();
|
||||||
const secureStorageService: StorageServiceAbstraction = new DesktopSecureStorageService();
|
const secureStorageService: StorageServiceAbstraction = new DesktopSecureStorageService();
|
||||||
@ -109,11 +110,14 @@ function initFactory(i18n: I18nService): Function {
|
|||||||
{ provide: EnvironmentServiceAbstraction, useValue: environmentService },
|
{ provide: EnvironmentServiceAbstraction, useValue: environmentService },
|
||||||
{ provide: TotpServiceAbstraction, useValue: totpService },
|
{ provide: TotpServiceAbstraction, useValue: totpService },
|
||||||
{ provide: TokenServiceAbstraction, useValue: tokenService },
|
{ provide: TokenServiceAbstraction, useValue: tokenService },
|
||||||
{ provide: I18nService, useValue: i18nService },
|
{ provide: I18nServiceAbstraction, useValue: i18nService },
|
||||||
|
{ provide: UtilsServiceAbstraction, useValue: utilsService },
|
||||||
|
{ provide: CryptoServiceAbstraction, useValue: cryptoService },
|
||||||
|
{ provide: PlatformUtilsServiceAbstraction, useValue: platformUtilsService },
|
||||||
{
|
{
|
||||||
provide: APP_INITIALIZER,
|
provide: APP_INITIALIZER,
|
||||||
useFactory: initFactory,
|
useFactory: initFactory,
|
||||||
deps: [I18nService],
|
deps: [I18nServiceAbstraction],
|
||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
<span class="row-label">{{'name' | i18n}}</span>
|
<span class="row-label">{{'name' | i18n}}</span>
|
||||||
{{cipher.name}}
|
{{cipher.name}}
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Login -->
|
||||||
<div *ngIf="cipher.login">
|
<div *ngIf="cipher.login">
|
||||||
<div class="box-content-row" *ngIf="cipher.login.uri">
|
<div class="box-content-row" *ngIf="cipher.login.uri">
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
@ -16,7 +17,8 @@
|
|||||||
*ngIf="cipher.login.canLaunch" (click)="launch()">
|
*ngIf="cipher.login.canLaunch" (click)="launch()">
|
||||||
<i class="fa fa-lg fa-share-square-o"></i>
|
<i class="fa fa-lg fa-share-square-o"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}">
|
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}"
|
||||||
|
(click)="copy(cipher.login.uri)">
|
||||||
<i class="fa fa-lg fa-clipboard"></i>
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -26,7 +28,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="box-content-row" *ngIf="cipher.login.username">
|
<div class="box-content-row" *ngIf="cipher.login.username">
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}">
|
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}"
|
||||||
|
(click)="copy(cipher.login.username)">
|
||||||
<i class="fa fa-lg fa-clipboard"></i>
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -40,7 +43,8 @@
|
|||||||
<i class="fa fa-lg"
|
<i class="fa fa-lg"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}">
|
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}"
|
||||||
|
(click)="copy(cipher.login.password)">
|
||||||
<i class="fa fa-lg fa-clipboard"></i>
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -51,7 +55,8 @@
|
|||||||
<div class="box-content-row totp" [ngClass]="{'low': totpLow}"
|
<div class="box-content-row totp" [ngClass]="{'low': totpLow}"
|
||||||
*ngIf="cipher.login.totp && totpCode">
|
*ngIf="cipher.login.totp && totpCode">
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}">
|
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}"
|
||||||
|
(click)="copy(totpCode)">
|
||||||
<i class="fa fa-lg fa-clipboard"></i>
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -69,6 +74,89 @@
|
|||||||
<span class="totp-code">{{totpCodeFormatted}}</span>
|
<span class="totp-code">{{totpCodeFormatted}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Card -->
|
||||||
|
<div *ngIf="cipher.card">
|
||||||
|
<div class="box-content-row" *ngIf="cipher.card.cardholderName">
|
||||||
|
<span class="row-label">{{'cardholderName' | i18n}}</span>
|
||||||
|
{{cipher.card.cardholderName}}
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" *ngIf="cipher.card.number">
|
||||||
|
<div class="action-buttons">
|
||||||
|
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}"
|
||||||
|
(click)="copy(cipher.card.number)">
|
||||||
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<span class="row-label">{{'number' | i18n}}</span>
|
||||||
|
{{cipher.card.number}}
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" *ngIf="cipher.card.brand">
|
||||||
|
<span class="row-label">{{'brand' | i18n}}</span>
|
||||||
|
{{cipher.card.brand}}
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" *ngIf="cipher.card.expiration">
|
||||||
|
<span class="row-label">{{'expiration' | i18n}}</span>
|
||||||
|
{{cipher.card.expiration}}
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" *ngIf="cipher.card.code">
|
||||||
|
<div class="action-buttons">
|
||||||
|
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}"
|
||||||
|
(click)="copy(cipher.card.code)">
|
||||||
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<span class="row-label">{{'securityCode' | i18n}}</span>
|
||||||
|
{{cipher.card.code}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Identity -->
|
||||||
|
<div *ngIf="cipher.identity">
|
||||||
|
<div class="box-content-row" *ngIf="cipher.identity.fullName">
|
||||||
|
<span class="row-label">{{'identityName' | i18n}}</span>
|
||||||
|
{{cipher.identity.fullName}}
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" *ngIf="cipher.identity.username">
|
||||||
|
<span class="row-label">{{'username' | i18n}}</span>
|
||||||
|
{{cipher.identity.username}}
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" *ngIf="cipher.identity.company">
|
||||||
|
<span class="row-label">{{'company' | i18n}}</span>
|
||||||
|
{{cipher.identity.company}}
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" *ngIf="cipher.identity.ssn">
|
||||||
|
<span class="row-label">{{'ssn' | i18n}}</span>
|
||||||
|
{{cipher.identity.ssn}}
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" *ngIf="cipher.identity.passportNumber">
|
||||||
|
<span class="row-label">{{'passportNumber' | i18n}}</span>
|
||||||
|
{{cipher.identity.passportNumber}}
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" *ngIf="cipher.identity.licenseNumber">
|
||||||
|
<span class="row-label">{{'licenseNumber' | i18n}}</span>
|
||||||
|
{{cipher.identity.licenseNumber}}
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" *ngIf="cipher.identity.email">
|
||||||
|
<span class="row-label">{{'email' | i18n}}</span>
|
||||||
|
{{cipher.identity.email}}
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" *ngIf="cipher.identity.phone">
|
||||||
|
<span class="row-label">{{'phone' | i18n}}</span>
|
||||||
|
{{cipher.identity.phone}}
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row"
|
||||||
|
*ngIf="cipher.identity.address1 || cipher.identity.city || cipher.identity.country">
|
||||||
|
<span class="row-label">{{'address' | i18n}}</span>
|
||||||
|
<div *ngIf="cipher.identity.address1">{{cipher.identity.address1}}</div>
|
||||||
|
<div *ngIf="cipher.identity.address2">{{cipher.identity.address2}}</div>
|
||||||
|
<div *ngIf="cipher.identity.address3">{{cipher.identity.address3}}</div>
|
||||||
|
<div *ngIf="cipher.identity.city || cipher.identity.state || cipher.identity.postalCode">
|
||||||
|
{{cipher.identity.city || '-'}},
|
||||||
|
{{cipher.identity.state || '-'}},
|
||||||
|
{{cipher.identity.postalCode || '-'}}
|
||||||
|
</div>
|
||||||
|
<div *ngIf="cipher.identity.country">{{cipher.identity.country}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box" *ngIf="cipher.notes">
|
<div class="box" *ngIf="cipher.notes">
|
||||||
@ -84,7 +172,31 @@
|
|||||||
{{'customFields' | i18n}}
|
{{'customFields' | i18n}}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
todo
|
<div class="box-content-row" *ngFor="let field of cipher.fields">
|
||||||
|
<div class="action-buttons">
|
||||||
|
<a class="row-btn" href="#" appStopClick title="{{'toggleVisibility' | i18n}}"
|
||||||
|
*ngIf="field.type === fieldType.Hidden" (click)="toggleFieldValue(field)">
|
||||||
|
<i class="fa fa-lg"
|
||||||
|
[ngClass]="{'fa-eye': !field.showValue, 'fa-eye-slash': field.showValue}"></i>
|
||||||
|
</a>
|
||||||
|
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}"
|
||||||
|
*ngIf="field.value && field.type !== fieldType.Boolean" (click)="copy(field.value)">
|
||||||
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<span class="row-label">{{field.name}}</span>
|
||||||
|
<div *ngIf="field.type === fieldType.Text">
|
||||||
|
{{field.value || ' '}}
|
||||||
|
</div>
|
||||||
|
<div *ngIf="field.type === fieldType.Hidden">
|
||||||
|
<span [hidden]="!field.showValue" class="monospaced">{{field.value}}</span>
|
||||||
|
<span [hidden]="field.showValue" class="monospaced">{{field.maskedValue}}</span>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="field.type === fieldType.Boolean">
|
||||||
|
<i class="fa fa-check-square-o" *ngIf="field.value === 'true'"></i>
|
||||||
|
<i class="fa fa-square-o" *ngIf="field.value !== 'true'"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box" *ngIf="cipher.hasAttachments && isPremium">
|
<div class="box" *ngIf="cipher.hasAttachments && isPremium">
|
||||||
@ -92,7 +204,13 @@
|
|||||||
{{'attachments' | i18n}}
|
{{'attachments' | i18n}}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
todo
|
<a class="box-content-row" *ngFor="let attachment of cipher.attachments" href="#" appStopClick
|
||||||
|
(click)="downloadAttachment(attachment)">
|
||||||
|
<i class="right-icon fa fa-download fa-fw" *ngIf="!attachment.downloading"></i>
|
||||||
|
<i class="right-icon fa fa-spinner fa-fw fa-spin" *ngIf="attachment.downloading"></i>
|
||||||
|
<small class="row-sub-label">{{attachment.sizeName}}</small>
|
||||||
|
{{attachment.fileName}}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,12 +10,19 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { CipherType } from 'jslib/enums/cipherType';
|
import { CipherType } from 'jslib/enums/cipherType';
|
||||||
|
import { FieldType } from 'jslib/enums/fieldType';
|
||||||
|
|
||||||
import { CipherService } from 'jslib/abstractions/cipher.service';
|
import { CipherService } from 'jslib/abstractions/cipher.service';
|
||||||
|
import { CryptoService } from 'jslib/abstractions/crypto.service';
|
||||||
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
|
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||||
import { TokenService } from 'jslib/abstractions/token.service';
|
import { TokenService } from 'jslib/abstractions/token.service';
|
||||||
import { TotpService } from 'jslib/abstractions/totp.service';
|
import { TotpService } from 'jslib/abstractions/totp.service';
|
||||||
|
import { UtilsService } from 'jslib/abstractions/utils.service';
|
||||||
|
|
||||||
|
import { AttachmentView } from 'jslib/models/view/attachmentView';
|
||||||
import { CipherView } from 'jslib/models/view/cipherView';
|
import { CipherView } from 'jslib/models/view/cipherView';
|
||||||
|
import { FieldView } from 'jslib/models/view/fieldView';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vault-view',
|
selector: 'app-vault-view',
|
||||||
@ -32,11 +39,14 @@ export class ViewComponent implements OnChanges, OnDestroy {
|
|||||||
totpDash: number;
|
totpDash: number;
|
||||||
totpSec: number;
|
totpSec: number;
|
||||||
totpLow: boolean;
|
totpLow: boolean;
|
||||||
|
fieldType = FieldType;
|
||||||
|
|
||||||
private totpInterval: NodeJS.Timer;
|
private totpInterval: NodeJS.Timer;
|
||||||
|
|
||||||
constructor(private cipherService: CipherService, private totpService: TotpService,
|
constructor(private cipherService: CipherService, private totpService: TotpService,
|
||||||
private tokenService: TokenService) {
|
private tokenService: TokenService, private utilsService: UtilsService,
|
||||||
|
private cryptoService: CryptoService, private platformUtilsService: PlatformUtilsService,
|
||||||
|
private i18nService: I18nService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnChanges() {
|
async ngOnChanges() {
|
||||||
@ -70,12 +80,57 @@ export class ViewComponent implements OnChanges, OnDestroy {
|
|||||||
this.showPassword = !this.showPassword;
|
this.showPassword = !this.showPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleFieldValue(field: FieldView) {
|
||||||
|
const f = (field as any);
|
||||||
|
f.showValue = !f.showValue;
|
||||||
|
}
|
||||||
|
|
||||||
launch() {
|
launch() {
|
||||||
// TODO
|
if (this.cipher.login.uri == null || this.cipher.login.uri.indexOf('://') === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.platformUtilsService.launchUri(this.cipher.login.uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(value: string) {
|
copy(value: string) {
|
||||||
// TODO
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.utilsService.copyToClipboard(value, window.document);
|
||||||
|
}
|
||||||
|
|
||||||
|
async downloadAttachment(attachment: AttachmentView) {
|
||||||
|
const a = (attachment as any);
|
||||||
|
if (a.downloading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.cipher.organizationId && !this.isPremium) {
|
||||||
|
this.platformUtilsService.alertError(this.i18nService.t('premiumRequired'),
|
||||||
|
this.i18nService.t('premiumRequiredDesc'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.downloading = true;
|
||||||
|
const response = await fetch(new Request(attachment.url, { cache: 'no-cache' }));
|
||||||
|
if (response.status !== 200) {
|
||||||
|
this.platformUtilsService.alertError(null, this.i18nService.t('errorOccurred'));
|
||||||
|
a.downloading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const buf = await response.arrayBuffer();
|
||||||
|
const key = await this.cryptoService.getOrgKey(this.cipher.organizationId);
|
||||||
|
const decBuf = await this.cryptoService.decryptFromBytes(buf, key);
|
||||||
|
this.platformUtilsService.saveFile(window, decBuf, null, attachment.fileName);
|
||||||
|
} catch (e) {
|
||||||
|
this.platformUtilsService.alertError(null, this.i18nService.t('errorOccurred'));
|
||||||
|
}
|
||||||
|
|
||||||
|
a.downloading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private cleanUp() {
|
private cleanUp() {
|
||||||
|
@ -91,5 +91,56 @@
|
|||||||
},
|
},
|
||||||
"toggleVisibility": {
|
"toggleVisibility": {
|
||||||
"message": "Toggle Visibility"
|
"message": "Toggle Visibility"
|
||||||
|
},
|
||||||
|
"cardholderName": {
|
||||||
|
"message": "Cardholder Name"
|
||||||
|
},
|
||||||
|
"number": {
|
||||||
|
"message": "Number"
|
||||||
|
},
|
||||||
|
"brand": {
|
||||||
|
"message": "Brand"
|
||||||
|
},
|
||||||
|
"expiration": {
|
||||||
|
"message": "Expiration"
|
||||||
|
},
|
||||||
|
"securityCode": {
|
||||||
|
"message": "Security Code"
|
||||||
|
},
|
||||||
|
"identityName": {
|
||||||
|
"message": "identityName"
|
||||||
|
},
|
||||||
|
"company": {
|
||||||
|
"message": "Company"
|
||||||
|
},
|
||||||
|
"ssn": {
|
||||||
|
"message": "Social Security Number"
|
||||||
|
},
|
||||||
|
"passportNumber": {
|
||||||
|
"message": "Passport Number"
|
||||||
|
},
|
||||||
|
"licenseNumber": {
|
||||||
|
"message": "License Number"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"message": "Email"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"message": "Phone"
|
||||||
|
},
|
||||||
|
"address": {
|
||||||
|
"message": "Address"
|
||||||
|
},
|
||||||
|
"premiumRequired": {
|
||||||
|
"message": "Premium Required"
|
||||||
|
},
|
||||||
|
"premiumRequiredDesc": {
|
||||||
|
"message": "A premium membership is required to use this feature."
|
||||||
|
},
|
||||||
|
"errorOccurred": {
|
||||||
|
"message": "An error has occurred."
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"message": "Error"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { app, BrowserWindow, ipcMain, screen } from 'electron';
|
import { app, BrowserWindow, dialog, ipcMain, screen } from 'electron';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
/*
|
/*
|
||||||
@ -26,6 +26,10 @@ ipcMain.on('keytar', async (event: any, message: any) => {
|
|||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
ipcMain.on('showError', async (event: any, message: any) => {
|
||||||
|
dialog.showErrorBox(message.title, message.message);
|
||||||
|
});
|
||||||
|
|
||||||
import { I18nService } from './services/i18n.service';
|
import { I18nService } from './services/i18n.service';
|
||||||
const i18nService = new I18nService('en', './locales/');
|
const i18nService = new I18nService('en', './locales/');
|
||||||
i18nService.init().then(() => { });
|
i18nService.init().then(() => { });
|
||||||
|
@ -589,6 +589,9 @@ a {
|
|||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
display: block;
|
||||||
|
color: $text-color;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "";
|
content: "";
|
||||||
@ -626,6 +629,12 @@ a {
|
|||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no-wrap {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
.row-label {
|
.row-label {
|
||||||
font-size: $font-size-small;
|
font-size: $font-size-small;
|
||||||
color: $text-muted;
|
color: $text-muted;
|
||||||
@ -666,6 +675,23 @@ a {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.right-icon {
|
||||||
|
float: right;
|
||||||
|
margin-top: 4px;
|
||||||
|
color: $list-icon-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-sub-label {
|
||||||
|
float: right;
|
||||||
|
display: block;
|
||||||
|
margin-right: 15px;
|
||||||
|
color: $gray-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
small.row-sub-label {
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
import { ipcRenderer, shell } from 'electron';
|
||||||
|
|
||||||
import { DeviceType } from 'jslib/enums';
|
import { DeviceType } from 'jslib/enums';
|
||||||
|
|
||||||
import { PlatformUtilsService } from 'jslib/abstractions';
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
|
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||||
|
|
||||||
const AnalyticsIds = {
|
const AnalyticsIds = {
|
||||||
[DeviceType.Windows]: 'UA-81915606-17',
|
[DeviceType.Windows]: 'UA-81915606-17',
|
||||||
@ -12,6 +15,9 @@ export class DesktopPlatformUtilsService implements PlatformUtilsService {
|
|||||||
private deviceCache: DeviceType = null;
|
private deviceCache: DeviceType = null;
|
||||||
private analyticsIdCache: string = null;
|
private analyticsIdCache: string = null;
|
||||||
|
|
||||||
|
constructor(private i18nService: I18nService) {
|
||||||
|
}
|
||||||
|
|
||||||
getDevice(): DeviceType {
|
getDevice(): DeviceType {
|
||||||
if (!this.deviceCache) {
|
if (!this.deviceCache) {
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
@ -93,4 +99,25 @@ export class DesktopPlatformUtilsService implements PlatformUtilsService {
|
|||||||
isViewOpen(): boolean {
|
isViewOpen(): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
launchUri(uri: string): void {
|
||||||
|
shell.openExternal(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveFile(win: Window, blobData: any, blobOptions: any, fileName: string): void {
|
||||||
|
const blob = new Blob([blobData], blobOptions);
|
||||||
|
const a = win.document.createElement('a');
|
||||||
|
a.href = win.URL.createObjectURL(blob);
|
||||||
|
a.download = fileName;
|
||||||
|
window.document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
window.document.body.removeChild(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
alertError(title: string, message: string): void {
|
||||||
|
ipcRenderer.send('showError', {
|
||||||
|
title: title || this.i18nService.t('error'),
|
||||||
|
message: message,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import { I18nService as I18nServiceAbstraction } from 'jslib/abstractions/i18n.service';
|
||||||
|
|
||||||
// First locale is the default (English)
|
// First locale is the default (English)
|
||||||
const SupportedLocales = [
|
const SupportedLocales = [
|
||||||
'en', 'es',
|
'en', 'es',
|
||||||
];
|
];
|
||||||
|
|
||||||
export class I18nService {
|
export class I18nService implements I18nServiceAbstraction {
|
||||||
defaultMessages: any = {};
|
defaultMessages: any = {};
|
||||||
localeMessages: any = {};
|
localeMessages: any = {};
|
||||||
language: string;
|
language: string;
|
||||||
|
Loading…
Reference in New Issue
Block a user