Move avatar component to jslib (#1233)
This commit is contained in:
parent
c3a910e785
commit
1b8f316066
2
jslib
2
jslib
|
@ -1 +1 @@
|
||||||
Subproject commit e3ab324d59ee04870007e5078901a22e4dd81440
|
Subproject commit 56233e4002d809113aaf221d30a2472a2767df42
|
|
@ -1,138 +0,0 @@
|
||||||
import {
|
|
||||||
Component,
|
|
||||||
Input,
|
|
||||||
OnChanges,
|
|
||||||
OnInit,
|
|
||||||
} from '@angular/core';
|
|
||||||
import { DomSanitizer } from '@angular/platform-browser';
|
|
||||||
|
|
||||||
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
|
||||||
|
|
||||||
import { Utils } from 'jslib-common/misc/utils';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-avatar',
|
|
||||||
template: '<img [src]="sanitizer.bypassSecurityTrustResourceUrl(src)" title="{{data}}" ' +
|
|
||||||
'[ngClass]="{\'rounded-circle\': circle}">',
|
|
||||||
})
|
|
||||||
export class AvatarComponent implements OnChanges, OnInit {
|
|
||||||
@Input() data: string;
|
|
||||||
@Input() email: string;
|
|
||||||
@Input() size = 45;
|
|
||||||
@Input() charCount = 2;
|
|
||||||
@Input() textColor = '#ffffff';
|
|
||||||
@Input() fontSize = 20;
|
|
||||||
@Input() fontWeight = 300;
|
|
||||||
@Input() dynamic = false;
|
|
||||||
@Input() circle = false;
|
|
||||||
|
|
||||||
src: string;
|
|
||||||
|
|
||||||
constructor(public sanitizer: DomSanitizer, private cryptoFunctionService: CryptoFunctionService,
|
|
||||||
private stateService: StateService) { }
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
if (!this.dynamic) {
|
|
||||||
this.generate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnChanges() {
|
|
||||||
if (this.dynamic) {
|
|
||||||
this.generate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async generate() {
|
|
||||||
const enableGravatars = await this.stateService.get<boolean>('enableGravatars');
|
|
||||||
if (enableGravatars && this.email != null) {
|
|
||||||
const hashBytes = await this.cryptoFunctionService.hash(this.email.toLowerCase().trim(), 'md5');
|
|
||||||
const hash = Utils.fromBufferToHex(hashBytes).toLowerCase();
|
|
||||||
this.src = 'https://www.gravatar.com/avatar/' + hash + '?s=' + this.size + '&r=pg&d=retro';
|
|
||||||
} else {
|
|
||||||
let chars: string = null;
|
|
||||||
const upperData = this.data.toUpperCase();
|
|
||||||
|
|
||||||
if (this.charCount > 1) {
|
|
||||||
chars = this.getFirstLetters(upperData, this.charCount);
|
|
||||||
}
|
|
||||||
if (chars == null) {
|
|
||||||
chars = this.unicodeSafeSubstring(upperData, this.charCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the chars contain an emoji, only show it.
|
|
||||||
if (chars.match(Utils.regexpEmojiPresentation)) {
|
|
||||||
chars = chars.match(Utils.regexpEmojiPresentation)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const charObj = this.getCharText(chars);
|
|
||||||
const color = this.stringToColor(upperData);
|
|
||||||
const svg = this.getSvg(this.size, color);
|
|
||||||
svg.appendChild(charObj);
|
|
||||||
const html = window.document.createElement('div').appendChild(svg).outerHTML;
|
|
||||||
const svgHtml = window.btoa(unescape(encodeURIComponent(html)));
|
|
||||||
this.src = 'data:image/svg+xml;base64,' + svgHtml;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private stringToColor(str: string): string {
|
|
||||||
let hash = 0;
|
|
||||||
for (let i = 0; i < str.length; i++) {
|
|
||||||
// tslint:disable-next-line
|
|
||||||
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
|
||||||
}
|
|
||||||
let color = '#';
|
|
||||||
for (let i = 0; i < 3; i++) {
|
|
||||||
// tslint:disable-next-line
|
|
||||||
const value = (hash >> (i * 8)) & 0xFF;
|
|
||||||
color += ('00' + value.toString(16)).substr(-2);
|
|
||||||
}
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getFirstLetters(data: string, count: number): string {
|
|
||||||
const parts = data.split(' ');
|
|
||||||
if (parts.length > 1) {
|
|
||||||
let text = '';
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
text += this.unicodeSafeSubstring(parts[i], 1);
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getSvg(size: number, color: string): HTMLElement {
|
|
||||||
const svgTag = window.document.createElement('svg');
|
|
||||||
svgTag.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
||||||
svgTag.setAttribute('pointer-events', 'none');
|
|
||||||
svgTag.setAttribute('width', size.toString());
|
|
||||||
svgTag.setAttribute('height', size.toString());
|
|
||||||
svgTag.style.backgroundColor = color;
|
|
||||||
svgTag.style.width = size + 'px';
|
|
||||||
svgTag.style.height = size + 'px';
|
|
||||||
return svgTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getCharText(character: string): HTMLElement {
|
|
||||||
const textTag = window.document.createElement('text');
|
|
||||||
textTag.setAttribute('text-anchor', 'middle');
|
|
||||||
textTag.setAttribute('y', '50%');
|
|
||||||
textTag.setAttribute('x', '50%');
|
|
||||||
textTag.setAttribute('dy', '0.35em');
|
|
||||||
textTag.setAttribute('pointer-events', 'auto');
|
|
||||||
textTag.setAttribute('fill', this.textColor);
|
|
||||||
textTag.setAttribute('font-family', '"Open Sans","Helvetica Neue",Helvetica,Arial,' +
|
|
||||||
'sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"');
|
|
||||||
textTag.textContent = character;
|
|
||||||
textTag.style.fontWeight = this.fontWeight.toString();
|
|
||||||
textTag.style.fontSize = this.fontSize + 'px';
|
|
||||||
return textTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
private unicodeSafeSubstring(str: string, count: number) {
|
|
||||||
const characters = str.match(/./ug);
|
|
||||||
return characters != null ? characters.slice(0, count).join('') : '';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,6 @@ import { RouterModule } from '@angular/router';
|
||||||
import { ToasterModule } from 'angular2-toaster';
|
import { ToasterModule } from 'angular2-toaster';
|
||||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||||
|
|
||||||
import { AvatarComponent } from './components/avatar.component';
|
|
||||||
import { NestedCheckboxComponent } from './components/nested-checkbox.component';
|
import { NestedCheckboxComponent } from './components/nested-checkbox.component';
|
||||||
import { PasswordRepromptComponent } from './components/password-reprompt.component';
|
import { PasswordRepromptComponent } from './components/password-reprompt.component';
|
||||||
import { PasswordStrengthComponent } from './components/password-strength.component';
|
import { PasswordStrengthComponent } from './components/password-strength.component';
|
||||||
|
@ -175,6 +174,7 @@ import { VaultComponent } from './vault/vault.component';
|
||||||
|
|
||||||
import { ProvidersComponent } from './providers/providers.component';
|
import { ProvidersComponent } from './providers/providers.component';
|
||||||
|
|
||||||
|
import { AvatarComponent } from 'jslib-angular/components/avatar.component';
|
||||||
import { CalloutComponent } from 'jslib-angular/components/callout.component';
|
import { CalloutComponent } from 'jslib-angular/components/callout.component';
|
||||||
import { IconComponent } from 'jslib-angular/components/icon.component';
|
import { IconComponent } from 'jslib-angular/components/icon.component';
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue