Skip to content

Commit fc8711c

Browse files
committed
feat($ionicLoading): on android, no back button action while loading
Fixes #1273
1 parent 89a9ed1 commit fc8711c

File tree

4 files changed

+167
-142
lines changed

4 files changed

+167
-142
lines changed

js/angular/service/loading.js

+14-5
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,12 @@ IonicModule
3939
'$q',
4040
'$log',
4141
'$compile',
42-
function($document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $compile) {
42+
'$ionicPlatform',
43+
function($document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $compile, $ionicPlatform) {
4344

4445
var loaderInstance;
45-
//default value
46+
//default values
47+
var deregisterBackAction = angular.noop;
4648
var loadingShowDelay = $q.when();
4749

4850
return {
@@ -79,7 +81,6 @@ function($document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $c
7981
appendTo: $document[0].body
8082
})
8183
.then(function(loader) {
82-
8384
var self = loader;
8485

8586
loader.show = function(options) {
@@ -136,21 +137,28 @@ function($document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $c
136137
$timeout.cancel(this.durationTimeout);
137138
this.isShown = false;
138139
};
140+
139141
return loader;
140142
});
141143
}
142-
return $q.when(loaderInstance);
144+
return loaderInstance;
143145
}
144146

145147
function showLoader(options) {
146148
options || (options = {});
147149
var delay = options.delay || options.showDelay || 0;
148150

149151
//If loading.show() was called previously, cancel it and show with our new options
150-
$timeout.cancel(loadingShowDelay);
152+
loadingShowDelay && $timeout.cancel(loadingShowDelay);
151153
loadingShowDelay = $timeout(angular.noop, delay);
152154

153155
loadingShowDelay.then(getLoader).then(function(loader) {
156+
deregisterBackAction();
157+
//Disable hardware back button while loading
158+
deregisterBackAction = $ionicPlatform.registerBackButtonAction(
159+
angular.noop,
160+
PLATFORM_BACK_BUTTON_PRIORITY_LOADING
161+
);
154162
return loader.show(options);
155163
});
156164

@@ -168,6 +176,7 @@ function($document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $c
168176
}
169177

170178
function hideLoader() {
179+
deregisterBackAction();
171180
$timeout.cancel(loadingShowDelay);
172181
getLoader().then(function(loader) {
173182
loader.hide();

js/angular/service/platform.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
var PLATFORM_BACK_BUTTON_PRIORITY_VIEW = 100;
22
var PLATFORM_BACK_BUTTON_PRIORITY_ACTION_SHEET = 300;
3-
var PLATFORM_BACK_BUTTON_PRIORITY_POPUP = 500;
3+
var PLATFORM_BACK_BUTTON_PRIORITY_POPUP = 400;
4+
var PLATFORM_BACK_BUTTON_PRIORITY_LOADING = 500;
45
/**
56
* @ngdoc service
67
* @name $ionicPlatform

test/html/loading.html

+4-7
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,22 @@
66
<!-- Sets initial viewport load and disables zooming -->
77
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
88

9-
<link rel="stylesheet" href="../../../../dist/css/ionic.css">
9+
<link rel="stylesheet" href="../../dist/css/ionic.css">
1010

1111
</head>
1212
<body ng-controller="LoadingCtrl">
1313
<button class="button button-dark" ng-click="startLoading()">Load</button>
14-
<script src="../../../../dist/js/ionic.bundle.js"></script>
14+
<script src="../../dist/js/ionic.bundle.js"></script>
1515
<script>
1616
angular.module('ionic.example', ['ionic'])
1717

1818
.controller('LoadingCtrl', function($scope, $ionicLoading) {
1919
$scope.startLoading = function() {
2020
$ionicLoading.show({
2121
template: 'Getting current location...',
22-
delay: 100
22+
delay: 100,
23+
duration: 500
2324
});
24-
$ionicLoading.show({
25-
template: 'Getting current location...',
26-
});
27-
$ionicLoading.hide();
2825
};
2926
});
3027
</script>

test/unit/angular/service/loading.unit.js

+147-129
Original file line numberDiff line numberDiff line change
@@ -6,151 +6,169 @@ describe('$ionicLoading service', function() {
66
expect(loader).toBe(loader2);
77
}));
88

9-
describe('loader instance', function() {
9+
describe('.show()', function() {
1010

11-
describe('.show()', function() {
12-
13-
it('should retain backdrop if !noBackdrop and !isShown', inject(function($ionicLoading, $ionicBackdrop) {
14-
spyOn($ionicBackdrop, 'retain');
15-
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
16-
loader.show({});
17-
expect($ionicBackdrop.retain).toHaveBeenCalled();
18-
}));
19-
it('should not retain backdrop if noBackdrop', inject(function($ionicLoading, $ionicBackdrop) {
20-
spyOn($ionicBackdrop, 'retain');
21-
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
22-
loader.show({ noBackdrop: true });
23-
expect($ionicBackdrop.retain).not.toHaveBeenCalled();
24-
}));
25-
it('should not retain backdrop if isShown', inject(function($ionicLoading, $ionicBackdrop) {
26-
spyOn($ionicBackdrop, 'retain');
27-
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
28-
loader.isShown = true;
29-
loader.show({});
30-
expect($ionicBackdrop.retain).not.toHaveBeenCalled();
31-
}));
32-
33-
it('should not timeout if no duration', inject(function($ionicLoading, $timeout) {
34-
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
35-
loader.show({});
36-
expect(loader.durationTimeout).toBeFalsy();
37-
}));
38-
it('should timeout if duration', inject(function($ionicLoading, $timeout) {
39-
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
40-
loader.show({ duration: 1000 });
41-
expect(loader.durationTimeout).toBeTruthy();
42-
expect(loader.durationTimeout.$$timeoutId).toBeTruthy();
43-
}));
44-
it('should add active', inject(function($ionicLoading, $timeout) {
45-
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
46-
ionic.requestAnimationFrame = function(cb) { cb(); };
47-
expect(loader.element.hasClass('active')).toBe(false);
48-
loader.show({});
49-
$timeout.flush();
50-
expect(loader.element.hasClass('active')).toBe(true);
51-
}));
52-
it('should isShown = true', inject(function($ionicLoading) {
53-
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
54-
expect(loader.isShown).toBeFalsy();
55-
loader.show({});
56-
expect(loader.isShown).toBe(true);
57-
}));
58-
59-
it('should use options.template', inject(function($ionicLoading, $rootScope) {
60-
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
61-
loader.show({ template: 'foo {{"bar"}}' });
62-
$rootScope.$apply();
63-
expect(loader.element.text()).toBe('foo bar');
64-
}));
65-
66-
it('should use options.templateUrl', inject(function($ionicLoading, $rootScope, $ionicTemplateLoader, $q) {
67-
spyOn($ionicTemplateLoader, 'load').andReturn($q.when('{{1}} content'));
68-
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
69-
loader.show({ templateUrl: 'template.html' });
70-
expect($ionicTemplateLoader.load).toHaveBeenCalledWith('template.html');
71-
$rootScope.$apply();
72-
expect(loader.element.text()).toBe('1 content');
73-
}));
74-
75-
});
76-
77-
describe('.hide()', function() {
78-
79-
it('should release backdrop if hasBackdrop and isShown', inject(function($ionicLoading, $ionicBackdrop) {
80-
spyOn($ionicBackdrop, 'release');
81-
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
82-
loader.isShown = true;
83-
loader.hasBackdrop = true;
84-
loader.hide();
85-
expect($ionicBackdrop.release).toHaveBeenCalled();
86-
}));
87-
it('should not release backdrop if !hasBackdrop', inject(function($ionicLoading, $ionicBackdrop) {
88-
spyOn($ionicBackdrop, 'release');
89-
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
90-
loader.isShown = true;
91-
loader.hide();
92-
expect($ionicBackdrop.release).not.toHaveBeenCalled();
93-
}));
94-
it('should cancel durationTimeout and set isShown to false', inject(function($ionicLoading, $timeout) {
95-
spyOn($timeout, 'cancel');
96-
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
97-
loader.durationTimeout = {};
98-
loader.isShown = true;
99-
loader.hide({});
100-
expect($timeout.cancel).toHaveBeenCalledWith(loader.durationTimeout);
101-
expect(loader.isShown).toBe(false);
102-
}));
103-
104-
});
11+
it('should retain backdrop if !noBackdrop and !isShown', inject(function($ionicLoading, $ionicBackdrop) {
12+
spyOn($ionicBackdrop, 'retain');
13+
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
14+
loader.show({});
15+
expect($ionicBackdrop.retain).toHaveBeenCalled();
16+
}));
17+
it('should not retain backdrop if noBackdrop', inject(function($ionicLoading, $ionicBackdrop) {
18+
spyOn($ionicBackdrop, 'retain');
19+
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
20+
loader.show({ noBackdrop: true });
21+
expect($ionicBackdrop.retain).not.toHaveBeenCalled();
22+
}));
23+
it('should not retain backdrop if isShown', inject(function($ionicLoading, $ionicBackdrop) {
24+
spyOn($ionicBackdrop, 'retain');
25+
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
26+
loader.isShown = true;
27+
loader.show({});
28+
expect($ionicBackdrop.retain).not.toHaveBeenCalled();
29+
}));
10530

106-
it('should show with options', inject(function($ionicLoading, $timeout) {
31+
it('should not timeout if no duration', inject(function($ionicLoading, $timeout) {
10732
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
108-
spyOn(loader, 'show');
109-
var options = {};
110-
$ionicLoading.show(options);
33+
loader.show({});
34+
expect(loader.durationTimeout).toBeFalsy();
35+
}));
36+
it('should timeout if duration', inject(function($ionicLoading, $timeout) {
37+
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
38+
loader.show({ duration: 1000 });
39+
expect(loader.durationTimeout).toBeTruthy();
40+
expect(loader.durationTimeout.$$timeoutId).toBeTruthy();
41+
}));
42+
it('should add active', inject(function($ionicLoading, $timeout) {
43+
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
44+
ionic.requestAnimationFrame = function(cb) { cb(); };
45+
expect(loader.element.hasClass('active')).toBe(false);
46+
loader.show({});
11147
$timeout.flush();
112-
expect(loader.show).toHaveBeenCalledWith(options);
48+
expect(loader.element.hasClass('active')).toBe(true);
49+
}));
50+
it('should isShown = true', inject(function($ionicLoading) {
51+
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
52+
expect(loader.isShown).toBeFalsy();
53+
loader.show({});
54+
expect(loader.isShown).toBe(true);
11355
}));
11456

115-
it('should $timeout.cancel & hide', inject(function($ionicLoading, $rootScope, $timeout) {
57+
it('should use options.template', inject(function($ionicLoading, $rootScope) {
11658
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
117-
spyOn($timeout, 'cancel');
118-
spyOn(loader, 'hide');
119-
$ionicLoading.hide();
120-
expect($timeout.cancel).toHaveBeenCalled();
59+
loader.show({ template: 'foo {{"bar"}}' });
12160
$rootScope.$apply();
122-
expect(loader.hide).toHaveBeenCalled();
61+
expect(loader.element.text()).toBe('foo bar');
12362
}));
12463

125-
it('hide should cancel show delay and just go ahead and hide', inject(function($ionicLoading, $timeout) {
126-
ionic.requestAnimationFrame = function(cb) { cb(); };
64+
it('should use options.templateUrl', inject(function($ionicLoading, $rootScope, $ionicTemplateLoader, $q) {
65+
spyOn($ionicTemplateLoader, 'load').andReturn($q.when('{{1}} content'));
12766
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
128-
spyOn(loader, 'hide').andCallThrough();
129-
spyOn(loader, 'show').andCallThrough();
130-
$ionicLoading.show({ delay: 1000 });
131-
$ionicLoading.hide();
132-
expect(loader.show).not.toHaveBeenCalled();
133-
expect(loader.hide).not.toHaveBeenCalled();
134-
$timeout.flush();
135-
expect(loader.show).not.toHaveBeenCalled();
136-
expect(loader.hide).toHaveBeenCalled();
137-
expect(loader.isShown).toBe(false);
138-
expect(loader.element.hasClass('active')).toBe(false);
67+
loader.show({ templateUrl: 'template.html' });
68+
expect($ionicTemplateLoader.load).toHaveBeenCalledWith('template.html');
69+
$rootScope.$apply();
70+
expect(loader.element.text()).toBe('1 content');
13971
}));
140-
it('show should only active after raf is still isShown', inject(function($ionicLoading) {
72+
73+
});
74+
75+
describe('.hide()', function() {
76+
77+
it('should release backdrop if hasBackdrop and isShown', inject(function($ionicLoading, $ionicBackdrop) {
78+
spyOn($ionicBackdrop, 'release');
14179
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
142-
var rafCallback;
143-
ionic.requestAnimationFrame = function(cb) {
144-
rafCallback = cb;
145-
};
146-
loader.show({});
147-
expect(loader.isShown).toBe(true);
80+
loader.isShown = true;
81+
loader.hasBackdrop = true;
14882
loader.hide();
83+
expect($ionicBackdrop.release).toHaveBeenCalled();
84+
}));
85+
it('should not release backdrop if !hasBackdrop', inject(function($ionicLoading, $ionicBackdrop) {
86+
spyOn($ionicBackdrop, 'release');
87+
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
88+
loader.isShown = true;
89+
loader.hide();
90+
expect($ionicBackdrop.release).not.toHaveBeenCalled();
91+
}));
92+
it('should cancel durationTimeout and set isShown to false', inject(function($ionicLoading, $timeout) {
93+
spyOn($timeout, 'cancel');
94+
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
95+
loader.durationTimeout = {};
96+
loader.isShown = true;
97+
loader.hide({});
98+
expect($timeout.cancel).toHaveBeenCalledWith(loader.durationTimeout);
14999
expect(loader.isShown).toBe(false);
150-
rafCallback();
151-
expect(loader.element.hasClass('active')).toBe(false);
152-
ionic.requestAnimationFrame = function(cb) { cb(); };
153100
}));
154101

155102
});
103+
104+
it('should show with options', inject(function($ionicLoading, $timeout) {
105+
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
106+
spyOn(loader, 'show');
107+
var options = {};
108+
$ionicLoading.show(options);
109+
$timeout.flush();
110+
expect(loader.show).toHaveBeenCalledWith(options);
111+
}));
112+
113+
it('should $timeout.cancel & hide', inject(function($ionicLoading, $rootScope, $timeout) {
114+
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
115+
spyOn($timeout, 'cancel');
116+
spyOn(loader, 'hide');
117+
$ionicLoading.hide();
118+
expect($timeout.cancel).toHaveBeenCalled();
119+
$rootScope.$apply();
120+
expect(loader.hide).toHaveBeenCalled();
121+
}));
122+
123+
it('hide should cancel show delay and just go ahead and hide', inject(function($ionicLoading, $timeout) {
124+
ionic.requestAnimationFrame = function(cb) { cb(); };
125+
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
126+
spyOn(loader, 'hide').andCallThrough();
127+
spyOn(loader, 'show').andCallThrough();
128+
$ionicLoading.show({ delay: 1000 });
129+
$ionicLoading.hide();
130+
expect(loader.show).not.toHaveBeenCalled();
131+
expect(loader.hide).not.toHaveBeenCalled();
132+
$timeout.flush();
133+
expect(loader.show).not.toHaveBeenCalled();
134+
expect(loader.hide).toHaveBeenCalled();
135+
expect(loader.isShown).toBe(false);
136+
expect(loader.element.hasClass('active')).toBe(false);
137+
}));
138+
it('show should only active after raf is still isShown', inject(function($ionicLoading) {
139+
var loader = TestUtil.unwrapPromise($ionicLoading._getLoader());
140+
var rafCallback;
141+
ionic.requestAnimationFrame = function(cb) {
142+
rafCallback = cb;
143+
};
144+
loader.show({});
145+
expect(loader.isShown).toBe(true);
146+
loader.hide();
147+
expect(loader.isShown).toBe(false);
148+
rafCallback();
149+
expect(loader.element.hasClass('active')).toBe(false);
150+
ionic.requestAnimationFrame = function(cb) { cb(); };
151+
}));
152+
153+
describe("back button", function() {
154+
it('.show() should register back button action', inject(function($ionicLoading, $ionicPlatform, $timeout) {
155+
spyOn($ionicPlatform, 'registerBackButtonAction');
156+
$ionicLoading.show();
157+
$timeout.flush();
158+
expect($ionicPlatform.registerBackButtonAction).toHaveBeenCalledWith(
159+
angular.noop,
160+
PLATFORM_BACK_BUTTON_PRIORITY_LOADING
161+
);
162+
}));
163+
it('.hide() should deregister back button action', inject(function($ionicLoading, $ionicPlatform, $timeout) {
164+
var deregisterSpy = jasmine.createSpy('deregister');
165+
spyOn($ionicPlatform, 'registerBackButtonAction').andReturn(deregisterSpy);
166+
$ionicLoading.show();
167+
$timeout.flush();
168+
expect(deregisterSpy).not.toHaveBeenCalled();
169+
$ionicLoading.hide();
170+
expect(deregisterSpy).toHaveBeenCalled();
171+
}));
172+
});
173+
156174
});

0 commit comments

Comments
 (0)