NativeScript / theme

@nativescript/theme
https://v7.docs.nativescript.org/ui/theme
Apache License 2.0
128 stars 45 forks source link

Cannot read property 'mainScreen' of undefined - when trying to get dark mode status #275

Open butaminas opened 4 years ago

butaminas commented 4 years ago

Environment Provide version numbers for the following components (information can be retrieved by running tns info in your project folder or by inspecting the package.json of the project):

 - Plugin(s):
    "@nativescript/core": "~7.0.0",
    "@nativescript/firebase": "rc",
    "@nativescript/theme": "nativescript-vue" (tried with rc also),
    "nativescript-vue": "~2.8.0"

**Describe the bug**
Pretty much stock nativescript-vue installation with NativeScript CLI 7.0.8.
In my main screen I import the theme using `import Theme from "@nativescript/theme";`
Then I'm trying to get the dark mode status like this:

mounted () { console.log(Theme.getMode()) },


I get an error:
`(CoreFoundation) *** Terminating app due to uncaught exception 'NativeScript encountered a fatal error: Uncaught TypeError: Cannot read property 'mainScreen' of undefined`

**To Reproduce**
Just try to use `Theme.getMode()` on NativeScript 7 with Vue (might be the same with others as well)

**Expected behavior**
Haven't used this feature before, I guess it will give you a string of "ns-light" or "ns-dark" and won't break your app.

**Sample project**
Can't reproduce this on play.nativescript.org as it uses the old NativeScript. As far as I've tested it seems to be the issue only with NativeScript 7
martijnvanschie commented 4 years ago

Same for me. Tried both a Angular Template and a few minutes ago a Core template. Both same error message.

martijnvanschie commented 4 years ago

This issue is still hunting me even after updating to 2.3.3 (which seems to fix it for some people) and the dark theme is really making my app unusable. Any progress on this?

martijnvanschie commented 4 years ago

So after looking a litthe further if found out that there is actually a new version of the Theme packages that support {N}7. I used the RC version ("@nativescript/theme": "^2.5.0").

An up-to-date listing of NativeScript plugins with 7.0 compatibility that we know of @nativescript/theme

After upgrading to this version the issue is gone and i can now use the light theme. Currently i can only implement a toggle button that works. Can not get the app to start in Light mode. Somehow the app loads in Dark Mode as this is currently my settings on the phone.

If i can get the app to also START in light mode i'm completely happy :)

butaminas commented 4 years ago

@martijnvanschie updating to RC also does the trick for me.

You can try adding this to your info.plist:
...
<key>UIUserInterfaceStyle</key>
<string>Light</string>
...

This would force the light mode by default as mentioned here: https://docs.nativescript.org/ui/dark-mode

butaminas commented 4 years ago

After working on it a bit more, I've noticed that Theme.getMode() is not working properly. Even if I start in dark mode, I always get ns-light.

The systemAppearanceChanged event works fine on the other hand. But I need to know the appearance when the app is launched.

This is most likely an old issue that is mentioned here: #232 where the solution was to use Application.systemAppearance() as an alternative, however, this also doesn't work as I end up with (CoreFoundation) *** Terminating app due to uncaught exception 'NativeScript encountered a fatal error: Uncaught TypeError: Cannot read property 'substr' of undefined error.

butaminas commented 4 years ago

Here is my workaround for the above problem. Note, this is not a solution and this still needs to be fixed but since I was struggling with this a lot it might be useful for someone else also:

import { Application, isIOS } from "@nativescript/core"
import store from "../index"

Application.on(Application.displayedEvent, (args) => {
    if (isIOS) {
        store.commit('generalStore/setDarkMode', Application.ios.systemAppearance)
    } else {
        store.commit('generalStore/setDarkMode', Application.android.systemAppearance)
    }
});

P.S. I'm using nativescript-vue and Vuex but the idea is clear - you go for Application.ios.systemAppearance or Application.android.systemAppearance.

IMPORTANT: you need to run it in displayedEvent or do a manual timeout, otherwise it will not work.

martijnvanschie commented 4 years ago

Thanx for the updates @butaminas.

I've been playing around some more with this code and found still strange behaviour. When changing the theme on my phone the {N} Theme stays the same. This is is my debugging code

Application.on(Application.systemAppearanceChangedEvent, (args: SystemAppearanceChangedEventData) => {
    console.log('systemAppearanceChangedEvent', args.newValue);
    console.log('Theme.getMode', Theme.getMode());
});

The output is al follows (When switching from Dark to Light and back)

JS: systemAppearanceChangedEvent light
JS: Theme.getMode ns-light
JS: systemAppearanceChangedEvent dark
JS: Theme.getMode ns-light

Also, setting mode to light in my main.ts requires a timeout to be set.

Below code does not set the {N} Theme

Theme.setMode(Theme.Light);

It required a (very short) timeout to make it work

setTimeout(() => {
    console.log('Setting mode to light');
    Theme.setMode(Theme.Light);
  },100);

So i kinda have it working now, but I keep on investigating :)

PS: I'm using {N}7 Angular

butaminas commented 4 years ago

@martijnvanschie it's definitely a bug that seems to be around for quite a while. From what I came to realize is that at this point, you can't use Theme.getMode() at all as it is not functioning properly.

Currently, the only solution that is working is Application.ios.systemAppearance and Application.android.systemAppearance as I mentioned in the previous post. This returns the correct value at launch (at least with all the tests I was able to make) and systemAppearanceChangedEvent also seems to be returning the correct value.

And yes, it also doesn't work for me without a delay. Setting everything to run at displayedEvent seems to be enough of a delay for everything to work properly, so I guess it makes sense that Theme.setMode would also require a delay to work.

If you find anything else worth mentioning - let me and the community know as I think this is uncharted waters for most of us. Hopefully these "hacks" will stay stable and do the trick until the core is fixed.

martijnvanschie commented 4 years ago

I'll take a look at that displayEvent again and test it out.

I do have another issue which is must more annoying than the hacks. When i go from light back to dark mode on my phone (Galaxy S20+) the app stays in dark mode. Even my test button to setMode() of toggleMode() wont work anymore, dark mode it is from that point on untill i restart the app again. Not a use case that is often performed i quess but one that should work as we developers do test the less obvious use cases :)

Think i'll raise a seperate issue for this but the issues keep stacking and it does not seem to be picked up. I understand it's busy and probably due to the {N}7 upgrade as well.

butaminas commented 4 years ago

That's strange. I've tried switching dark / light mode on Xiaomi Redmi Note 8 and the appearance in the app is changing accordingly.

Will try to test this out on raw Android to see if I get different results.