NativeScript / theme

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

App is changing from Light to Dark Theme after Webpack HMR #289

Open Tyler-V opened 3 years ago

Tyler-V commented 3 years ago

Environment image

Describe the bug I am trying to ensure the app remains in light mode. When I am debugging I set my phone settings to enable dark mode. I then debug the app and initially it appears in light mode however when webpack reloads on a change during development, it then appears to apply the dark theme.

To Reproduce

  1. Turn dark mode on in the phone settings
  2. ns debug android on my connected Pixel 5 (Android 11)
  3. Change text in a component to trigger HMR
  4. App reloads and appears in the dark theme

image

Expected behavior Dark mode should remain disabled and the ns-dark theme should not apply.

Additional context I have tried the following to ensure dark mode does not apply, none prevent the app from changing from light to dark on HMR.

  1. Setting <item name="android:forceDarkAllowed">false</item> in styles.xml (values-v29)
  2. Annotating class="ns-light" on <page-router-outlet>
  3. Setting Theme.setMode(Theme.Light); manually on ngOnInit in the app component and main.ts
  4. Adding the following snipplet to force it to disable night mode in main.ts
    if (android) {
    on(launchEvent, (args: ApplicationEventData) => {
        androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode(androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO);
    });
    }
csimpi commented 3 years ago

@Tyler-V Any news on this? We're having the same problem. I want to cut Dark Mode short and make sure the system setting won't override the app's theme.

Tyler-V commented 3 years ago

@csimpi I have not found a solution for this, the only other option I can think of if it affects the actual production built app is to create a custom theme and override the dark theme that is applied by importing the nativescript prebuilt themes to use the same light theme.

Tyler-V commented 3 years ago

After testing I can confirm this issue is isolated to Android and only during debugging after the first HMR, this issue thankfully does not exist in production on the built mobile app.

As a temporary solution, you can modify your App_Resources/Android/src/main/res/values/colors.xml and change the ns_primaryDark color to whatever your ns_primary color is.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="ns_primary">#F5F5F5</color>
    <color name="ns_primaryDark">#F5F5F5</color>   <---
    <color name="ns_accent">#33B5E5</color>
    <color name="ns_blue">#272734</color>
</resources>
senner008 commented 3 years ago

I have this happening seemingly at random with release builds also. As indicated I have not found a reliable way to reproduce yet.

Phone : OnePlus 6T, Android 10

Dependecies:


 "dependencies": {
    "@angular/animations": "~11.0.0",
    "@angular/common": "~11.0.0",
    "@angular/compiler": "~11.0.0",
    "@angular/core": "~11.0.0",
    "@angular/forms": "~11.0.0",
    "@angular/platform-browser": "~11.0.0",
    "@angular/platform-browser-dynamic": "~11.0.0",
    "@angular/router": "~11.0.0",
    "@nativescript-community/ble": "^3.0.13",
    "@nativescript/angular": "~11.0.0",
    "@nativescript/animated-circle": "^1.1.5",
    "@nativescript/core": "~7.0.0",
    "@nativescript/theme": "2.3.4",
    "@nstudio/nativescript-pulltorefresh": "^3.0.1",
    "@types/buffers": "^0.1.31",
    "@types/web-bluetooth": "0.0.8",
    "axios": "^0.21.1",
    "base-64": "^0.1.0",
    "buffer": "^5.6.0",
    "core-js": "^3.8.0",
    "crypto-js": "^3.3.0",
    "moment": "^2.27.0",
    "nativescript-appversion": "^1.4.4",
    "nativescript-background-fetch": "^1.2.1",
    "nativescript-couchbase-plugin": "^0.9.6",
    "nativescript-inappbrowser": "^3.0.1",
    "nativescript-permissions": "^1.3.11",
    "nativescript-screen-orientation": "^2.0.0",
    "reflect-metadata": "~0.1.12",
    "rxjs": "~6.6.0",
    "tslib": "~2.0.0",
    "uniqid": "^5.2.0",
    "uuid": "^8.2.0",
    "zone.js": "~0.11.1"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.1100.0",
    "@angular/cli": "~11.0.0",
    "@angular/compiler-cli": "~11.0.0",
    "@nativescript/android": "7.0.1",
    "@nativescript/tslint-rules": "~0.0.5",
    "@nativescript/types": "~7.0.0",
    "@nativescript/webpack": "~3.0.0",
    "@types/jasmine": "~3.5.0",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "^12.11.1",
    "codelyzer": "^5.1.2",
    "husky": "^4.2.5",
    "jasmine-core": "~3.5.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~4.4.1",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~2.1.0",
    "karma-jasmine": "~3.0.1",
    "karma-jasmine-html-reporter": "^1.4.2",
    "node-sass": "^4.7.1",
    "prettier": "^2.2.1",
    "pretty-quick": "^3.1.0",
    "protractor": "~5.4.3",
    "ts-node": "~8.3.0",
    "tslint": "~6.1.0",
    "typescript": "~4.0.0"
  },
NathanaelA commented 3 years ago

First this is a Android only issue, as it is Android's underlying OS that is changing colors out from underneath you. Example, you state it is supposed to be yellow and it replaces it with blue. Which really sucks, if I wanted Blue, I would have told it blue...

By default NS theme 2.3.4 and 3.0.1 automatically disable system forced themes. However, if something is (re)creating a Activity (like HMR might be doing), this could cause the system to consider it a new window and re-apply the system theme replacing system. I have also seen actual android dialogs get the wrong color, but I believe the code in 2.3.4/3.0.1 have fixes for that.

One solution to this is:

Tyler-V commented 3 years ago

@NathanaelA in the ticket additional context I've tried every possible option to disable dark mode completely which works when the application is built but resets as you've stated during HMR.

