-
Notifications
You must be signed in to change notification settings - Fork 24.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
iOS: Add Appearance TurboModule, useColorScheme hook [DO NOT MERGE] #26143
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* @format | ||
* @flow | ||
*/ | ||
|
||
'use strict'; | ||
|
||
import EventEmitter from '../vendor/emitter/EventEmitter'; | ||
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter'; | ||
import NativeAppearance, {type AppearancePreferences} from './NativeAppearance'; | ||
import invariant from 'invariant'; | ||
|
||
const COLOR_SCHEME_NAME = { | ||
light: 'light', | ||
dark: 'dark', | ||
noPreference: 'no-preference', | ||
}; | ||
type ColorSchemeName = $Keys<{ | ||
light: string, | ||
dark: string, | ||
noPreference: string, | ||
}>; | ||
type AppearanceListener = (preferences: AppearancePreferences) => void; | ||
|
||
const eventEmitter = new EventEmitter(); | ||
|
||
let appearancePreferencesInitialized = false; | ||
let appearancePreferences: AppearancePreferences; | ||
|
||
class Appearance { | ||
/** | ||
* Note: Although appearance is available immediately, it may change (e.g | ||
* Dark Mode) so any rendering logic or styles that depend on this should try | ||
* to call this function on every render, rather than caching the value (for | ||
* example, using inline styles rather than setting a value in a | ||
* `StyleSheet`). | ||
* | ||
* Example: `const colorScheme = Appearance.get('colorScheme');` | ||
* | ||
* @param {string} preference Name of preference (e.g. 'colorScheme'). | ||
* @returns {ColorSchemeName} Value for the preference. | ||
*/ | ||
static get(preference: string): ColorSchemeName { | ||
invariant( | ||
appearancePreferences[preference], | ||
'No preference set for key ' + preference, | ||
); | ||
return appearancePreferences[preference]; | ||
} | ||
|
||
/** | ||
* This should only be called from native code by sending the | ||
* appearanceChanged event. | ||
* | ||
* @param {object} appearancePreferences Simple string-keyed object of | ||
* appearance preferences to set. | ||
*/ | ||
static set(preferences: AppearancePreferences): void { | ||
let {colorScheme} = preferences; | ||
appearancePreferences = {colorScheme}; | ||
|
||
if (appearancePreferencesInitialized) { | ||
// Don't fire 'change' the first time the dimensions are set. | ||
eventEmitter.emit('change', preferences); | ||
} else { | ||
appearancePreferencesInitialized = true; | ||
} | ||
} | ||
|
||
/** | ||
* Add an event handler that is fired when appearance preferences change. | ||
*/ | ||
static addChangeListener(listener: AppearanceListener): void { | ||
eventEmitter.addListener('change', listener); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should return the result of this, an |
||
} | ||
|
||
/** | ||
* Remove an event handler. | ||
*/ | ||
static removeChangeListener(listener: AppearanceListener): void { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should get rid of this function |
||
eventEmitter.removeListener('change', listener); | ||
} | ||
} | ||
|
||
if (NativeAppearance) { | ||
const nativeEventEmitter = new NativeEventEmitter(NativeAppearance); | ||
// Subscribe before calling to make sure we don't miss any updates in between. | ||
nativeEventEmitter.addListener( | ||
'appearanceChanged', | ||
(newAppearance: AppearancePreferences) => { | ||
Appearance.set(newAppearance); | ||
}, | ||
); | ||
Appearance.set(NativeAppearance.getPreferences()); | ||
} else { | ||
Appearance.set({colorScheme: 'no-preference'}); | ||
} | ||
|
||
module.exports = Appearance; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
|
||
'use strict'; | ||
|
||
import type {TurboModule} from '../TurboModule/RCTExport'; | ||
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; | ||
|
||
export type AppearancePreferences = {| | ||
colorScheme?: string, | ||
|}; | ||
|
||
export interface Spec extends TurboModule { | ||
+getPreferences: () => AppearancePreferences; | ||
|
||
// RCTEventEmitter | ||
+addListener: (eventName: string) => void; | ||
+removeListeners: (count: number) => void; | ||
} | ||
|
||
export default (TurboModuleRegistry.get<Spec>('Appearance'): ?Spec); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* @format | ||
* @flow | ||
*/ | ||
|
||
'use strict'; | ||
|
||
import {useMemo} from 'react'; | ||
import {useSubscription} from 'use-subscription'; | ||
import Appearance from './Appearance'; | ||
|
||
export default function useColorScheme() { | ||
const subscription = useMemo( | ||
() => ({ | ||
getCurrentValue: () => Appearance.get('colorScheme'), | ||
subscribe: callback => { | ||
Appearance.addChangeListener(callback); | ||
return () => Appearance.removeChangeListener(callback); | ||
}, | ||
}), | ||
[], | ||
); | ||
|
||
return useSubscription(subscription); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/dimensions/appearance preferences/