diff --git a/ionic/animations/animation.ts b/ionic/animations/animation.ts
index 88e7a1b32c..a3f90d465d 100644
--- a/ionic/animations/animation.ts
+++ b/ionic/animations/animation.ts
@@ -392,6 +392,7 @@ export class Animation {
}
progress(value) {
+ value = Math.min(1, Math.max(0, value));
this.isProgress = true;
let i;
@@ -589,7 +590,6 @@ class Animate {
if (animation) {
// passed a number between 0 and 1
- value = Math.max(0, Math.min(1, value));
if (animation.playState !== 'paused') {
animation.pause();
diff --git a/ionic/components/app/activator.ts b/ionic/components/app/activator.ts
index 5687b3973b..ae76edd35c 100644
--- a/ionic/components/app/activator.ts
+++ b/ionic/components/app/activator.ts
@@ -84,7 +84,7 @@ export class Activator {
touchEnd(ev) {
let self = this;
- if (self.tapPolyfill && self.start && !self.app.isTransitioning()) {
+ if (self.tapPolyfill && self.start && self.app.isEnabled()) {
let endCoord = pointerCoord(ev);
if (!hasPointerMoved(self.pointerTolerance, self.start, endCoord)) {
@@ -140,7 +140,7 @@ export class Activator {
pointerStart(ev) {
let targetEle = this.getActivatableTarget(ev.target);
- if (targetEle && !this.app.isTransitioning()) {
+ if (targetEle && this.app.isEnabled()) {
this.start = pointerCoord(ev);
this.queueActivate(targetEle);
@@ -179,7 +179,7 @@ export class Activator {
* @return {boolean} True if click event should be allowed, otherwise false.
*/
allowClick(ev) {
- if (this.app.isTransitioning()) {
+ if (!this.app.isEnabled()) {
return false;
}
if (!ev.isIonicTap) {
@@ -257,11 +257,10 @@ export class Activator {
deactivate() {
const self = this;
- if (self.app.isTransitioning() && self.deactivateAttempt < 30) {
- // the app is actively transitioning, don't bother deactivating
- // anything this makes it easier on the GPU so it doesn't
- // have to redraw any buttons during a transition
- // retry
+ if (!self.app.isEnabled() && self.deactivateAttempt < 30) {
+ // the app is actively disabled, so don't bother deactivating anything.
+ // this makes it easier on the GPU so it doesn't have to redraw any
+ // buttons during a transition. This will retry in XX milliseconds.
++self.deactivateAttempt;
self.queueDeactivate();
diff --git a/ionic/components/app/app.ts b/ionic/components/app/app.ts
index be3847c64d..87a8f6c12f 100644
--- a/ionic/components/app/app.ts
+++ b/ionic/components/app/app.ts
@@ -3,6 +3,7 @@ import {ROUTER_BINDINGS, HashLocationStrategy, LocationStrategy, Router} from 'a
import {IonicConfig} from '../../config/config';
import {IonicPlatform, Platform} from '../../platform/platform';
+import {ClickBlock} from '../../util/click-block';
import * as util from '../../util/util';
// injectables
@@ -40,7 +41,7 @@ export class IonicApp {
*/
constructor() {
this.overlays = [];
- this._transDone = 0;
+ this._enableTime = 0;
// Our component registry map
this.components = {};
@@ -77,21 +78,27 @@ export class IonicApp {
}
/**
- * Sets if the app is currently transitioning or not. For example
- * this is set to `true` while views transition, a modal slides up, an action-menu
- * slides up, etc. After the transition completes it is set back to `false`.
- * @param {bool} isTransitioning
+ * Sets if the app is currently enabled or not, meaning if it's
+ * available to accept new user commands. For example, this is set to `false`
+ * while views transition, a modal slides up, an action-menu
+ * slides up, etc. After the transition completes it is set back to `true`.
+ * @param {bool} isEnabled
+ * @param {bool} fallback When `isEnabled` is set to `false`, this argument
+ * is used to set the maximum number of milliseconds that app will wait until
+ * it will automatically enable the app again. It's basically a fallback incase
+ * something goes wrong during a transition and the app wasn't re-enabled correctly.
*/
- setTransitioning(isTransitioning, msTilDone=800) {
- this._transDone = (isTransitioning ? Date.now() + msTilDone : 0);
+ setEnabled(isEnabled, fallback=700) {
+ this._enableTime = (isEnabled ? 0 : Date.now() + fallback);
+ ClickBlock(!isEnabled, fallback + 100);
}
/**
* Boolean if the app is actively transitioning or not.
* @return {bool}
*/
- isTransitioning() {
- return (this._transDone > Date.now());
+ isEnabled() {
+ return (this._enableTime < Date.now());
}
/**
diff --git a/ionic/components/app/structure.scss b/ionic/components/app/structure.scss
index d2e5a6888e..f0420772a3 100644
--- a/ionic/components/app/structure.scss
+++ b/ionic/components/app/structure.scss
@@ -13,7 +13,6 @@ $z-index-navbar-container: 10 !default;
$z-index-content: 5 !default;
$z-index-toolbar: 10 !default;
-$z-index-swipe-handle: 15 !default;
$z-index-toolbar-border: 20 !default;
$z-index-list-border: 50 !default;
diff --git a/ionic/components/menu/menu.ts b/ionic/components/menu/menu.ts
index 296e74a8e8..e15fb636fa 100644
--- a/ionic/components/menu/menu.ts
+++ b/ionic/components/menu/menu.ts
@@ -129,6 +129,7 @@ export class Menu extends Ion {
setProgess(value) {
// user actively dragging the menu
this._disable();
+ this.app.setEnabled(false);
this._type.setProgess(value);
}
@@ -147,7 +148,7 @@ export class Menu extends Ion {
this.getBackdropElement().classList.add('show-backdrop');
this._disable();
- this.app.setTransitioning(true);
+ this.app.setEnabled(false);
}
_after(isOpen) {
@@ -165,7 +166,7 @@ export class Menu extends Ion {
this.getBackdropElement().classList.remove('show-backdrop');
}
- this.app.setTransitioning(false);
+ this.app.setEnabled(true);
}
_disable() {
@@ -232,6 +233,7 @@ export class Menu extends Ion {
onDestroy() {
this.app.unregister(this.id);
+ this._gesture && this._gesture.destroy();
this._type && this._type.onDestroy();
this.contentElement = null;
}
diff --git a/ionic/components/nav/pane.ts b/ionic/components/nav/pane.ts
index 3f7990c81d..6181fedc2b 100644
--- a/ionic/components/nav/pane.ts
+++ b/ionic/components/nav/pane.ts
@@ -3,7 +3,6 @@ import {Component, Directive, View, ElementRef, Inject, forwardRef, Injector, bi
import {Ion} from '../ion';
import {IonicConfig} from '../../config/config';
import {ViewController} from '../view/view-controller';
-import {SwipeHandle} from './swipe-handle';
import {IonicComponent} from '../../config/annotations';
import {PaneAnchor, PaneContentAnchor, NavBarContainer} from './anchors';
@@ -116,10 +115,9 @@ export class PaneController {
`,
- directives: [PaneAnchor, PaneContentAnchor, SwipeHandle]
+ directives: [PaneAnchor, PaneContentAnchor]
})
export class Pane extends Ion {
constructor(
diff --git a/ionic/components/nav/swipe-handle.ts b/ionic/components/nav/swipe-handle.ts
deleted file mode 100644
index d617072450..0000000000
--- a/ionic/components/nav/swipe-handle.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-import {ElementRef, Directive, Host, Optional, Inject, forwardRef, NgZone} from 'angular2/angular2';
-
-import {ViewController} from '../view/view-controller';
-import {Pane} from './pane';
-import {Gesture} from 'ionic/gestures/gesture';
-
-
-/**
- * TODO
- */
-@Directive({
- selector: '.swipe-handle',
- host: {
- '[class.show-handle]': 'showHandle'
- }
-})
-export class SwipeHandle {
- /**
- * TODO
- * @param {ViewController=} viewCtrl TODO
- * @param {Pane} pane TODO
- * @param {ElementRef} elementRef TODO
- * @param {NgZone} ngZone TODO
- */
- constructor(
- @Optional() @Inject(forwardRef(() => ViewController)) viewCtrl: ViewController,
- @Host() @Inject(forwardRef(() => Pane)) pane: Pane,
- elementRef: ElementRef,
- ngZone: NgZone
- ) {
-
- if (!viewCtrl || !viewCtrl.isSwipeBackEnabled() || !pane) return;
-
- const self = this;
-
- self.pane = pane;
- self.viewCtrl = viewCtrl;
- self.zone = ngZone;
-
- this.zone.runOutsideAngular(() => {
- let gesture = self.gesture = new Gesture(elementRef.nativeElement);
- gesture.listen();
-
- function dragHorizontal(ev) {
- self.onDragHorizontal(ev);
- }
-
- gesture.on('panend', gesture => { self.onDragEnd(gesture); });
- gesture.on('panleft', dragHorizontal);
- gesture.on('panright', dragHorizontal);
- });
-
- self.startX = null;
- self.width = null;
- }
-
- onDragEnd(gesture) {
- gesture.srcEvent.preventDefault();
- gesture.srcEvent.stopPropagation();
-
- // TODO: POLISH THESE NUMBERS WITH GOOD MATHIFICATION
- let progress = (gesture.center.x - this.startX) / this.width;
- let completeSwipeBack = (progress > 0.5);
- let playbackRate = 4;
-
- if (completeSwipeBack) {
- // complete swipe back
- if (progress > 0.9) {
- playbackRate = 1;
- } else if (progress > 0.8) {
- playbackRate = 2;
- } else if (progress > 0.7) {
- playbackRate = 3;
- }
-
- } else {
- // cancel swipe back
- if (progress < 0.1) {
- playbackRate = 1;
- } else if (progress < 0.2) {
- playbackRate = 2;
- } else if (progress < 0.3) {
- playbackRate = 3;
- }
- }
-
- this.zone.run(() => {
- this.viewCtrl.swipeBackFinish(completeSwipeBack, playbackRate);
- });
-
- this.startX = null;
- }
-
- onDragHorizontal(gesture) {
- this.zone.run(() => {
- if (this.startX === null) {
- // starting drag
- gesture.srcEvent.preventDefault();
- gesture.srcEvent.stopPropagation();
-
- this.startX = gesture.center.x;
- this.width = this.pane.width() - this.startX;
-
- this.viewCtrl.swipeBackStart();
- }
-
- this.viewCtrl.swipeBackProgress( (gesture.center.x - this.startX) / this.width );
- });
- }
-
- get showHandle() {
- return (this.viewCtrl ? this.viewCtrl.canSwipeBack() : false);
- }
-
- onDestroy() {
- this.gesture && this.gesture.destroy();
- }
-
-}
diff --git a/ionic/components/overlay/overlay.ts b/ionic/components/overlay/overlay.ts
index 0aa164ee9f..7c359de0be 100644
--- a/ionic/components/overlay/overlay.ts
+++ b/ionic/components/overlay/overlay.ts
@@ -3,7 +3,6 @@ import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
import {IonicApp} from '../app/app';
import {Animation} from '../../animations/animation';
-import {ClickBlock} from '../../util/click-block';
import * as util from 'ionic/util';
@@ -127,16 +126,14 @@ export class OverlayRef {
animation.before.addClass('show-overlay');
- ClickBlock(true, animation.duration() + 200);
- this.app.setTransitioning(true);
+ this.app.setEnabled(false, animation.duration());
this.app.zoneRunOutside(() => {
animation.play().then(() => {
this.app.zoneRun(() => {
- ClickBlock(false);
- this.app.setTransitioning(false);
+ this.app.setEnabled(true);
animation.dispose();
instance.viewDidEnter && instance.viewDidEnter();
resolve();
@@ -161,8 +158,7 @@ export class OverlayRef {
let animation = Animation.create(this._elementRef.nativeElement, animationName);
animation.after.removeClass('show-overlay');
- ClickBlock(true, animation.duration() + 200);
- this.app.setTransitioning(true, animation.duration() + 200);
+ this.app.setEnabled(false, animation.duration());
animation.play().then(() => {
instance.viewDidLeave && instance.viewDidLeave();
@@ -170,8 +166,7 @@ export class OverlayRef {
this._dispose();
- ClickBlock(false);
- this.app.setTransitioning(false);
+ this.app.setEnabled(true);
animation.dispose();
resolve();
diff --git a/ionic/components/tabs/tabs.ts b/ionic/components/tabs/tabs.ts
index 5bd5f62acf..bd4d3eb51d 100644
--- a/ionic/components/tabs/tabs.ts
+++ b/ionic/components/tabs/tabs.ts
@@ -1,5 +1,6 @@
import {Component, Directive, View, Injector, NgFor, ElementRef, Optional, Host, forwardRef, NgZone} from 'angular2/angular2';
+import {IonicApp} from '../app/app';
import {ViewController} from '../view/view-controller';
import {ViewItem} from '../view/view-item';
import {Icon} from '../icon/icon';
@@ -56,11 +57,13 @@ export class Tabs extends ViewController {
constructor(
@Optional() hostViewCtrl: ViewController,
@Optional() viewItem: ViewItem,
+ app: IonicApp,
injector: Injector,
elementRef: ElementRef,
zone: NgZone
) {
super(hostViewCtrl, injector, elementRef, zone);
+ this.app = app;
// Tabs may also be an actual ViewItem which was navigated to
// if Tabs is static and not navigated to within a ViewController
@@ -117,7 +120,7 @@ export class Tabs extends ViewController {
enteringItem = this.getByInstance(tab)
}
- if (!enteringItem || !enteringItem.instance || this.isTransitioning()) {
+ if (!enteringItem || !enteringItem.instance || !this.app.isEnabled()) {
return Promise.reject();
}
diff --git a/ionic/components/text-input/text-input.ts b/ionic/components/text-input/text-input.ts
index d88d911d79..020922d62e 100644
--- a/ionic/components/text-input/text-input.ts
+++ b/ionic/components/text-input/text-input.ts
@@ -7,7 +7,6 @@ import {Label} from './label';
import {Ion} from '../ion';
import {IonicApp} from '../app/app';
import {Content} from '../content/content';
-import {ClickBlock} from '../../util/click-block';
import * as dom from '../../util/dom';
import {IonicPlatform} from '../../platform/platform';
@@ -160,7 +159,7 @@ export class TextInput extends Ion {
* @param {Event} ev TODO
*/
pointerStart(ev) {
- if (this.scrollAssist && !this.app.isTransitioning()) {
+ if (this.scrollAssist && this.app.isEnabled()) {
// remember where the touchstart/mousedown started
this.startCoord = dom.pointerCoord(ev);
}
@@ -171,8 +170,7 @@ export class TextInput extends Ion {
* @param {Event} ev TODO
*/
pointerEnd(ev) {
-
- if (this.app.isTransitioning()) {
+ if (!this.app.isEnabled()) {
ev.preventDefault();
ev.stopPropagation();
@@ -234,8 +232,7 @@ export class TextInput extends Ion {
// manually scroll the text input to the top
// do not allow any clicks while it's scrolling
- ClickBlock(true, SCROLL_INTO_VIEW_DURATION + 100);
- this.app.setTransitioning(true, SCROLL_INTO_VIEW_DURATION + 100);
+ this.app.setEnabled(false, SCROLL_INTO_VIEW_DURATION);
// temporarily move the focus to the focus holder so the browser
// doesn't freak out while it's trying to get the input in place
@@ -249,8 +246,7 @@ export class TextInput extends Ion {
this.setFocus();
// all good, allow clicks again
- ClickBlock(false);
- this.app.setTransitioning(false);
+ this.app.setEnabled(true);
});
} else {
diff --git a/ionic/components/toolbar/toolbar.ts b/ionic/components/toolbar/toolbar.ts
index dca04bff82..4aacc02b02 100644
--- a/ionic/components/toolbar/toolbar.ts
+++ b/ionic/components/toolbar/toolbar.ts
@@ -34,7 +34,7 @@ export class ToolbarBase extends Ion {
* @returns {TODO} TODO
*/
getTitleRef() {
- return this.titleCmp && this.titleCmp.elementRef.textContent;
+ return this.titleCmp && this.titleCmp.elementRef;
}
/**
diff --git a/ionic/components/view/swipe-back.ts b/ionic/components/view/swipe-back.ts
new file mode 100644
index 0000000000..a4c5372708
--- /dev/null
+++ b/ionic/components/view/swipe-back.ts
@@ -0,0 +1,29 @@
+import {SlideEdgeGesture} from 'ionic/gestures/slide-edge-gesture';
+
+
+export class SwipeBackGesture extends SlideEdgeGesture {
+
+ constructor(element: Element, opts: Object = {}, viewCtrl) {
+ super(element, opts);
+ // Can check corners through use of eg 'left top'
+ this.edges = opts.edge.split(' ');
+ this.threshold = opts.threshold;
+ this.viewCtrl = viewCtrl;
+ }
+
+ onSlideStart() {
+ this.viewCtrl.swipeBackStart();
+ }
+
+ onSlide(slide, ev) {
+ this.viewCtrl.swipeBackProgress(slide.distance / slide.max);
+ }
+
+ onSlideEnd(slide, ev) {
+ let shouldComplete = (Math.abs(ev.velocityX) > 0.2 || Math.abs(slide.delta) > Math.abs(slide.max) * 0.5);
+
+ // TODO: calculate a better playback rate depending on velocity and distance
+ this.viewCtrl.swipeBackFinish(shouldComplete, 1);
+ }
+
+}
diff --git a/ionic/components/view/view-controller.ts b/ionic/components/view/view-controller.ts
index 206201e261..3fc5f4bfdf 100644
--- a/ionic/components/view/view-controller.ts
+++ b/ionic/components/view/view-controller.ts
@@ -9,8 +9,7 @@ import {ViewItem} from './view-item';
import {NavController} from '../nav/nav-controller';
import {PaneController} from '../nav/pane';
import {Transition} from '../../transitions/transition';
-import {ClickBlock} from '../../util/click-block';
-import {SlideEdgeGesture} from 'ionic/gestures/slide-edge-gesture';
+import {SwipeBackGesture} from './swipe-back';
import * as util from 'ionic/util';
/**
@@ -39,9 +38,9 @@ export class ViewController extends Ion {
this.items = [];
this.panes = new PaneController(this);
- this.sbTransition = null;
- this.sbActive = false;
- this.sbEnabled = true;
+ this._sbTrans = null;
+ this.sbEnabled = config.setting('swipeBackEnabled') || false;
+ this.sbThreshold = config.setting('swipeBackThreshold') || 40
this.id = ++ctrlIds;
this._ids = -1;
@@ -62,7 +61,7 @@ export class ViewController extends Ion {
* @returns {Promise} TODO
*/
push(componentType, params = {}, opts = {}) {
- if (!componentType || this.isTransitioning()) {
+ if (!componentType || !this._isEnabled()) {
return Promise.reject();
}
@@ -110,7 +109,7 @@ export class ViewController extends Ion {
* @returns {Promise} TODO
*/
pop(opts = {}) {
- if (this.isTransitioning() || this.items.length < 2) {
+ if (!this._isEnabled() || !this.canGoBack()) {
return Promise.reject();
}
@@ -146,7 +145,7 @@ export class ViewController extends Ion {
});
} else {
- this.transitionComplete();
+ this._transComplete();
resolve();
}
@@ -279,8 +278,7 @@ export class ViewController extends Ion {
if (duration > 64) {
// block any clicks during the transition and provide a
// fallback to remove the clickblock if something goes wrong
- ClickBlock(true, duration + 200);
- this.app.setTransitioning(true, duration + 200);
+ this._setEnabled(false, duration);
}
// start the transition
@@ -298,7 +296,7 @@ export class ViewController extends Ion {
// all done!
this.zone.run(() => {
- this.transitionComplete();
+ this._transComplete();
callback();
});
});
@@ -313,12 +311,12 @@ export class ViewController extends Ion {
* TODO
*/
swipeBackStart() {
- if (this.isTransitioning() || this.items.length < 2) {
+ if (!this._isEnabled() || !this.canSwipeBack()) {
return;
}
- this.sbActive = true;
- this.sbResolve = null;
+ // disables the app during the transition
+ this._setEnabled(false);
// default the direction to "back"
let opts = {
@@ -339,55 +337,20 @@ export class ViewController extends Ion {
enteringItem.shouldCache = false;
enteringItem.willEnter();
- this.app.setTransitioning(true);
-
// wait for the new item to complete setup
enteringItem.stage(() => {
- // set that the new item pushed on the stack is staged to be entering/leaving
- // staged state is important for the transition to find the correct item
- enteringItem.state = STAGED_ENTERING_STATE;
- leavingItem.state = STAGED_LEAVING_STATE;
+ this.zone.runOutsideAngular(() => {
+ // set that the new item pushed on the stack is staged to be entering/leaving
+ // staged state is important for the transition to find the correct item
+ enteringItem.state = STAGED_ENTERING_STATE;
+ leavingItem.state = STAGED_LEAVING_STATE;
- // init the transition animation
- this.sbTransition = Transition.create(this, opts);
- this.sbTransition.easing('linear').progressStart();
-
- let swipeBackPromise = new Promise(res => { this.sbResolve = res; });
-
- swipeBackPromise.then((completeSwipeBack) => {
-
- if (completeSwipeBack) {
- // swipe back has completed, update each item's state
- enteringItem.state = ACTIVE_STATE;
- leavingItem.state = CACHED_STATE;
-
- enteringItem.didEnter();
- leavingItem.didLeave();
-
- if (this.router) {
- // notify router of the state change
- this.router.stateChange('pop', enteringItem);
- }
-
- } else {
- // cancelled the swipe back, return items to original state
- leavingItem.state = ACTIVE_STATE;
- enteringItem.state = CACHED_STATE;
-
- leavingItem.willEnter();
- leavingItem.didEnter();
- enteringItem.didLeave();
-
- leavingItem.shouldDestroy = false;
- enteringItem.shouldDestroy = false;
- }
-
- // all done!
- this.transitionComplete();
+ // init the swipe back transition animation
+ this._sbTrans = Transition.create(this, opts);
+ this._sbTrans.easing('linear').progressStart();
});
-
});
}
@@ -396,31 +359,102 @@ export class ViewController extends Ion {
* TODO
* @param {TODO} progress TODO
*/
- swipeBackProgress(progress) {
- if (this.sbTransition) {
- ClickBlock(true, 4000);
- this.app.setTransitioning(true, 4000);
- this.sbTransition.progress( Math.min(1, Math.max(0, progress)) );
+ swipeBackProgress(value) {
+ if (this._sbTrans) {
+ // continue to disable the app while actively dragging
+ this._setEnabled(false, 4000);
+
+ // set the transition animation's progress
+ this._sbTrans.progress(value);
}
}
/**
- * TODO
- * @param {TODO} completeSwipeBack TODO
- * @param {TODO} progress TODO
- * @param {TODO} playbackRate TODO
+ * @private
+ * @param {TODO} completeSwipeBack Should the swipe back complete or not.
+ * @param {number} rate How fast it closes
*/
- swipeBackFinish(completeSwipeBack, playbackRate) {
- // to reverse the animation use a negative playbackRate
- if (this.sbTransition && this.sbActive) {
- this.sbActive = false;
+ swipeBackFinish(completeSwipeBack, rate) {
+ if (!this._sbTrans) return;
+
+ // disables the app during the transition
+ this._setEnabled(false);
+
+ this._sbTrans.progressFinish(completeSwipeBack, rate).then(() => {
+
+ this.zone.run(() => {
+ // find the items that were entering and leaving
+ let enteringItem = this.getStagedEnteringItem();
+ let leavingItem = this.getStagedLeavingItem();
+
+ if (enteringItem && leavingItem) {
+ // finish up the animation
+
+ if (completeSwipeBack) {
+ // swipe back has completed navigating back
+ // update each item's state
+ enteringItem.state = ACTIVE_STATE;
+ leavingItem.state = CACHED_STATE;
+
+ enteringItem.didEnter();
+ leavingItem.didLeave();
+
+ if (this.router) {
+ // notify router of the pop state change
+ this.router.stateChange('pop', enteringItem);
+ }
+
+ } else {
+ // cancelled the swipe back, they didn't end up going back
+ // return items to their original state
+ leavingItem.state = ACTIVE_STATE;
+ enteringItem.state = CACHED_STATE;
+
+ leavingItem.willEnter();
+ leavingItem.didEnter();
+ enteringItem.didLeave();
+
+ leavingItem.shouldDestroy = false;
+ enteringItem.shouldDestroy = false;
+ }
+ }
+
+ // empty out and dispose the swipe back transition animation
+ this._sbTrans && this._sbTrans.dispose();
+ this._sbTrans = null;
+
+ // all done!
+ this._transComplete();
- this.sbTransition.progressFinish(completeSwipeBack, playbackRate).then(() => {
- this.sbResolve && this.sbResolve(completeSwipeBack);
- this.sbTransition && this.sbTransition.dispose();
- this.sbResolve = this.sbTransition = null;
- this.app.setTransitioning(false);
});
+ });
+
+ }
+
+ _runSwipeBack() {
+ if (this.canSwipeBack()) {
+ // it is possible to swipe back
+
+ if (this.sbGesture) {
+ // this is already an active gesture, don't create another one
+ return;
+ }
+
+ let opts = {
+ edge: 'left',
+ threshold: this.sbThreshold
+ };
+ this.sbGesture = new SwipeBackGesture(this.getNativeElement(), opts, this);
+ console.debug('SwipeBackGesture listen');
+ this.sbGesture.listen();
+
+
+ } else if (this.sbGesture) {
+ // it is not possible to swipe back and there is an
+ // active sbGesture, so unlisten it
+ console.debug('SwipeBackGesture unlisten');
+ this.sbGesture.unlisten();
+ this.sbGesture = null;
}
}
@@ -437,27 +471,33 @@ export class ViewController extends Ion {
}
/**
- * TODO
- * @returns {TODO} TODO
+ * If it's possible to use swipe back or not. If it's not possible
+ * to go back, or swipe back is not enable then this will return false.
+ * If it is possible to go back, and swipe back is enabled, then this
+ * will return true.
+ * @returns {boolean}
*/
canSwipeBack() {
- if (this.sbEnabled) {
- let activeItem = this.getActive();
- if (activeItem) {
- return activeItem.enableBack();
- }
+ return (this.sbEnabled && this.canGoBack());
+ }
+
+ /**
+ * Returns `true` if there's a valid previous view that we can pop back to.
+ * Otherwise returns false.
+ * @returns {boolean}
+ */
+ canGoBack() {
+ let activeItem = this.getActive();
+ if (activeItem) {
+ return activeItem.enableBack();
}
return false;
}
- runSwipeBack() {
- if (!this.canSwipeBack()) return;
- }
-
/**
- * TODO
+ * @private
*/
- transitionComplete() {
+ _transComplete() {
let destroys = [];
this.items.forEach(item => {
@@ -476,31 +516,34 @@ export class ViewController extends Ion {
item.destroy();
});
- // allow clicks again
- ClickBlock(false);
- this.app.setTransitioning(false);
+ // allow clicks again, but still set an enable time
+ // meaning nothing with this view controller can happen for XXms
+ this._setEnabled(true);
if (this.items.length === 1) {
this.elementRef.nativeElement.classList.add('has-views');
}
- this.runSwipeBack();
+ this._runSwipeBack();
}
- /**
- * TODO
- * @returns {boolean} TODO
- */
- isTransitioning() {
- let state;
- for (let i = 0, ii = this.items.length; i < ii; i++) {
- state = this.items[i].state;
- if (state === STAGED_ENTERING_STATE ||
- state === STAGED_LEAVING_STATE) {
- return true;
- }
+ _setEnabled(isEnabled, fallback) {
+ // used to prevent unwanted transitions after JUST completing one
+ // prevents the user from crazy clicking everything and possible flickers
+ // gives the app some time to cool off, slow down, and think about life
+ this._enableTime = Date.now() + 100;
+
+ // IonicApp global setEnabled to prevent other things from starting up
+ this.app.setEnabled(isEnabled, fallback);
+ }
+
+ _isEnabled() {
+ // used to prevent unwanted transitions after JUST completing one
+ if (this._enableTime > Date.now()) {
+ return false;
}
- return false;
+ // IonicApp global isEnabled, maybe something else has the app disabled
+ return this.app.isEnabled();
}
/**
diff --git a/ionic/platform/registry.ts b/ionic/platform/registry.ts
index 9df4180097..8b3e8f2301 100644
--- a/ionic/platform/registry.ts
+++ b/ionic/platform/registry.ts
@@ -79,6 +79,11 @@ IonicPlatform.register({
},
keyboardHeight: 290,
hoverCSS: false,
+ swipeBackEnabled: function(p) {
+ return true; // TODO: remove me! Force it to always work for iOS mode for now
+ return /iphone|ipad|ipod/i.test(p.navigatorPlatform());
+ },
+ swipeBackThreshold: 40,
},
isMatch(p) {
return p.isPlatform('ios', 'iphone|ipad|ipod');
diff --git a/ionic/util/util.scss b/ionic/util/util.scss
index 0aab7f0c26..fc3abce1cd 100755
--- a/ionic/util/util.scss
+++ b/ionic/util/util.scss
@@ -97,31 +97,6 @@ backdrop {
}
-// Swipe Handle
-// --------------------------------------------------
-
-$swipe-handle-width: 20px !default;
-$swipe-handle-top: 70px !default;
-$swipe-handle-bottom: 70px !default;
-
-.swipe-handle {
- position: absolute;
- top: $swipe-handle-top;
- left: 0;
- bottom: $swipe-handle-bottom;
- width: $swipe-handle-width;
- z-index: $z-index-swipe-handle;
-
- //background: red;
- //opacity: 0.2;
-
- transform: translate3d(-999px, 0px, 0px);
- &.show-handle {
- transform: translate3d(0px, 0px, 0px);
- }
-}
-
-
// Loading Icon
// --------------------------------------------------