Skip to content

Commit

Permalink
ios dark mode detection (keybase#19340)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisnojima authored Sep 10, 2019
1 parent 838c602 commit f6b30d7
Show file tree
Hide file tree
Showing 16 changed files with 300 additions and 48 deletions.
20 changes: 20 additions & 0 deletions shared/actions/platform-specific/index.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
Alert,
Linking,
NativeModules,
NativeEventEmitter,
ActionSheetIOS,
CameraRoll,
PermissionsAndroid,
Expand Down Expand Up @@ -613,6 +614,24 @@ const getE164 = (phoneNumber: string, countryCode?: string) => {
}
}

function* setupDarkMode() {
const NativeAppearance = NativeModules.Appearance
if (NativeAppearance) {
const channel = Saga.eventChannel(emitter => {
const nativeEventEmitter = new NativeEventEmitter(NativeAppearance)
nativeEventEmitter.addListener('appearanceChanged', ({colorScheme}) => {
emitter(colorScheme)
})
return () => {}
}, Saga.buffers.sliding(1))

while (true) {
const mode = yield Saga.take(channel)
yield Saga.put(ConfigGen.createSetSystemDarkMode({dark: mode === 'dark'}))
}
}
}

export function* platformConfigSaga(): Saga.SagaGenerator<any, any> {
yield* Saga.chainGenerator<ConfigGen.PersistRoutePayload>(ConfigGen.persistRoute, persistRoute)
yield* Saga.chainAction2(ConfigGen.mobileAppState, updateChangedFocus)
Expand Down Expand Up @@ -646,4 +665,5 @@ export function* platformConfigSaga(): Saga.SagaGenerator<any, any> {
yield Saga.spawn(loadStartupDetails)
yield Saga.spawn(pushSaga)
yield Saga.spawn(setupNetInfoWatcher)
yield Saga.spawn(setupDarkMode)
}
2 changes: 1 addition & 1 deletion shared/chat/inbox/container/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class InboxWrapper extends React.PureComponent<Props> {
static navigationOptions = {
header: undefined,
headerRight: <HeaderNewChatButton />,
headerTitle: (
headerTitle: () => (
<Kb.Text type="BodyBig" lineClamp={1}>
{' '}
Chats{' '}
Expand Down
33 changes: 16 additions & 17 deletions shared/common-adapters/text.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,28 @@ import openURL from '../util/open-url'
import {fontSizeToSizeStyle, lineClamp, metaData} from './text.meta.native'
import * as Styles from '../styles'
import shallowEqual from 'shallowequal'
import {NativeClipboard, NativeText, NativeStyleSheet, NativeAlert} from './native-wrappers.native'
import {NativeClipboard, NativeText, NativeAlert} from './native-wrappers.native'
import {Props, TextType} from './text'

const modes = ['positive', 'negative']

const styleMap = Object.keys(metaData()).reduce<{[key: string]: Styles.StylesCrossPlatform}>(
(map, type) => {
const meta = metaData()[type as TextType]
modes.forEach(mode => {
map[`${type}:${mode}`] = {
...fontSizeToSizeStyle(meta.fontSize),
color: meta.colorForBackground[mode] || Styles.globalColors.black,
...meta.styleOverride,
}
})
return map
},
{center: {textAlign: 'center'}}
const styles = Styles.styleSheetCreate(() =>
Object.keys(metaData()).reduce<{[key: string]: Styles.StylesCrossPlatform}>(
(map, type) => {
const meta = metaData()[type as TextType]
modes.forEach(mode => {
map[`${type}:${mode}`] = {
...fontSizeToSizeStyle(meta.fontSize),
color: meta.colorForBackground[mode] || Styles.globalColors.black,
...meta.styleOverride,
}
})
return map
},
{center: {textAlign: 'center'}}
)
)

// @ts-ignore TODO fix styles
const styles = NativeStyleSheet.create(styleMap)

// Init common styles for perf

class Text extends Component<Props> {
Expand Down
3 changes: 2 additions & 1 deletion shared/desktop/renderer/main.desktop.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Entry point to the chrome part of the app: ORDER IS IMPORTANT
import '../../util/user-timings'
import 'react-hot-loader'
import {_setSystemIsDarkMode} from '../../styles/dark-mode'
import {_setSystemIsDarkMode, _setSystemSupported} from '../../styles/dark-mode'
import {isDarwin} from '../../constants/platform'
import * as SafeElectron from '../../util/safe-electron.desktop'

_setSystemIsDarkMode(isDarwin && SafeElectron.getSystemPreferences().isDarkMode())
_setSystemSupported(isDarwin)
require('./main2.desktop')
8 changes: 8 additions & 0 deletions shared/index.ios.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
// @flow
// React-native tooling assumes this file is here, so we just require our real entry point
import './app/globals.native'
import {NativeModules} from 'react-native'
import {_setSystemIsDarkMode, _setSystemSupported} from './styles/dark-mode'

// Load storybook or the app
if (__STORYBOOK__) {
const load = require('./storybook/index.native').default
load()
} else {
const NativeAppearance = NativeModules.Appearance

if (NativeAppearance) {
_setSystemIsDarkMode(NativeAppearance.initialColorScheme === 'dark')
_setSystemSupported(NativeAppearance.supported === '1')
}
const {load} = require('./app/index.native')
load()
}
12 changes: 12 additions & 0 deletions shared/ios/Keybase.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
DB0846051BA75DC800F54960 /* keybase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB0846041BA75DC800F54960 /* keybase.framework */; };
DB27DE571D747A6C003DE8BC /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = DB27DE561D747A6C003DE8BC /* Utils.m */; };
DB59F9B72238A27E00E271C1 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB59F97B2238A27E00E271C1 /* JavaScriptCore.framework */; };
DB7C1EB62326A5F000A47A9E /* Appearance.m in Sources */ = {isa = PBXBuildFile; fileRef = DB7C1EB52326A5F000A47A9E /* Appearance.m */; };
DB7C1EBA2326AA3F00A47A9E /* AppearanceRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = DB7C1EB92326AA3F00A47A9E /* AppearanceRootView.m */; };
DBBCC6131D9C4A8100312652 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DBBCC6121D9C4A8100312652 /* AppDelegate.m */; };
DBBCC6181D9C5C6A00312652 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DBBCC6171D9C5C6A00312652 /* main.m */; };
DBCE36251B90F14800ECADDE /* Engine.m in Sources */ = {isa = PBXBuildFile; fileRef = DBCE36241B90F14800ECADDE /* Engine.m */; };
Expand Down Expand Up @@ -159,6 +161,10 @@
DB27DE551D747A6B003DE8BC /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = "<group>"; };
DB27DE561D747A6C003DE8BC /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utils.m; sourceTree = "<group>"; };
DB59F97B2238A27E00E271C1 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
DB7C1EB52326A5F000A47A9E /* Appearance.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Appearance.m; sourceTree = "<group>"; };
DB7C1EB72326A5FF00A47A9E /* Appearance.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Appearance.h; sourceTree = "<group>"; };
DB7C1EB82326AA3F00A47A9E /* AppearanceRootView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppearanceRootView.h; sourceTree = "<group>"; };
DB7C1EB92326AA3F00A47A9E /* AppearanceRootView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppearanceRootView.m; sourceTree = "<group>"; };
DBBCC6111D9C4A8100312652 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
DBBCC6121D9C4A8100312652 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
DBBCC6171D9C5C6A00312652 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -356,6 +362,10 @@
DBCE36231B90F14800ECADDE /* Engine.h */,
DBCE36241B90F14800ECADDE /* Engine.m */,
DBBCC6171D9C5C6A00312652 /* main.m */,
DB7C1EB72326A5FF00A47A9E /* Appearance.h */,
DB7C1EB52326A5F000A47A9E /* Appearance.m */,
DB7C1EB82326AA3F00A47A9E /* AppearanceRootView.h */,
DB7C1EB92326AA3F00A47A9E /* AppearanceRootView.m */,
DB27DE551D747A6B003DE8BC /* Utils.h */,
DB27DE561D747A6C003DE8BC /* Utils.m */,
A969A4161CF65CB200F273F2 /* LogSend.h */,
Expand Down Expand Up @@ -625,7 +635,9 @@
DBEE30831FE8511F0020EBA5 /* DDLog.m in Sources */,
DBEE30811FE8511F0020EBA5 /* DDOSLogger.m in Sources */,
27EE26A820A140EF00F92CD1 /* Pusher.m in Sources */,
DB7C1EB62326A5F000A47A9E /* Appearance.m in Sources */,
A90CD4A81FCE1D0B00F97901 /* NativeLogger.m in Sources */,
DB7C1EBA2326AA3F00A47A9E /* AppearanceRootView.m in Sources */,
DBCE36251B90F14800ECADDE /* Engine.m in Sources */,
DBBCC6131D9C4A8100312652 /* AppDelegate.m in Sources */,
375CA6651FFD40D300210B11 /* Storybook.m in Sources */,
Expand Down
4 changes: 2 additions & 2 deletions shared/ios/Keybase/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#import <AVFoundation/AVFoundation.h>
#import <React/RCTPushNotificationManager.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import "AppearanceRootView.h"
#import "Engine.h"
#import "LogSend.h"
#import <React/RCTLinkingManager.h>
Expand Down Expand Up @@ -71,7 +71,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider: [[UMModuleRegistryProvider alloc] init]];

RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
AppearanceRootView *rootView = [[AppearanceRootView alloc] initWithBridge:bridge
moduleName:@"Keybase"
initialProperties:nil];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
Expand Down
22 changes: 22 additions & 0 deletions shared/ios/Keybase/Appearance.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// A placeholder implementation for RN's https://github.com/facebook/react-native/commit/63fa3f21c5ab308def450bffb22054241a8842ef
// Appearance.h
// Keybase
//
// Created by Chris Nojima on 9/9/19.
// Copyright © 2019 Keybase. All rights reserved.
//

#ifndef Appearance_h
#define Appearance_h

#import <UIKit/UIKit.h>

#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

NSString *const RCTUserInterfaceStyleDidChangeNotification = @"RCTUserInterfaceStyleDidChangeNotification";

@interface Appearance : RCTEventEmitter <RCTBridgeModule>
@end

#endif /* Appearance_h */
106 changes: 106 additions & 0 deletions shared/ios/Keybase/Appearance.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// Appearance.m
// Keybase
//
// Created by Chris Nojima on 9/9/19.
// Copyright © 2019 Keybase. All rights reserved.
//

#import "Appearance.h"

#import <React/RCTEventEmitter.h>

NSString *const RCTAppearanceColorSchemeLight = @"light";
NSString *const RCTAppearanceColorSchemeDark = @"dark";

