Skip to content

Issue with navigation between pages and data #476

@GauthierBosson

Description

@GauthierBosson

Hello,

So my issue is a weird one and I have no clue what is happening.
I have a Detail.ts page which receive props from an API. The data are fetched in the App.ts file. You can see both files below.

App.ts

{
    path: '/detail/:urn',
    component: detailPage,
    announce: 'Page Détail',
    options: { keepAlive: true },
    hooks: {
      async before(to, from) {
        // @ts-expect-error Blits typing issue
        this.$appState.previousPageHash = from !== undefined ? from.hash : ''
        // this.$appState.isPageLoading = true

        if (!to.params?.urn) {
          return '/error'
        }

        try {
          const { data, error, success } = await apiManager.fetchDetail(to.params?.urn as string)
          if (!success || error || !data) {
            console.error('Failed to fetch Detail data:', error)
            return '/error'
          }
          const { urn, gradientColor, hero, tabColor, tabs } = data
          to.data = {
            urn,
            gradientColor,
            hero,
            tabColor,
            tabs,
            backButtonText: getBackButtonText(from),
          }
          return to
        } catch (e) {
          return '/error'
        }
      },
    },
  },

Detail.ts:

import Blits from '@lightningjs/blits'
import Hero from '@components/organisms/Hero'
import Tabs from '@/components/molecules/Tabs'
import BackButton from '@/components/atoms/BackButton'
import DetailHero from '@/components/organisms/hero/details/DetailHero'
import { DISMISS_SPLASHSCREEN } from '@/App'
import { processImageUrl } from '@/utils/api/imageProxy'
import { DetailHeroItem, DetailTabs } from '../utils/types'
import { PageDetail } from '@/utils/api/ApiManager'

const DEFAULT_DETAIL_HERO_DATA: Omit<DetailHeroItem, 'downloadButton' | 'shareButton'> = {
  urn: '',
  title: '',
  logo: null,
  image: { url: '', alt: '' },
  playButton: { icon: 'Play', label: 'Lire', urn: '', accessibilityLabel: '' },
  myListButton: {
    value: false,
    inactive: { icon: 'MyList', label: 'Ma liste', accessibilityLabel: '' },
    active: { icon: 'Checkmark', label: 'Dans ma liste', accessibilityLabel: '' },
  },
  progress: null,
  tagline: '',
  oneLiner: null,
  labels: [],
}

const DEFAULT_DETAIL_DATA: Omit<PageDetail, 'hero'> & { hero: Omit<DetailHeroItem, 'downloadButton' | 'shareButton'> } = {
  urn: '',
  gradientColor: '',
  hero: DEFAULT_DETAIL_HERO_DATA,
  tabColor: '',
  tabs: [],
}

export default Blits.Component('Detail', {
  components: {
    Hero,
    Tabs,
    BackButton,
    DetailHero,
  },
  template: `
    <Element color="black">
      <Element :y.transition="$heroDetailsOffset" z="1">
        <BackButton
          z="1"
          ref="details-item-0"
          text="$backButtonText"
          x="$$theme.get('columns.margin')"
          y="$$theme.get('rows.margin')"
        />
        <!--<Hero
                                  :src="$proxyImage($hero?.image?.url)"
                                  :blurredSrc="$proxyImage($hero?.image?.url, true)"
                                  :gradientColor="$gradientColor"
                                  childMount="{y: -0.9}"
                                >
                                  <DetailHero slot="hero-content" ref="details-item-1" :data="$hero" />
                                </Hero>-->
        <Text :content="$hero?.title" />
      </Element>
      <Element
        z="1"
        w="$$theme.get('viewport.width')"
        h="$$theme.get('viewport.height')"
        :shader="$shader('gradient', {
            colors: [$gradientColor || '#000000', '#000000'],
            stops: [0, 40],
            alphas: [1.0, 1.0],
          })"
        :y.transition="$gradientOffset"
      />

      <Tabs z="2" ref="details-item-2" :tabs="$tabs" x="$$theme.get('columns.margin')" :y.transition="$tabsOffset" />
    </Element>
  `,
  props: [
    { key: 'urn', default: DEFAULT_DETAIL_DATA.urn },
    { key: 'gradientColor', default: DEFAULT_DETAIL_DATA.gradientColor },
    { key: 'tabColor', default: DEFAULT_DETAIL_DATA.tabColor },
    { key: 'hero', default: DEFAULT_DETAIL_DATA.hero },
    { key: 'tabs', default: DEFAULT_DETAIL_DATA.tabs },
    { key: 'detailData', default: DEFAULT_DETAIL_DATA },
    { key: 'backButtonText', default: 'Homepage' },
  ],
  state() {
    return {
      focused: 1,
    }
  },
  hooks: {
    ready() {
      this.$emit(DISMISS_SPLASHSCREEN)
    },
    focus() {
      if (this.$appState.isPageLoading) this.$appState.isPageLoading = false
      this.$trigger('focused')
    },
  },
  computed: {
    testTitle() {
      return 'test title ' + this.urn
    },
    heroDetailsOffset() {
      if (this.focused <= 1) return 0
      return -(this.focused - 1) * 1100
    },
    tabsOffset() {
      if (this.focused <= 1) return 1042
      return -(this.focused - 1) * 1000 + 1030
    },
    gradientOffset() {
      if (this.focused <= 1) return 1080
      return -(this.focused - 1) * 1100 + 1080
    },
  },
  methods: {
    proxyImage(url: string | undefined, blurred: boolean = false) {
      if (!url) return ''
      return processImageUrl(url, null, blurred)
    },
  },
  watch: {
    focused(value: number) {
      const item = this.$select('details-item-' + value)
      if (item && item.$focus) {
        item.$focus()
      }
    },
    '$router.state.navigating'() {
      if (!this.$router.state.navigating && this.$router.currentRoute.path === 'details/:id')
        this.$appState.isPageLoading = false
    },
  },
  input: {
    up() {
      this.focused = Math.max(0, this.focused - 1)
    },
    down() {
      this.focused = Math.min(2, this.focused + 1)
    },
  },
})

The issue I have is that when for example I am in the home page, go the a detail page, go back and go to another detail page, It randomly crash with the following error:

Image

At first I thought something was undefined, but that's not the case, every data seems to be there when I log the returned to from the before hook. Then I found out the error was emitted by both the Hero and HeroDetail components, which both receive the hero props. After commenting those components, everything works as intended. Then I tried to place a random text which receive $hero.title and the problem was back again.
The issue is that as I said, hero and hero.title are defined so there is not reason for it to crash in my opinion so I suspect maybe there's something wrong with how data is passed to the page?

Here you can see that the before hook logs the data but it stills crash in the end

Image

Below you can find two videos. One where $hero.title and the app crashes, and another one where the props urn from the API is used and everything works fine.

crash with $hero.title:

app-crash.mp4

works fine with $urn:

app-works.mp4

Really not sure what is happening here, not even sure if it's something internal or it has something to do with my data, but if you have any insight I'd happily hear them.

EDIT: I updated to 1.38.3, issue is still here the only difference is that the app crashes before navigation so I don't see a black screen anymore

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions