Skip to content

Commit

Permalink
feat: show screen to promote pwa
Browse files Browse the repository at this point in the history
  • Loading branch information
gion-andri committed Dec 14, 2023
1 parent 30a4fa7 commit 9dbdbc6
Show file tree
Hide file tree
Showing 11 changed files with 326 additions and 66 deletions.
69 changes: 68 additions & 1 deletion src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,34 @@ import { ActivatedRoute } from '@angular/router';
import { IframeService } from './services/iframe.service';
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
import { EventsFilterService } from './shared/services/events-filter.service';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Platform } from '@angular/cdk/platform';
import { DeviceDetectorService } from 'ngx-device-detector';
import {
PwaInstallInstructionsComponent
} from './components/pwa-install-instructions/pwa-install-instructions.component';

const LOCALSTORAGE_APP_OPEN_TIMES = 'chalender-app-open-times';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

private promptEvent: any;

constructor(
private translate: TranslateService,
private route: ActivatedRoute,
private iframeService: IframeService,
@Inject(DOCUMENT) private document: Document,
@Inject(PLATFORM_ID) private platformId: any,
private eventsFilterService: EventsFilterService
private eventsFilterService: EventsFilterService,
private platform: Platform,
private detectorService: DeviceDetectorService,
private modalService: NgbModal,
) {
// this language will be used as a fallback when a translation isn't found in the current language
translate.setDefaultLang('rm');
Expand Down Expand Up @@ -48,6 +62,10 @@ export class AppComponent implements OnInit {
this.eventsFilterService.setSelectedView('list')
}
});

if (isPlatformBrowser(this.platformId)) {
this.handlePwaNotificationStuff();
}
}

private setupIframeSizeHandling() {
Expand Down Expand Up @@ -75,4 +93,53 @@ export class AppComponent implements OnInit {
};
window.parent.postMessage(message, "*");
}

private handlePwaNotificationStuff(): void {
// do not show the notification on desktop
if (this.detectorService.isDesktop()) {
return;
}

// only show notification the third time the app is opened
if (!this.checkIfThirdTimeAppOpened()) {
return;
}

if (this.platform.ANDROID) {
if (this.platform.FIREFOX) {
this.openPwaWindow('instructions_android_firefox');
} else {
window.addEventListener('beforeinstallprompt', (event: any) => {
event.preventDefault();
this.promptEvent = event;
this.openPwaWindow('direct');
});
}
}

if (this.platform.IOS) {
const isInStandaloneMode = ('standalone' in window.navigator) && (window.navigator['standalone']);
if (!isInStandaloneMode) {
this.openPwaWindow('instructions_ios');
}
}
}

private openPwaWindow(type: 'instructions_ios' | 'instructions_android_firefox' | 'direct') {
const modalRef = this.modalService.open(PwaInstallInstructionsComponent, {size: 'xl', centered: true});
modalRef.componentInstance.type = type;
modalRef.closed.subscribe((value) => {
if (value === 'install') {
this.promptEvent.prompt();
}
});
}

private checkIfThirdTimeAppOpened(): boolean {
let appOpenTimes = +(localStorage.getItem(LOCALSTORAGE_APP_OPEN_TIMES) || 0);
appOpenTimes++;
localStorage.setItem(LOCALSTORAGE_APP_OPEN_TIMES, appOpenTimes + '');
return appOpenTimes == 3;

}
}
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { DatepickerHeaderComponent } from './components/datepicker-header/datepi
import { NoEventsComponent } from './components/events/no-events/no-events.component';
import { ScrollableTitleDirective } from './shared/directives/scrollable-title.directive';
import { FilterScrollPositionDirective } from './shared/directives/filter-scroll-position.directive';
import { PwaInstallInstructionsComponent } from './components/pwa-install-instructions/pwa-install-instructions.component';