static NSString *RCTColorSchemePreference(UITraitCollection *traitCollection)
{
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
if (@available(iOS 13.0, *)) {
static NSDictionary *appearances;
static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{
appearances = @{
@(UIUserInterfaceStyleLight): RCTAppearanceColorSchemeLight,
@(UIUserInterfaceStyleDark): RCTAppearanceColorSchemeDark
};
});

traitCollection = traitCollection ?: [UITraitCollection currentTraitCollection];
return appearances[@(traitCollection.userInterfaceStyle)] ?: nil;
}
#endif

return nil;
}

@interface Appearance ()
@end

@implementation Appearance

RCT_EXPORT_MODULE(Appearance)

+ (BOOL)requiresMainQueueSetup
{
return YES;
}

- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}

RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSString *, getColorScheme)
{
return RCTColorSchemePreference(nil);
}

- (void)appearanceChanged:(NSNotification *)notification
{
NSDictionary *userInfo = [notification userInfo];
UITraitCollection *traitCollection = nil;
if (userInfo) {
traitCollection = userInfo[@"traitCollection"];
}
[self sendEventWithName:@"appearanceChanged" body:@{@"colorScheme": RCTColorSchemePreference(traitCollection)}];
}

#pragma mark - RCTEventEmitter

- (NSArray<NSString *> *)supportedEvents
{
return @[@"appearanceChanged"];
}

- (void)startObserving
{
if (@available(iOS 13.0, *)) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appearanceChanged:)
name:RCTUserInterfaceStyleDidChangeNotification
object:nil];
}
}

- (void)stopObserving
{
if (@available(iOS 13.0, *)) {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
}

- (NSDictionary *)constantsToExport {
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
return @{ @"initialColorScheme": RCTColorSchemePreference(nil),
@"supported": @"1"
};
#else
return @{ @"initialColorScheme": @"light",
@"supported": @"0"
};
#endif
}

@end
17 changes: 17 additions & 0 deletions shared/ios/Keybase/AppearanceRootView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// AppearanceRootView.h
// Keybase
//
// Created by Chris Nojima on 9/9/19.
// Copyright © 2019 Keybase. All rights reserved.
//

#import <React/RCTRootView.h>

NS_ASSUME_NONNULL_BEGIN

@interface AppearanceRootView : RCTRootView

@end

NS_ASSUME_NONNULL_END
30 changes: 30 additions & 0 deletions shared/ios/Keybase/AppearanceRootView.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// AppearanceRootView.m
// Keybase
//
// Created by Chris Nojima on 9/9/19.
// Copyright © 2019 Keybase. All rights reserved.
//

#import "AppearanceRootView.h"

@implementation AppearanceRootView

#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
{
NSString * RCTUserInterfaceStyleDidChangeNotification = @"RCTUserInterfaceStyleDidChangeNotification";
[super traitCollectionDidChange:previousTraitCollection];

if (@available(iOS 13.0, *)) {
if ([previousTraitCollection hasDifferentColorAppearanceComparedToTraitCollection:self.traitCollection]) {
[[NSNotificationCenter defaultCenter] postNotificationName:RCTUserInterfaceStyleDidChangeNotification
object:self
userInfo:@{@"traitCollection": self.traitCollection}];
}
}
}
#endif

@end
Loading

0 comments on commit f6b30d7

Please sign in to comment.