Skip to content

Commit 6a6fa98

Browse files
Merge pull request #1132 from rocket-admin/custom-favicon
Custom favicon
2 parents eb740ad + 5c26346 commit 6a6fa98

File tree

8 files changed

+242
-87
lines changed

8 files changed

+242
-87
lines changed

frontend/src/app/app.component.html

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,33 @@
5454
<mat-sidenav-content class="main-menu-content">
5555
<mat-toolbar color="primary" class="nav-bar"
5656
[ngClass]="{'nav-bar_home': !connectionID, 'nav-bar_connection': connectionID,
57-
'nav-bar_exterior': !userLoggedIn, 'nav-bar_interior': userLoggedIn}">
58-
<a routerLink="/connections-list" *ngIf="userLoggedIn && logo" class="logo">
59-
<img [src]="logo" alt="Logo" class="logo__image">
57+
'nav-bar_exterior': userLoggedIn === false, 'nav-bar_interior': userLoggedIn === true}">
58+
<!--<a routerLink="/connections-list" *ngIf="userLoggedIn && whiteLabelSettings.logo" class="logo">
59+
<img [src]="whiteLabelSettings.logo" alt="Logo" class="logo__image">
6060
</a>
6161
62-
<a routerLink="/connections-list" *ngIf="userLoggedIn && !logo" class="logo">
63-
<img src="../assets/rocketadmin_logo_white.svg" alt="Rocketadmin" class="logo__image">
64-
</a>
62+
<a routerLink="/connections-list" *ngIf="userLoggedIn && !whiteLabelSettings.logo" class="logo">
63+
<img src="../assets/rocketadmin_logo_white.svg" *ngIf="whiteLabelSettings.logo !== '' && !whiteLabelSettings.logo" class="logo__image">
64+
</a>-->
6565

66-
<a *ngIf="!userLoggedIn" href="https://rocketadmin.com/" class="logo">
67-
<picture>
68-
<source media="(max-width: 767px)" srcset="../assets/rocketadmin_logo_white-short.svg">
69-
<img src="../assets/rocketadmin_logo_white.svg" alt="Rocketadmin logo" class="logo__image">
70-
</picture>
71-
</a>
66+
<div>
67+
<a routerLink="/connections-list"
68+
*ngIf="userLoggedIn"
69+
class="logo">
70+
<img *ngIf="whiteLabelSettingsLoaded"
71+
[src]="whiteLabelSettings.logo || '../assets/rocketadmin_logo_white.svg'"
72+
alt="Logo"
73+
class="logo__image"
74+
>
75+
</a>
76+
77+
<a *ngIf="userLoggedIn === false" href="https://rocketadmin.com/" class="logo">
78+
<picture>
79+
<source media="(max-width: 767px)" srcset="../assets/rocketadmin_logo_white-short.svg">
80+
<img src="../assets/rocketadmin_logo_white.svg" alt="Rocketadmin logo" class="logo__image">
81+
</picture>
82+
</a>
83+
</div>
7284

7385
<div *ngIf="connectionID" class="menu">
7486
<a mat-button data-testid="connections-list-header-link"
@@ -138,7 +150,7 @@
138150
</mat-menu>
139151
</div>
140152

141-
<div *ngIf="userLoggedIn === null" class="actions">
153+
<div *ngIf="!currentUser" class="actions">
142154
<ng-container *ngIf="page === '/login'">
143155
<a mat-stroked-button routerLink="/login"
144156
data-testid="login-header-link"

frontend/src/app/app.component.spec.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { TablesService } from './services/tables.service';
2020
import { DomSanitizer } from '@angular/platform-browser';
2121
import { ChangeDetectorRef } from '@angular/core';
2222

23-
fdescribe('AppComponent', () => {
23+
describe('AppComponent', () => {
2424
let app: AppComponent;
2525
let fixture: ComponentFixture<AppComponent>;
2626
// let connectionsService: ConnectionsService;
@@ -56,7 +56,7 @@ fdescribe('AppComponent', () => {
5656
};
5757

5858
const mockCompanyService = {
59-
getCompanyLogo: jasmine.createSpy('getCompanyLogo')
59+
getWhiteLabelProperties: jasmine.createSpy('getWhiteLabelProperties')
6060
};
6161

6262
const mockUiSettingsService = {
@@ -108,8 +108,15 @@ fdescribe('AppComponent', () => {
108108
beforeEach(() => {
109109
fixture = TestBed.createComponent(AppComponent);
110110
app = fixture.debugElement.componentInstance;
111-
// connectionsService = TestBed.inject(ConnectionsService);
112-
// companyService = TestBed.inject(CompanyService);
111+
112+
app.navigationTabs = {
113+
'dashboard': { caption: 'Tables' },
114+
'audit': { caption: 'Audit' },
115+
'permissions': { caption: 'Permissions' },
116+
'connection-settings': { caption: 'Connection settings' },
117+
'edit-db': { caption: 'Edit connection' },
118+
};
119+
113120
fixture.detectChanges();
114121

115122
spyOn(app, 'logOut');
@@ -121,7 +128,7 @@ fdescribe('AppComponent', () => {
121128
(app.logOut as jasmine.Spy)?.calls.reset?.();
122129
(app['router'].navigate as jasmine.Spy)?.calls.reset?.();
123130
mockUiSettingsService.getUiSettings.calls.reset?.();
124-
mockCompanyService.getCompanyLogo.calls.reset?.();
131+
mockCompanyService.getWhiteLabelProperties.calls.reset?.();
125132
mockUserService.fetchUser.calls.reset?.();
126133
});
127134

@@ -130,19 +137,19 @@ fdescribe('AppComponent', () => {
130137
});
131138

132139
it('should set userLoggedIn and logo on user session initialization', fakeAsync(() => {
133-
mockCompanyService.getCompanyLogo.and.returnValue(of('data:png;base64,some-base64-data'));
140+
mockCompanyService.getWhiteLabelProperties.and.returnValue(of({logo: 'data:png;base64,some-base64-data'}));
134141
mockUiSettingsService.getUiSettings.and.returnValue(of({settings: {globalSettings: {lastFeatureNotificationId: 'old-id'}}}));
135142
app.initializeUserSession();
136143
tick();
137144

138145
expect(app.currentUser.email).toBe('[email protected]');
139-
expect(app.logo).toBe('data:png;base64,some-base64-data');
146+
expect(app.whiteLabelSettings.logo).toBe('data:png;base64,some-base64-data');
140147
expect(app.userLoggedIn).toBeTrue();
141148
expect(mockUiSettingsService.getUiSettings).toHaveBeenCalled();
142149
}));
143150

144151
it('should render custom logo in navbar if it is set', fakeAsync(() => {
145-
mockCompanyService.getCompanyLogo.and.returnValue(of('data:png;base64,some-base64-data'));
152+
mockCompanyService.getWhiteLabelProperties.and.returnValue(of({logo: 'data:png;base64,some-base64-data'}));
146153
mockUiSettingsService.getUiSettings.and.returnValue(of({settings: {globalSettings: {lastFeatureNotificationId: 'old-id'}}}));
147154
app.initializeUserSession();
148155
tick();
@@ -156,7 +163,7 @@ fdescribe('AppComponent', () => {
156163
}));
157164

158165
it('should render the link to Connetions list that contains the custom logo in the navbar', fakeAsync(() => {
159-
mockCompanyService.getCompanyLogo.and.returnValue(of(null));
166+
mockCompanyService.getWhiteLabelProperties.and.returnValue(of({logo: null}));
160167
mockUiSettingsService.getUiSettings.and.returnValue(of({settings: {globalSettings: {lastFeatureNotificationId: 'old-id'}}}));
161168
app.initializeUserSession();
162169
tick();
@@ -184,7 +191,7 @@ fdescribe('AppComponent', () => {
184191

185192
it('should render feature popup if isFeatureNotificationShown different on server and client', fakeAsync(() => {
186193
app.currentFeatureNotificationId = 'new-id';
187-
mockCompanyService.getCompanyLogo.and.returnValue(of(null));
194+
mockCompanyService.getWhiteLabelProperties.and.returnValue(of({logo: null}));
188195
mockUiSettingsService.getUiSettings.and.returnValue(of({globalSettings: {lastFeatureNotificationId: 'old-id'}}));
189196
app.initializeUserSession();
190197
tick();
@@ -197,7 +204,7 @@ fdescribe('AppComponent', () => {
197204

198205
it('should not render feature popup if isFeatureNotificationShown the same on server and client', fakeAsync(() => {
199206
app.currentFeatureNotificationId = 'old-id';
200-
mockCompanyService.getCompanyLogo.and.returnValue(of(null));
207+
mockCompanyService.getWhiteLabelProperties.and.returnValue(of({logo: null}));
201208
mockUiSettingsService.getUiSettings.and.returnValue(of({globalSettings: {lastFeatureNotificationId: 'old-id'}}));
202209
app.initializeUserSession();
203210
tick();
@@ -238,7 +245,6 @@ fdescribe('AppComponent', () => {
238245

239246
it('should handle user login flow when cast emits user with expires', fakeAsync(() => {
240247
const expirationDate = new Date(Date.now() + 10_000); // 10s from now
241-
// spyOn(app['router'], 'navigate');
242248
app['currentFeatureNotificationId'] = 'some-id';
243249

244250
app.ngOnInit();
@@ -254,7 +260,7 @@ fdescribe('AppComponent', () => {
254260
expect(app.userLoggedIn).toBeTrue();
255261
expect(app.currentUser.email).toBe('[email protected]');
256262
expect(mockUserService.fetchUser).toHaveBeenCalled();
257-
expect(mockCompanyService.getCompanyLogo).toHaveBeenCalledWith('company-12345678');
263+
expect(mockCompanyService.getWhiteLabelProperties).toHaveBeenCalledWith('company-12345678');
258264
expect(mockUiSettingsService.getUiSettings).toHaveBeenCalled();
259265
expect(app.isFeatureNotificationShown).toBeTrue();
260266
}));
@@ -263,9 +269,6 @@ fdescribe('AppComponent', () => {
263269
const expiration = new Date(Date.now() + 5000); // 5s ahead
264270
localStorage.setItem('token_expiration', expiration.toString());
265271

266-
// spyOn(app['router'], 'navigate');
267-
// spyOn(app, 'logOut');
268-
269272
spyOn(app, 'initializeUserSession').and.callFake(() => {
270273
app.userLoggedIn = true;
271274
});
@@ -283,12 +286,10 @@ fdescribe('AppComponent', () => {
283286
expect(app['router'].navigate).toHaveBeenCalledWith(['/login']);
284287
}));
285288

286-
xit('should immediately log out and navigate to login if token is expired', fakeAsync(() => {
289+
it('should immediately log out and navigate to login if token is expired', fakeAsync(() => {
287290
const expiration = new Date(Date.now() - 5000); // Expired 5s ago
288291
localStorage.setItem('token_expiration', expiration.toString());
289292

290-
// spyOn(app['router'], 'navigate');
291-
// spyOn(app, 'logOut');
292293
spyOn(app, 'initializeUserSession');
293294

294295
app.userLoggedIn = true;

frontend/src/app/app.component.ts

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,14 @@ export class AppComponent {
7171
navigationTabs: object;
7272
currentUser: User;
7373
page: string;
74-
logo: any;
75-
// upgradeButtonShown: boolean = true;
74+
whiteLabelSettingsLoaded = false;
75+
whiteLabelSettings: {
76+
logo: string,
77+
favicon: string,
78+
} = {
79+
logo: '',
80+
favicon: ''
81+
}
7682

7783
constructor (
7884
private changeDetector: ChangeDetectorRef,
@@ -118,6 +124,12 @@ export class AppComponent {
118124
localStorage.setItem('token_expiration', expirationDateString.toString());
119125
};
120126

127+
const expirationToken = localStorage.getItem('token_expiration');
128+
129+
if (!expirationToken) {
130+
this.setUserLoggedIn(false);
131+
}
132+
121133
this.navigationTabs = {
122134
'dashboard': {
123135
caption: 'Tables'
@@ -154,9 +166,8 @@ export class AppComponent {
154166
}, expirationInterval);
155167

156168
}
157-
// app initialization if user is logged in
158-
else if (res !== 'delete') {
159-
const expirationToken = localStorage.getItem('token_expiration');
169+
// app initialization if user is logged in (session restoration)
170+
if (expirationToken) {
160171
const expirationTime = expirationToken ? new Date(expirationToken) : null;
161172
const currantTime = new Date();
162173

@@ -214,8 +225,43 @@ export class AppComponent {
214225
user_id: res.id,
215226
email: res.email
216227
});
217-
this._company.getCompanyLogo(res.company.id).subscribe( logo => {
218-
this.logo = logo;
228+
this._company.getWhiteLabelProperties(res.company.id).subscribe( whiteLabelSettings => {
229+
this.whiteLabelSettings.logo = whiteLabelSettings.logo;
230+
this.whiteLabelSettingsLoaded = true;
231+
232+
if (whiteLabelSettings.favicon) {
233+
const link: HTMLLinkElement | null = document.querySelector("link[rel*='icon']");
234+
if (link) {
235+
link.href = whiteLabelSettings.favicon;
236+
} else {
237+
const newLink = document.createElement('link');
238+
newLink.rel = 'icon';
239+
newLink.href = whiteLabelSettings.favicon;
240+
document.head.appendChild(newLink);
241+
}
242+
} else {
243+
const faviconIco = document.createElement('link');
244+
faviconIco.rel = 'icon';
245+
faviconIco.type = 'image/x-icon';
246+
faviconIco.href = 'assets/favicon.ico';
247+
248+
const favicon16 = document.createElement('link');
249+
favicon16.rel = 'icon';
250+
favicon16.type = 'image/png';
251+
favicon16.setAttribute('sizes', '16x16');
252+
favicon16.href = 'assets/favicon-16x16.png';
253+
254+
const favicon32 = document.createElement('link');
255+
favicon32.rel = 'icon';
256+
favicon32.type = 'image/png';
257+
favicon32.setAttribute('sizes', '32x32');
258+
favicon32.href = 'assets/favicon-32x32.png';
259+
260+
document.head.appendChild(favicon16);
261+
document.head.appendChild(favicon32);
262+
}
263+
264+
// this.whiteLabelSettingsLoaded = true;
219265
})
220266
this._uiSettings.getUiSettings().subscribe(settings => {
221267
this.isFeatureNotificationShown = (settings?.globalSettings?.lastFeatureNotificationId !== this.currentFeatureNotificationId)

0 commit comments

Comments
 (0)