export function jwtOptionsFactory(authService: AuthenticationService) {
return {
Expand Down Expand Up @@ -111,6 +112,7 @@ export function inIframe() {
NoEventsComponent,
ScrollableTitleDirective,
FilterScrollPositionDirective,
PwaInstallInstructionsComponent,
],
imports: [
BrowserModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { EventsService } from '../../../shared/services/events.service';
import { EventDto } from '../../../shared/data/event';
import { DeviceDetectorService } from 'ngx-device-detector';
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
import { PlatformService } from '../../../shared/services/platform.service';
import { Platform } from '@angular/cdk/platform';

@Component({
selector: 'app-event-details',
Expand All @@ -20,7 +20,7 @@ export class EventDetailsComponent {
constructor(
private eventsService: EventsService,
private detectorService: DeviceDetectorService,
private platformService: PlatformService
private platform: Platform,
) {
}

Expand All @@ -35,7 +35,7 @@ export class EventDetailsComponent {
return 'https://www.google.com/maps/search/?api=1&query=' + encodeURI(input);
}

if (this.platformService.isIos()) {
if (this.platform.IOS) {
return 'maps://?q=' + encodeURI(input);
}
return 'geo://?q=' + encodeURI(input);
Expand Down
5 changes: 5 additions & 0 deletions src/app/components/events/no-events/no-events.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
padding-top: 150px;
}

.description {
padding: 0.5rem;
}

button {
margin-top: 20px;
margin-right: 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<div class="close-button" (click)="close()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="25" viewBox="0 0 24 25" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M18.7071 5.66789C19.0976 6.05842 19.0976 6.69158 18.7071 7.08211L6.70711 19.0821C6.31658 19.4726 5.68342 19.4726 5.29289 19.0821C4.90237 18.6916 4.90237 18.0584 5.29289 17.6679L17.2929 5.66789C17.6834 5.27737 18.3166 5.27737 18.7071 5.66789Z"
fill="#1C2B33"/>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M5.29289 5.66789C5.68342 5.27737 6.31658 5.27737 6.70711 5.66789L18.7071 17.6679C19.0976 18.0584 19.0976 18.6916 18.7071 19.0821C18.3166 19.4726 17.6834 19.4726 17.2929 19.0821L5.29289 7.08211C4.90237 6.69158 4.90237 6.05842 5.29289 5.66789Z"
fill="#1C2B33"/>
</svg>
</div>

<div class="content">
<div class="head">
<div class="icon">
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="79" viewBox="0 0 78 79" fill="none">
<g filter="url(#filter0_d_2303_9133)">
<g clip-path="url(#clip0_2303_9133)">
<rect x="10" y="10.75" width="58" height="58" fill="white"/>
<path
d="M48.7674 32.5933C48.0122 28.9362 44.4249 26.6032 39.9566 26.6032C34.4184 26.6032 29.1949 30.3864 29.1949 39.5924C29.1949 48.8613 34.3555 52.7076 40.0825 52.7076C45.1172 52.7076 49.5855 49.8071 50.5924 44.8889L59.8438 48.9244C57.0117 58.0042 48.7044 62.8594 39.7049 62.8594C28.7543 62.8594 18.4961 55.5451 18.4961 39.7815C18.4961 23.8919 29.3207 16.6406 40.0825 16.6406C48.7044 16.6406 56.1306 21.1175 58.3333 29.5037L48.7674 32.5933Z"
fill="black"/>
</g>
</g>
<defs>
<filter id="filter0_d_2303_9133" x="0" y="0.75" width="78" height="78" filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_2303_9133"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_2303_9133" result="shape"/>
</filter>
<clipPath id="clip0_2303_9133">
<rect x="10" y="10.75" width="58" height="58" rx="13" fill="white"/>
</clipPath>
</defs>
</svg>
</div>
<div class="slogan">
<div class="title">chalender.ch</div>
<div class="description">memorisar sco app <br>sin il telefonin</div>
</div>
</div>

<div class="instructions" *ngIf="type === 'instructions_ios'">
<div class="step">
<div class="icon">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="25" viewBox="0 0 20 25" fill="none">
<path
d="M9.52832 16.209C9.99023 16.209 10.3877 15.8223 10.3877 15.3711V4.33887L10.3232 2.72754L11.043 3.49023L12.6758 5.23047C12.8262 5.40234 13.041 5.48828 13.2559 5.48828C13.6963 5.48828 14.04 5.16602 14.04 4.72559C14.04 4.5 13.9434 4.32812 13.7822 4.16699L10.1514 0.665039C9.93652 0.450195 9.75391 0.375 9.52832 0.375C9.31348 0.375 9.13086 0.450195 8.90527 0.665039L5.27441 4.16699C5.11328 4.32812 5.02734 4.5 5.02734 4.72559C5.02734 5.16602 5.34961 5.48828 5.80078 5.48828C6.00488 5.48828 6.24121 5.40234 6.3916 5.23047L8.01367 3.49023L8.74414 2.72754L8.67969 4.33887V15.3711C8.67969 15.8223 9.06641 16.209 9.52832 16.209ZM3.37305 24.6201H15.6943C17.9395 24.6201 19.0674 23.5029 19.0674 21.29V10.5693C19.0674 8.35645 17.9395 7.23926 15.6943 7.23926H12.6973V8.96875H15.6621C16.7256 8.96875 17.3379 9.54883 17.3379 10.666V21.1934C17.3379 22.3105 16.7256 22.8906 15.6621 22.8906H3.39453C2.32031 22.8906 1.72949 22.3105 1.72949 21.1934V10.666C1.72949 9.54883 2.32031 8.96875 3.39453 8.96875H6.37012V7.23926H3.37305C1.12793 7.23926 0 8.35645 0 10.5693V21.29C0 23.5029 1.12793 24.6201 3.37305 24.6201Z"
fill="black"/>
</svg>
</div>
<div class="instruction">
Tutga en il navigatur l’icona «parter»
</div>
</div>
<div class="step">
<div class="icon">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="19" viewBox="0 0 18 19" fill="none">
<rect x="0.75" y="1.125" width="16.5" height="16.5" rx="3.25" stroke="black" stroke-width="1.5"/>
<path d="M9 5.375V13.375" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M13 9.375L5 9.375" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
</svg>
</div>
<div class="instruction">
Tscherna en il menu <br>«Zum Home-Bildschirm»
</div>
</div>
</div>

<div class="instructions" *ngIf="type === 'instructions_android_firefox'">
<div class="step">
<div class="icon">

<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="24.000000pt" height="28.000000pt" viewBox="0 0 128.000000 140.000000"
preserveAspectRatio>
<g transform="translate(0.000000,140.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path
d="M702 938 c-15 -15 -15 -61 0 -76 7 -7 24 -12 38 -12 14 0 31 5 38 12 7 7 12 24 12 38 0 14 -5 31 -12 38 -15 15 -61 15 -76 0z"/>
<path d="M694 674 c-10 -40 7 -59 52 -59 39 0 39 0 39 40 0 40 0 40 -42 43 -39 3 -43 1 -49 -24z"/>
<path
d="M702 448 c-15 -15 -16 -60 0 -76 16 -16 63 -15 77 2 7 8 11 30 9 48 -3 30 -6 33 -38 36 -19 2 -41 -3 -48 -10z"/>
</g>
</svg>
</div>
<div class="instruction">
Avra il menu
</div>
</div>
<div class="step">
<div class="icon">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="26.000000pt" height="28.000000pt" viewBox="0 0 132.000000 146.000000"
preserveAspectRatio>
<g transform="translate(0.000000,146.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path
d="M408 1147 c-15 -12 -32 -35 -38 -50 -14 -40 -14 -631 1 -660 6 -12 21 -32 32 -44 20 -22 26 -23 218 -23 l197 0 31 35 c31 35 31 37 31 148 0 116 -6 137 -42 137 -22 0 -27 -22 -30 -130 l-3 -85 -183 -3 -182 -2 2 297 3 298 214 5 c132 3 216 9 218 15 2 6 -11 27 -28 48 l-30 37 -192 0 c-184 0 -193 -1 -219 -23z m280 -729 c-3 -9 -26 -13 -68 -13 -42 0 -65 4 -67 13 -4 9 14 12 67 12 53 0 71 -3 68 -12z"/>
<path
d="M845 965 c-53 -54 -101 -93 -105 -89 -5 5 -10 20 -12 33 -4 33 -31 49 -52 32 -20 -17 -23 -180 -4 -199 16 -16 180 -16 196 0 27 27 0 67 -45 68 -13 0 -23 3 -23 7 0 4 44 52 97 105 83 83 96 101 87 116 -22 34 -43 23 -139 -73z"/>
<path d="M597 683 c-12 -11 -8 -40 7 -52 28 -23 68 17 45 45 -12 15 -41 19 -52 7z"/>
<path d="M483 564 c-8 -22 9 -49 33 -49 30 0 27 59 -4 63 -14 2 -25 -3 -29 -14z"/>
<path d="M593 564 c-8 -20 8 -48 30 -52 22 -5 42 35 26 54 -16 19 -48 18 -56 -2z"/>
<path d="M713 573 c-18 -7 -16 -38 2 -53 23 -19 47 -4 43 27 -3 27 -20 37 -45 26z"/>
</g>
</svg>
</div>
<div class="instruction">
Tscherna «Installar»
</div>
</div>
</div>

<div class="instructions" *ngIf="type === 'direct'">
<button class="clndr accent" (click)="install()">Installar</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
:host {
padding: 2em;
}

.close-button {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
background: transparent;
border: none;
cursor: pointer;
}

.head {
display: flex;

.icon {

}

.slogan {
padding: 8px;
display: flex;
flex-direction: column;
justify-content: space-between;

.title {
font-family: "CadizBlack", sans-serif;
text-transform: uppercase;
font-size: 20px;
font-style: normal;
font-weight: 800;
line-height: normal;
}

.description {
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: normal;
}
}
}

.instructions {
margin: 0 2rem 0 2rem;
display: flex;
flex-direction: column;

.step {
display: flex;
flex-direction: row;
margin-top: 12px;
align-items: center;

.icon {
margin-right: 12px;
}

.instruction {
font-size: 14px;
}
}

button {
margin-right: 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { PwaInstallInstructionsComponent } from './pwa-install-instructions.component';

describe('PwaInstallInstructionsComponent', () => {
let component: PwaInstallInstructionsComponent;
let fixture: ComponentFixture<PwaInstallInstructionsComponent>;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [PwaInstallInstructionsComponent]
});
fixture = TestBed.createComponent(PwaInstallInstructionsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading

0 comments on commit 9dbdbc6

Please sign in to comment.