All files / runtime/src/plugins navigation.ts

0% Statements 0/122
0% Branches 0/1
0% Functions 0/1
0% Lines 0/122

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123                                                                                                                                                                                                                                                     
import { App, Component, isRef, Ref } from '@vue/runtime-core'
import { Frame, NavigationEntry, Page } from '@nativescript/core'
import { createApp, NSVElement } from '@nativescript-vue/runtime'
import { NavigatedData } from '@nativescript/core'

declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    /**
     * todo: update docblock
     * Navigate to {target} component.
     *
     * The frame to navigate defaults to the topmost frame
     * @param target
     * @param options
     */
    $navigateTo: (
      target: Component,
      options?: NavigationOptions
    ) => Promise<any>
    $navigateBack: (options?: NavigationOptions) => void
  }
}

type ResolvableFrame = string | Ref | NSVElement | Frame | undefined

export interface NavigationOptions extends NavigationEntry {
  props?: Record<string, any>
  frame?: ResolvableFrame
}

export interface BackNavigationOptions {
  frame?: ResolvableFrame
}

/**
 * @internal
 */
export function install(app: App) {
  app.config.globalProperties.$navigateTo = $navigateTo
  app.config.globalProperties.$navigateBack = $navigateBack
}

function resolveFrame(frame: ResolvableFrame): Frame {
  if (!frame) {
    return Frame.topmost()
  }

  if (frame instanceof Frame) {
    return frame
  }

  // todo: check if refs work this way or not
  if (isRef(frame)) {
    return frame.value
  }

  if (frame instanceof NSVElement) {
    return frame.nativeView
  }

  // todo: either change core Frame to add frames to the stack when they are created
  // or do as we did in 2.x - handle a Map of frames.
  // right now, empty frames can't be navigated as they are not recognized by `getFrameById`
  return Frame.getFrameById(frame)
}

export async function $navigateTo(
  target: Component,
  options?: NavigationOptions
): Promise<Page> {
  options = Object.assign({}, options)
  console.log('$navigateTo')

  try {
    const frame = resolveFrame(options.frame)

    if (!frame) {
      throw new Error('Failed to resolve frame. Make sure your frame exists.')
    }

    const navigationApp = createApp(target, options.props)
    const targetPage = (navigationApp.mount().$el.nativeView as unknown) as Page

    const handler = (args: NavigatedData) => {
      if (args.isBackNavigation) {
        targetPage.off('navigatedFrom', handler as any)
        navigationApp.unmount()
      }
    }
    targetPage.on('navigatedFrom', handler)

    const dispose = targetPage.disposeNativeView
    targetPage.disposeNativeView = () => {
      navigationApp.unmount()
      dispose.call(targetPage)
    }

    frame.navigate({
      ...options,
      create: () => targetPage,
    })
    return targetPage
  } catch (e) {
    console.log('[$navigateTo] Failed to navigate:\n\n')
    console.log(e)
    throw e
  }
}

export async function $navigateBack(options?: BackNavigationOptions) {
  const frame = resolveFrame(options ? options.frame : undefined)

  if (!frame) {
    throw new Error('Failed to resolve frame. Make sure your frame exists.')
  }

  if (!frame.canGoBack()) {
    return
  }

  frame.goBack()
}