I think the only way to disable dark theme if using the nativescript core themes is to create a custom theme / mixin where the dark theme color values are set to the light theme values although I have not tested this.

This is strictly found in HMR on Android as you stated and does not impact production, just makes it a pain to continuously restart debugging if you are dealing with small text changes that may not show up against the dark theme based on your project.

NathanaelA commented 3 years ago

I'm pretty sure that I tested when I was fixing 2.5.4/3.0.1 using the disable xml in the style code and it worked all the time. Problem was it wasn't a solution I thought we could ship. However, now that I understand the root cause of the issue a lot more, It might actually be a valid solution. I need to run some additional tests...

senner007 commented 3 years ago

It happens in production also.

main.tns.ts :

// @ts-ignore
const myAndroidx: typeof androidx = androidx;
// @ts-ignore
const myAndroid: typeof android = android;
// tslint:disable-next-line: typedef
Application.android.on(launchEvent, (args: ApplicationEventData) => {
  myAndroidx.appcompat.app.AppCompatDelegate.setDefaultNightMode(
    myAndroidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO,
  );
});
export function forceLightTheme(): void {
  Theme.setMode(Theme.Light);
}

App_Resources\Android\values-v29\styles.xml :

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Application theme -->
    <style name="AppThemeBase29" parent="AppThemeBase21">
        <item name="android:forceDarkAllowed">false</item>
    </style>

    <style name="AppTheme" parent="AppThemeBase29">
    </style>

</resources>

App_Resources\Android\values\colors.xml :

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="ns_primary">#F5F5F5</color>
    <color name="ns_primaryDark">#F5F5F5</color>
    <color name="ns_accent">#33B5E5</color>
    <color name="ns_blue">#272734</color>
</resources>
NathanaelA commented 3 years ago

@senner007 - as long as you are using Theme v2.3.4 or 3.0.1 it shouldn't happen in production as long as you are importing/including ns-theme during the bootstrap. If you do the on "lauch" to late in the bootstrap, it WON'T actually do anything, which is very annoying of Google...

However, this bootstrap code will be moved into the main framework in NS 8 (because of potential breaking changes). So that it will always run early...

See: https://github.com/NativeScript/theme/blob/master/src/index.js#L25-L27

But you also need to make sure you call the Theme.setMode(...) function so that it will then force the theme to be the NativeScript light theme.

P.S. Also verify in your node_modules/@nativescript/theme/package.json that it actually is v2.3.4+ or do a ns clean

senner007 commented 3 years ago

No sure what you mean by too late. The lauchEvent code runs in main.tns.ts. Is that too late?

I have this in my app.scss

@import "~@nativescript/theme/core.compat";
@import "~@nativescript/theme/blue.compat";

if that what you are referring to?

And yes Im calling the forceLightTheme() in app.module.tns.ts

export class AppModule {
  constructor(private _state: StateService) {
    ...
    forceLightTheme();
    ... 

the node_modules/@nativescript/theme/package.json says : "version": "2.3.4",

NathanaelA commented 3 years ago

@senner007 - That looks correct, not sure why it wouldn't be working all the time for you as long as you are using the latest version of the Theme.

senner007 commented 3 years ago

@NathanaelA - No and its difficult to get it to happen and unfortunately I don't have an Android 10 phone myself.

NathanaelA commented 3 years ago

I did a lot of tests with this on an Android 10 device. I could not get it to fail after the changes, which is why I'm surprised you are seeing any issues.

senner007 commented 3 years ago

@NathanaelA - I repeatedly opened the app, then closed or supended it, did something else on the phone, then opened the app again and once in a while, although rarely, it would open with the dark mode theme.

jalberto-ghub commented 3 years ago

I am experiencing a similar problem when I have the app running on the device and I change the display mode setting to dark-mode (tested on an Samsung Galaxy 9+). The colours will change to dark-mode but Theme.getMode() will report ns-light. It seems that the the two are out of sync. The only way I was able to fix it was by adding the following on main.tns.ts:

// Android Launch Event
if (global.isAndroid) {
    try {
        // Activity Started (app started/booted)
        Application.android.on(
          AndroidApplication.activityStartedEvent,
          (args: ApplicationEventData) => {
            androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode(
              androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
            );
            setTimeout(() => {
                Theme.setMode(Theme.Auto);
                Theme.setMode(Theme.Light);
            }, 500);
            console.log('DarkMode Disabled on Android', Theme.getMode());
          }
        );
    } catch (e) {
      console.log('Error setting up Android Events', e);
    }
}

If you remove setting the mode to Theme.Auto it will not work. Also notice that from starting the app and as you change dark-modes on the settings page while the app is running you will see the following sequence of values for Theme.getMode():

auto, ns-light, ns-light, ns-light, ns-light, ... no matter whether the app is rendering in dark-mode or not. Adding the Theme.setMode(Theme.Auto); seems to get things back in sync.

ishiharas commented 2 years ago

I'm running: "@nativescript/theme": "^3.0.2", "@nativescript/angular": "^12.2.0", "@nativescript/core": "^8.1.5",

I've tried all the solutions, that can be found regarding this topic.

@jalberto-ghub solution came close but wasn't consistent. Probably due the timeout. From time to time the dark mode would appear.

I also tried force-dark-allowed with "ns clean" and rebuilding everything.

I also tried setting the MODE_NIGHT_NO enum in onCreate/onStart in the android activity itself.

The only solution through the various threads, that worked consistently for me, was the following.

forceDarkMode() {
    Theme.setMode(Theme.Auto);
    Theme.setMode(Theme.Light);
}

and called the method in RadSidedrawer's - loaded method.