Skip to content
This repository was archived by the owner on Nov 30, 2022. It is now read-only.

Commit afcfc88

Browse files
feat(back-button): add example page and modal to test the back button functionality
* works with android back button * can currently be used with url, Ionic Modal and FivGallery
1 parent e1dec01 commit afcfc88

18 files changed

Lines changed: 257 additions & 87 deletions
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import { NgModule } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import { FivBackButton } from './back-button.component';
4-
import { FivRoutingStateService } from './routing-state.service';
54
import { FivIconModule } from '../icon/icon.module';
65
import { IonicModule } from '@ionic/angular';
76

87
@NgModule({
98
declarations: [FivBackButton],
109
imports: [CommonModule, FivIconModule, IonicModule],
1110
exports: [FivBackButton],
12-
providers: [FivRoutingStateService]
11+
providers: []
1312
})
1413
export class FivBackButtonModule {}

projects/core/src/lib/back-button/routing-state.service.ts

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { Injectable } from '@angular/core';
22
import { Router, NavigationEnd } from '@angular/router';
3-
import { filter } from 'rxjs/operators';
3+
import { filter, tap, takeWhile } from 'rxjs/operators';
44
import { NavController, Platform } from '@ionic/angular';
5+
import { Navigateable } from '../interfaces';
6+
import { fromEvent, zip, race, from } from 'rxjs';
57

