Skip to content

Commit 7f23186

Browse files
committed
fix (hmr): Fix navigation trigger on iOS when HMR reloads app if <script> is changed. See nativescript-vue#388
1 parent 38c468d commit 7f23186

File tree

6 files changed

+119
-12
lines changed

6 files changed

+119
-12
lines changed

platform/nativescript/plugins/navigator-plugin.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { isObject, isDef, isPrimitive } from 'shared/util'
22
import { getFrame } from '../util/frame'
33
import { getFrameById } from 'tns-core-modules/ui/frame'
44

5-
function getFrameInstance(frame) {
5+
export function getFrameInstance(frame) {
66
// get the frame that we need to navigate
77
// this can be a frame id (String)
88
// a Vue ref to a frame
@@ -19,7 +19,7 @@ function getFrameInstance(frame) {
1919
return getFrame(frame.id)
2020
}
2121

22-
function _findParentNavigationEntry(vm) {
22+
export function findParentNavigationEntry(vm) {
2323
if (!vm) {
2424
return false
2525
}
@@ -35,7 +35,7 @@ function _findParentNavigationEntry(vm) {
3535
export default {
3636
install(Vue) {
3737
Vue.prototype.$navigateBack = function(options, backstackEntry = null) {
38-
const navEntry = _findParentNavigationEntry(this)
38+
const navEntry = findParentNavigationEntry(this)
3939
const defaultOptions = {
4040
frame: navEntry ? navEntry.$options.frame : 'default'
4141
}

platform/nativescript/renderer/ViewNode.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ export default class ViewNode {
2727
this._meta = null
2828

2929
/* istanbul ignore next
30-
* make vue happy :)
31-
*/
30+
* make vue happy :)
31+
*/
3232
this.hasAttribute = this.removeAttribute = () => false
3333
}
3434

platform/nativescript/runtime/components/frame.js

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { setFrame, getFrame, deleteFrame } from '../../util/frame'
2-
import { extend } from 'shared/util'
3-
import { isAndroid } from 'tns-core-modules/platform'
2+
import { isHMRChecking } from '../../util/hmr'
3+
import { isAndroid, isIOS } from 'tns-core-modules/platform'
4+
import { ios as iosUtils } from 'tns-core-modules/utils/utils'
45

56
let idCounter = 1
67

@@ -88,13 +89,80 @@ export default {
8889
},
8990

9091
notifyPageMounted(pageVm) {
92+
let options = {
93+
create: () => pageVm.$el.nativeView
94+
}
95+
9196
this.$nextTick(() => {
92-
this.navigate({
93-
create: () => pageVm.$el.nativeView
94-
})
97+
if (isHMRChecking()) {
98+
this.replace(options)
99+
} else {
100+
this.navigate(options)
101+
}
95102
})
96103
},
97104

105+
replace(entry) {
106+
const frame = this._getFrame()
107+
const page = entry.create()
108+
entry.create = () => page
109+
110+
const backstackEntry = {
111+
entry: entry,
112+
resolvedPage: page,
113+
navDepth: undefined,
114+
fragmentTag: undefined
115+
}
116+
if (isIOS) {
117+
// TODO: this should be in a specific NS Frame method
118+
let viewController = backstackEntry.resolvedPage.ios
119+
if (!viewController) {
120+
throw new Error(
121+
'Required page does not have a viewController created.'
122+
)
123+
}
124+
viewController['_transition'] = { name: 'non-animated' }
125+
viewController['_delegate'] = null
126+
frame._ios.controller.delegate = null
127+
// backstackEntry[NAV_DEPTH] = navDepth;
128+
viewController['_entry'] = backstackEntry
129+
130+
if (iosUtils.MajorVersion > 10) {
131+
// Reset back button title before pushing view controller to prevent
132+
// displaying default 'back' title (when NavigationButton custom title is set).
133+
let barButtonItem = UIBarButtonItem.alloc().initWithTitleStyleTargetAction(
134+
'',
135+
UIBarButtonItemStyle.Plain,
136+
null,
137+
null
138+
)
139+
viewController.navigationItem.backBarButtonItem = barButtonItem
140+
}
141+
142+
let newControllers = NSMutableArray.alloc().initWithArray(
143+
frame._ios.controller.viewControllers
144+
)
145+
if (newControllers.count === 0) {
146+
throw new Error('Wrong controllers count.')
147+
}
148+
149+
// the code below fixes a phantom animation that appears on the Back button in this case
150+
viewController.navigationItem.hidesBackButton =
151+
frame.backStack.length === 0
152+
153+
// swap the top entry with the new one
154+
const skippedNavController = newControllers.lastObject
155+
skippedNavController.isBackstackSkipped = true
156+
newControllers.removeLastObject()
157+
newControllers.addObject(viewController)
158+
159+
// replace the controllers instead of pushing directly
160+
frame._ios.controller.setViewControllersAnimated(newControllers, false)
161+
} else {
162+
// TODO: Implement for Android
163+
}
164+
},
165+
98166
navigate(entry, back = false) {
99167
const frame = this._getFrame()
100168

platform/nativescript/runtime/components/page.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
import {
2+
findParentNavigationEntry,
3+
getFrameInstance
4+
} from '../../plugins/navigator-plugin'
5+
import { isHMRChecking } from '../../util/hmr'
6+
17
export const PAGE_REF = '__vuePageRef__'
28

39
export default {
@@ -14,7 +20,17 @@ export default {
1420
mounted() {
1521
this.$el.nativeView[PAGE_REF] = this
1622

17-
const frame = this._findParentFrame()
23+
let frame = null
24+
25+
if (isHMRChecking()) {
26+
const navEntry = findParentNavigationEntry(this)
27+
const options = {
28+
frame: navEntry ? navEntry.$options.frame : 'default'
29+
}
30+
frame = getFrameInstance(options.frame)
31+
} else {
32+
frame = this._findParentFrame()
33+
}
1834

1935
if (frame) {
2036
frame.notifyPageMounted(this)

platform/nativescript/runtime/modules/transition.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ export function enter(vnode, toggleDisplay) {
7676

7777
const beforeEnterHook = isAppear ? beforeAppear || beforeEnter : beforeEnter
7878
const enterHook = isAppear
79-
? typeof appear === 'function' ? appear : enter
79+
? typeof appear === 'function'
80+
? appear
81+
: enter
8082
: enter
8183
const afterEnterHook = isAppear ? afterAppear || afterEnter : afterEnter
8284
const enterCancelledHook = isAppear

platform/nativescript/util/hmr.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
let hmrChecking = false
2+
3+
export const isHMRChecking = () => {
4+
return hmrChecking
5+
}
6+
7+
export const resetHMRChecking = () => {
8+
hmrChecking = false
9+
}
10+
11+
if (module.hot) {
12+
module.hot.addStatusHandler(status => {
13+
if (status === 'check') {
14+
hmrChecking = true
15+
// TODO: Improve the logic here
16+
setTimeout(() => {
17+
hmrChecking = false
18+
}, 2000)
19+
}
20+
})
21+
}

0 commit comments

Comments
 (0)