68
export interface RoutingStateConfig {
79
clearOn: string[];
@@ -12,7 +14,7 @@ export interface RoutingStateConfig {
1214
providedIn: 'root'
1315
})
1416
export class FivRoutingStateService {
15-
private history: string[] = [];
17+
private history: (string | Navigateable)[] = [];
1618
private config: RoutingStateConfig;
1719

1820
constructor(
@@ -32,7 +34,7 @@ export class FivRoutingStateService {
3234
this.pop();
3335
}
3436
// add url to history
35-
this.history = [...this.history, urlAfterRedirects];
37+
this.history.push(urlAfterRedirects);
3638
if (this.config && this.config.clearOn) {
3739
const clear = this.config.clearOn.some(s => s === urlAfterRedirects);
3840
if (clear) {
@@ -42,27 +44,40 @@ export class FivRoutingStateService {
4244
});
4345
}
4446

47+
registerNavigateable(target: Navigateable) {
48+
if (isNavigateable(target)) {
49+
this.history.push(target);
50+
}
51+
}
52+
4553
handleAndroidBackButton() {
46-
this.platform.backButton.subscribe(() => {
47-
if (this.getHistory().length <= 1) {
48-
// close the app because we are at root level
49-
navigator['app'].exitApp();
50-
} else {
51-
// go back
54+
this.platform.backButton
55+
.pipe(filter(() => !isNavigateable(this.getCurrentUrl())))
56+
.subscribe(event => {
5257
this.goBack();
53-
}
54-
});
58+
});
59+
60+
this.platform.backButton
61+
.pipe(filter(() => isNavigateable(this.getCurrentUrl())))
62+
.subscribe(event => {
63+
event.register(99999, () => {
64+
this.goBack('/');
65+
});
66+
});
5567
}
5668

57-
public getHistory(): string[] {
69+
public getHistory(): (string | Navigateable)[] {
5870
return this.history;
5971
}
6072

61-
public getPreviousUrl(defaultHref = '/'): string {
62-
return this.history[this.history.length - 2] || defaultHref;
73+
public getPreviousUrl(defaultHref = '/'): string | Navigateable {
74+
if (this.history.length > 2) {
75+
return this.history[this.history.length - 2];
76+
}
77+
return defaultHref;
6378
}
6479

65-
public pop(): string {
80+
public pop(): string | Navigateable {
6681
return this.history.pop();
6782
}
6883

@@ -84,11 +99,40 @@ export class FivRoutingStateService {
8499
}
85100
}
86101

87-
public getCurrentUrl(): string {
102+
public getCurrentUrl(): string | Navigateable {
88103
return this.history[this.history.length - 1];
89104
}
90105

91106
goBack(defaultHref = '/') {
92-
this.navCtrl.navigateBack(this.getPreviousUrl(defaultHref));
107+
if (this.getHistory().length <= 1) {
108+
// close the app because we are at root level
109+
return navigator['app'].exitApp();
110+
}
111+
const current = this.getCurrentUrl();
112+
if (typeof current !== 'string' && isNavigateable(current)) {
113+
current.dismiss();
114+
return this.pop();
115+
}
116+
117+
const previous = this.getPreviousUrl(defaultHref);
118+
if (typeof previous === 'string') {
119+
return this.navCtrl.navigateBack(previous);
120+
}
121+
if (isNavigateable(previous)) {
122+
return this.navCtrl.navigateBack(this.getLatestUrl(defaultHref));
123+
}
124+
}
125+
126+
getLatestUrl(defaultHref: string): string {
127+
const urls = this.history.filter(e => !!(typeof e === 'string'));
128+
const latest = urls[urls.length - 1];
129+
if (urls.length > 0 && latest && typeof latest === 'string') {
130+
return latest;
131+
}
132+
return defaultHref;
93133
}
94134
}
135+
136+
export function isNavigateable(object: any): boolean {
137+
return !!object && object.dismiss !== undefined;
138+
}

projects/core/src/lib/gallery/gallery.component.scss

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
:host {
2-
}
31

42
.viewer {
53
width: 100vw;

src/app/app-routing.module.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ const routes: Routes = [
8787
loadChildren:
8888
'./pages/developer-tools/developer-tools.module#DeveloperToolsPageModule'
8989
},
90-
{ path: 'test', loadChildren: './test/test.module#TestPageModule' },
90+
{
91+
path: 'backbutton',
92+
loadChildren: './pages/backbutton/backbutton.module#BackButtonPageModule'
93+
},
9194
{
9295
path: '**',
9396
loadChildren:

src/app/app.component.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ export class AppComponent {
1616
url: '/app-bar',
1717
icon: 'git-commit'
1818
},
19+
{
20+
title: 'Back Button',
21+
url: '/backbutton',
22+
icon: 'md-arrow-back'
23+
},
1924
{
2025
title: 'Bottom Sheet',
2126
url: '/bottom-sheet',
Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
1-
import { FivBackButtonModule } from './../../../projects/core/src/lib/back-button/back-button.module';
2-
import { FivIconModule } from './../../../projects/core/src/lib/icon/icon.module';
31
import { ExampleComponent } from './example/example.component';
42
import { NgModule } from '@angular/core';
53
import { CommonModule } from '@angular/common';
64
import { IonicModule } from '@ionic/angular';
75
import { MarkdownModule } from 'ngx-markdown';
8-
import { FivethreeCoreModule } from '@fivethree/core';
6+
import {
7+
FivethreeCoreModule,
8+
FivIconModule,
9+
FivBackButtonModule,
10+
FivGalleryModule
11+
} from '@fivethree/core';
912
import { MatTooltipModule, MatTabsModule } from '@angular/material';
1013
import { HeaderComponent } from './header/header.component';
1114
import { FooterComponent } from './footer/footer.component';
1215
import { LottieAnimationViewModule } from 'ng-lottie';
1316
import { RouterModule } from '@angular/router';
1417
import { FormsModule } from '@angular/forms';
1518
import { DocsFooterComponent } from './docs-footer/docs-footer.component';
19+
import { ModalPage } from './modal/modal.page';
1620

1721
const components = [
1822
ExampleComponent,
1923
HeaderComponent,
2024
FooterComponent,
21-
DocsFooterComponent
25+
DocsFooterComponent,
26+
ModalPage
2227
];
2328
@NgModule({
2429
declarations: components,
@@ -32,8 +37,10 @@ const components = [
3237
MatTabsModule,
3338
LottieAnimationViewModule,
3439
FivIconModule,
35-
FivBackButtonModule
40+
FivBackButtonModule,
41+
FivGalleryModule
3642
],
37-
exports: [...components, LottieAnimationViewModule, MarkdownModule]
43+
exports: [...components, LottieAnimationViewModule, MarkdownModule],
44+
entryComponents: [ModalPage]
3845
})
3946
export class ComponentsModule {}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<ion-header>
2+
<ion-toolbar>
3+
<ion-buttons slot="start">
4+
<fiv-back-button [icon]="'ios-arrow-down'"></fiv-back-button>
5+
</ion-buttons>
6+
<ion-title>modal</ion-title>
7+
</ion-toolbar>
8+
</ion-header>
9+
10+
<ion-content>
11+
12+
<ion-button (click)="presentModal()">
13+
ADD MODAL
14+
</ion-button>
15+
16+
<fiv-gallery #gallery (willClose)="unregister()" (willOpen)="register(gallery)">
17+
<fiv-gallery-toolbar position="top">
18+
<ion-toolbar color="transparent">
19+
<ion-buttons slot="start">
20+
<fiv-back-button icon="close"></fiv-back-button>
21+
</ion-buttons>
22+
</ion-toolbar>
23+
</fiv-gallery-toolbar>
24+
<ion-grid fixed>
25+
<ion-row>
26+
<ion-col *ngFor="let pic of picsum" size="6" sizeMd="4">
27+
<fiv-gallery-image [src]="pic"></fiv-gallery-image>
28+
</ion-col>
29+
</ion-row>
30+
</ion-grid>
31+
</fiv-gallery>
32+
33+
</ion-content>

src/app/test/test.page.spec.ts renamed to src/app/components/modal/modal.page.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
22
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
33

4-
import { TestPage } from './test.page';
4+
import { ModalPage } from './modal.page';
55

6-
describe('TestPage', () => {
7-
let component: TestPage;
8-
let fixture: ComponentFixture<TestPage>;
6+
describe('ModalPage', () => {
7+
let component: ModalPage;
8+
let fixture: ComponentFixture<ModalPage>;
99

1010
beforeEach(async(() => {
1111
TestBed.configureTestingModule({
12-
declarations: [ TestPage ],
12+
declarations: [ ModalPage ],
1313
schemas: [CUSTOM_ELEMENTS_SCHEMA],
1414
})
1515
.compileComponents();
1616
}));
1717

1818
beforeEach(() => {
19-
fixture = TestBed.createComponent(TestPage);
19+
fixture = TestBed.createComponent(ModalPage);
2020
component = fixture.componentInstance;
2121
fixture.detectChanges();
2222
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Component, OnInit } from '@angular/core';
2+
import { ModalController, NavController } from '@ionic/angular';
3+
import { FivRoutingStateService, FivGallery } from '@fivethree/core';
4+
5+
@Component({
6+
selector: 'app-modal',
7+
templateUrl: './modal.page.html',
8+
styleUrls: ['./modal.page.scss']
9+
})
10+
export class ModalPage implements OnInit {
11+
picsum: string[] = Array.from(
12+
new Array(10),
13+
(x, i) => `https://picsum.photos/500/?${i}`
14+
);
15+
16+
constructor(
17+
public modalController: ModalController,
18+
private nav: NavController,
19+
private routing: FivRoutingStateService
20+
) {}
21+
22+
ngOnInit() {}
23+
24+
async presentModal() {
25+
const m = await this.modalController.create({
26+
component: ModalPage,
27+
componentProps: { value: 123 }
28+
});
29+
30+
this.routing.registerNavigateable(m);
31+
32+
m.present();
33+
}
34+
35+
navigate(url: string) {
36+
this.nav.navigateForward(url);
37+
}
38+
39+
register(gallery: FivGallery) {
40+
this.routing.registerNavigateable(gallery);
41+
}
42+
43+
unregister() {
44+
this.routing.pop();
45+
}
46+
}

0 commit comments

Comments
 (0)