From ade150b390718c807f27e0cefcce4f56e426b5b7 Mon Sep 17 00:00:00 2001 From: lyzhan7 <78454019+lyzhan7@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:43:40 -0800 Subject: [PATCH 01/11] Support multiple trait collections on native side --- .../ios/FRNAppearanceAdditions.m | 68 +++++++++++-------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m b/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m index 1b785aac70..0f5130aba0 100644 --- a/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m +++ b/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m @@ -3,6 +3,7 @@ #import #import #import +#import NSString *const FRNAppearanceSizeClassCompact = @"compact"; NSString *const FRNAppearanceSizeClassRegular = @"regular"; @@ -67,9 +68,10 @@ @implementation FRNAppearanceAdditions { BOOL _hasListeners; - NSString *_horizontalSizeClass; - NSString *_userInterfaceLevel; - NSString *_accessibilityContrastOption; + + NSMutableDictionary * _rootTagHorizontalSizeClassMap; + NSMutableDictionary * _rootTagUserInterfaceLevelMap; + NSMutableDictionary * _rootTagAccessibilityContrastMap; } + (BOOL)requiresMainQueueSetup { @@ -78,17 +80,17 @@ + (BOOL)requiresMainQueueSetup { RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(horizontalSizeClass) { - return _horizontalSizeClass; + return _rootTagHorizontalSizeClassMap; } RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(userInterfaceLevel) { - return _userInterfaceLevel; + return _rootTagUserInterfaceLevelMap; } RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(accessibilityContrastOption) { - return _accessibilityContrastOption; + return _rootTagAccessibilityContrastMap; } #pragma mark - RCTEventEmitter @@ -105,23 +107,24 @@ - (dispatch_queue_t)methodQueue - (void)startObserving { _hasListeners = YES; - // Note that [UITraitCollection currentTraitCollection] always returns the same default trait collection, - // presumably because FRNAppearanceAdditions isn't a view, so it never gets updated with the right traitCollection - // (which happens when a view gets added to the view hierachy). In order to get the right trait collection, - // we need to access a view that's been added to the view hierarchy - UIViewController *viewControllerWithInitialTraitCollection = RCTPresentedViewController(); - UITraitCollection *initialTraitCollection; + _rootTagHorizontalSizeClassMap = [NSMutableDictionary new]; + _rootTagUserInterfaceLevelMap = [NSMutableDictionary new]; + _rootTagAccessibilityContrastMap = [NSMutableDictionary new]; - if (viewControllerWithInitialTraitCollection != nil) { - initialTraitCollection = [viewControllerWithInitialTraitCollection traitCollection]; - } else { - initialTraitCollection = [UITraitCollection currentTraitCollection]; + for (UIWindow *window in RCTSharedApplication().windows) + { + NSNumber *rootTag = [[[window rootViewController] view] reactTag]; + + if (rootTag != nil) + { + UITraitCollection *windowTraitCollection = [window traitCollection]; + + _rootTagHorizontalSizeClassMap[rootTag] = RCTHorizontalSizeClassPreference(windowTraitCollection); + _rootTagUserInterfaceLevelMap[rootTag] = RCTUserInterfaceLevelPreference(windowTraitCollection); + _rootTagAccessibilityContrastMap[rootTag] = RCTAccessibilityContrastPreference(windowTraitCollection); + } } - _horizontalSizeClass = RCTHorizontalSizeClassPreference(initialTraitCollection); - _userInterfaceLevel = RCTUserInterfaceLevelPreference(initialTraitCollection); - _accessibilityContrastOption = RCTAccessibilityContrastPreference(initialTraitCollection); - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appearanceChanged:) name:RCTUserInterfaceStyleDidChangeNotification @@ -145,18 +148,23 @@ - (void)appearanceChanged:(NSNotification *)notification { NSString *horizontalSizeClass = RCTHorizontalSizeClassPreference(traitCollection); NSString *userInterfaceLevel = RCTUserInterfaceLevelPreference(traitCollection); NSString *accessibilityContrastOption = RCTAccessibilityContrastPreference(traitCollection); - - if (![horizontalSizeClass isEqualToString:_horizontalSizeClass] || - ![userInterfaceLevel isEqualToString:_userInterfaceLevel] || - ![accessibilityContrastOption isEqualToString:_accessibilityContrastOption]) { - _horizontalSizeClass = horizontalSizeClass; - _userInterfaceLevel = userInterfaceLevel; - _accessibilityContrastOption = accessibilityContrastOption; + + NSNumber *rootTag = [[[notification object] contentView] reactTag]; + + if (![horizontalSizeClass isEqualToString: _rootTagHorizontalSizeClassMap[rootTag]] || + ![userInterfaceLevel isEqualToString:_rootTagUserInterfaceLevelMap[rootTag]] || + ![accessibilityContrastOption isEqualToString:_rootTagAccessibilityContrastMap[rootTag]]) { + + _rootTagHorizontalSizeClassMap[rootTag] = horizontalSizeClass; + _rootTagUserInterfaceLevelMap[rootTag] = userInterfaceLevel; + _rootTagAccessibilityContrastMap[rootTag] = accessibilityContrastOption; + [self sendEventWithName:@"appearanceChanged" body:@{ - @"horizontalSizeClass": _horizontalSizeClass, - @"userInterfaceLevel": _userInterfaceLevel, - @"accessibilityContrastOption": _accessibilityContrastOption, + @"rootTag": rootTag, + @"horizontalSizeClass": horizontalSizeClass, + @"userInterfaceLevel": userInterfaceLevel, + @"accessibilityContrastOption": accessibilityContrastOption, }]; } } From 55135d14b238816d25c65d9fe147f3b93e8f7edb Mon Sep 17 00:00:00 2001 From: lyzhan7 <78454019+lyzhan7@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:44:53 -0800 Subject: [PATCH 02/11] Support multiple trait collections on js side --- .../src/NativeAppearanceAdditions.types.ts | 8 +-- .../src/appearanceAdditions.ios.ts | 49 ++++++++++--------- .../src/getSizeClass.ios.ts | 4 +- 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.types.ts b/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.types.ts index db253c34a6..7b19c285b6 100644 --- a/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.types.ts +++ b/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.types.ts @@ -1,9 +1,11 @@ export interface AppearanceAdditions { - readonly horizontalSizeClass: SizeClass; - readonly userInterfaceLevel: UserInterfaceLevel; - readonly accessibilityContrastOption: AccessibilityContrastOption; + horizontalSizeClassForRootTag(rootTag: number): SizeClass; + userInterfaceLevelForRootTag(rootTag: number): UserInterfaceLevel; + accessibilityContrastOptionForRootTag(rootTag: number): AccessibilityContrastOption; } +export const RootTagKey = 'rootTag'; + export const HorizontalSizeClassKey = 'horizontalSizeClass'; export type SizeClass = 'compact' | 'regular'; diff --git a/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ios.ts b/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ios.ts index 01a17633cc..8caf92ed18 100644 --- a/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ios.ts +++ b/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ios.ts @@ -4,37 +4,42 @@ import { memoize } from '@fluentui-react-native/framework'; import NativeAppearanceAdditions from './NativeAppearanceAdditions'; import type { AppearanceAdditions, SizeClass, UserInterfaceLevel, AccessibilityContrastOption } from './NativeAppearanceAdditions.types'; -import { HorizontalSizeClassKey, UserInterfaceLevelKey, AccessibilityContrastOptionKey } from './NativeAppearanceAdditions.types'; +import { + HorizontalSizeClassKey, + UserInterfaceLevelKey, + AccessibilityContrastOptionKey, + RootTagKey, +} from './NativeAppearanceAdditions.types'; class AppearanceAdditionsImpl implements AppearanceAdditions { - _horizontalSizeClass: SizeClass; - _userInterfaceLevel: UserInterfaceLevel; - _accessibilityContrastOption: AccessibilityContrastOption; - - get horizontalSizeClass(): SizeClass { - return this._horizontalSizeClass; - } - - get userInterfaceLevel(): UserInterfaceLevel { - return this._userInterfaceLevel; - } - - get accessibilityContrastOption(): AccessibilityContrastOption { - return this._accessibilityContrastOption; - } + _rootTagHorizontalSizeClassMap: { [key: number]: SizeClass } = {}; + _rootTagUserInterfaceLevelMap: { [key: number]: UserInterfaceLevel } = {}; + _rootTagAccessibilityContrastOptionMap: { [key: number]: AccessibilityContrastOption } = {}; constructor() { - this._horizontalSizeClass = NativeAppearanceAdditions.horizontalSizeClass(); - this._userInterfaceLevel = NativeAppearanceAdditions.userInterfaceLevel(); - this._accessibilityContrastOption = NativeAppearanceAdditions.accessibilityContrastOption(); + this._rootTagHorizontalSizeClassMap = NativeAppearanceAdditions.horizontalSizeClass(); + this._rootTagUserInterfaceLevelMap = NativeAppearanceAdditions.userInterfaceLevel(); + this._rootTagAccessibilityContrastOptionMap = NativeAppearanceAdditions.accessibilityContrastOption(); const eventEmitter = new NativeEventEmitter(NativeAppearanceAdditions as any); eventEmitter.addListener('appearanceChanged', (newValue) => { - this._horizontalSizeClass = newValue[HorizontalSizeClassKey]; - this._userInterfaceLevel = newValue[UserInterfaceLevelKey]; - this._accessibilityContrastOption = newValue[AccessibilityContrastOptionKey]; + const rootTag = newValue[RootTagKey]; + + this._rootTagHorizontalSizeClassMap[rootTag] = newValue[HorizontalSizeClassKey]; + this._rootTagUserInterfaceLevelMap[rootTag] = newValue[UserInterfaceLevelKey]; + this._rootTagAccessibilityContrastOptionMap[rootTag] = newValue[AccessibilityContrastOptionKey]; }); } + + horizontalSizeClassForRootTag(rootTag: number): SizeClass { + return this._rootTagHorizontalSizeClassMap[rootTag]; + } + userInterfaceLevelForRootTag(rootTag: number): UserInterfaceLevel { + return this._rootTagUserInterfaceLevelMap[rootTag]; + } + accessibilityContrastOptionForRootTag(rootTag: number): AccessibilityContrastOption { + return this._rootTagAccessibilityContrastOptionMap[rootTag]; + } } function getAppearanceAdditionsWorker() { diff --git a/packages/experimental/AppearanceAdditions/src/getSizeClass.ios.ts b/packages/experimental/AppearanceAdditions/src/getSizeClass.ios.ts index 13c7708ea2..a823caaab7 100644 --- a/packages/experimental/AppearanceAdditions/src/getSizeClass.ios.ts +++ b/packages/experimental/AppearanceAdditions/src/getSizeClass.ios.ts @@ -9,7 +9,7 @@ import type { SizeClass } from './NativeAppearanceAdditions.types'; const eventEmitter = NativeAppearanceAdditions ? new NativeEventEmitter(NativeAppearanceAdditions as any) : undefined; -export function useHorizontalSizeClass(): SizeClass { +export function useHorizontalSizeClass(rootTag: number): SizeClass { if (!eventEmitter) { return 'regular'; } @@ -18,7 +18,7 @@ export function useHorizontalSizeClass(): SizeClass { // eslint-disable-next-line react-hooks/rules-of-hooks const subscription = useMemo( () => ({ - getCurrentValue: () => appearanceAdditions().horizontalSizeClass, + getCurrentValue: () => appearanceAdditions().horizontalSizeClassForRootTag(rootTag), subscribe: (callback) => { const appearanceSubscription = eventEmitter.addListener('appearanceChanged', callback); return () => { From 43691336f984a3e28e7ba878b20dead30997e36e Mon Sep 17 00:00:00 2001 From: lyzhan7 <78454019+lyzhan7@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:45:09 -0800 Subject: [PATCH 03/11] Update stubs for non-ios --- .../AppearanceAdditions/src/NativeAppearanceAdditions.ts | 8 +++----- .../AppearanceAdditions/src/appearanceAdditions.ts | 6 +----- .../experimental/AppearanceAdditions/src/getSizeClass.ts | 2 +- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.ts b/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.ts index 53d844c4a8..4e7d396b9d 100644 --- a/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.ts +++ b/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.ts @@ -1,5 +1,3 @@ -import type { AccessibilityContrastOption, SizeClass, UserInterfaceLevel } from './NativeAppearanceAdditions.types'; - export const NativeAppearanceAdditions = { // eslint-disable-next-line @typescript-eslint/no-empty-function addListener: (_: string) => {}, @@ -7,15 +5,15 @@ export const NativeAppearanceAdditions = { removeListeners: (_: number) => {}, horizontalSizeClass: () => { console.warn('NativeAppearanceAdditions is only available on iOS'); - return 'regular' as SizeClass; + return {}; }, userInterfaceLevel: () => { console.warn('NativeAppearanceAdditions is only available on iOS'); - return 'base' as UserInterfaceLevel; + return {}; }, accessibilityContrastOption: () => { console.warn('NativeAppearanceAdditions is only available on iOS'); - return 'normal' as AccessibilityContrastOption; + return {}; }, }; diff --git a/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ts b/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ts index 1731b01fcb..40f6f924a5 100644 --- a/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ts +++ b/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ts @@ -4,11 +4,7 @@ import type { AppearanceAdditions } from './NativeAppearanceAdditions.types'; // Default values for non-iOS clients. function getAppearanceAdditionsWorker() { - return { - horizontalSizeClass: 'regular', - userInterfaceLevel: 'base', - accessibilityContrastOption: 'normal', - } as AppearanceAdditions; + return {} as AppearanceAdditions; } export const appearanceAdditions = memoize(getAppearanceAdditionsWorker); diff --git a/packages/experimental/AppearanceAdditions/src/getSizeClass.ts b/packages/experimental/AppearanceAdditions/src/getSizeClass.ts index b34074de2d..d5e308e2da 100644 --- a/packages/experimental/AppearanceAdditions/src/getSizeClass.ts +++ b/packages/experimental/AppearanceAdditions/src/getSizeClass.ts @@ -1,6 +1,6 @@ import type { SizeClass } from './NativeAppearanceAdditions.types'; -export function useHorizontalSizeClass(): SizeClass { +export function useHorizontalSizeClass(_rootTag: number): SizeClass { // Stubbed out for non-iOS platforms return 'regular'; } From 669796fe552eda60a0ec8c9a791224fd7de602c2 Mon Sep 17 00:00:00 2001 From: lyzhan7 <78454019+lyzhan7@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:45:21 -0800 Subject: [PATCH 04/11] Update usages of size class to pass in root tag --- apps/fluent-tester/src/FluentTesterApp.tsx | 5 +++-- packages/components/Notification/src/Notification.tsx | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/fluent-tester/src/FluentTesterApp.tsx b/apps/fluent-tester/src/FluentTesterApp.tsx index 73d1034748..93566389ef 100644 --- a/apps/fluent-tester/src/FluentTesterApp.tsx +++ b/apps/fluent-tester/src/FluentTesterApp.tsx @@ -1,7 +1,7 @@ 'use strict'; import * as React from 'react'; -import { Platform } from 'react-native'; +import { Platform, RootTagContext } from 'react-native'; import { useHorizontalSizeClass } from '@fluentui-react-native/experimental-appearance-additions'; import { ThemeProvider } from '@fluentui-react-native/theme'; @@ -11,7 +11,8 @@ import { FluentTester } from './FluentTester'; import { testerTheme } from './theme/index'; export const FluentTesterApp: React.FunctionComponent = (props) => { - const sizeClass = useHorizontalSizeClass(); + const rootTag = React.useContext(RootTagContext); + const sizeClass = useHorizontalSizeClass(rootTag); const isMobile = Platform.OS === 'android' || (Platform.OS === 'ios' && Platform.isPad === false); // If on iPad we are presented in a Split View or Slide Over context, show the single pane view. diff --git a/packages/components/Notification/src/Notification.tsx b/packages/components/Notification/src/Notification.tsx index 523295ef51..ec1d3eeb28 100644 --- a/packages/components/Notification/src/Notification.tsx +++ b/packages/components/Notification/src/Notification.tsx @@ -1,6 +1,7 @@ /** @jsx withSlots */ +import { useContext } from 'react'; import type { PressableProps, ViewStyle, ViewProps } from 'react-native'; -import { useWindowDimensions, View } from 'react-native'; +import { useWindowDimensions, View, RootTagContext } from 'react-native'; import type { SizeClassIOS } from '@fluentui-react-native/experimental-appearance-additions'; import { useHorizontalSizeClass } from '@fluentui-react-native/experimental-appearance-additions'; @@ -46,7 +47,8 @@ export const Notification = compose({ const Slots = useSlots(userProps, (layer) => notificationLookup(layer, userProps)); const isBar = ['primaryOutlineBar', 'primaryBar', 'neutralBar'].includes(userProps.variant); const width = useWindowDimensions().width / 2; - const sizeClass = useHorizontalSizeClass(); + const rootTag = useContext(RootTagContext); + const sizeClass = useHorizontalSizeClass(rootTag); const onActionPress = userProps.onActionPress; const rootStyle = getRootStyle(isBar, width, sizeClass); From f644ad2c88b3f0f9c03f3e5a305c36de1f636315 Mon Sep 17 00:00:00 2001 From: lyzhan7 <78454019+lyzhan7@users.noreply.github.com> Date: Wed, 1 Mar 2023 18:03:51 -0800 Subject: [PATCH 05/11] Change files --- ...nce-additions-bb61210e-5d2b-4ab0-a20e-97ec5b013b59.json | 7 +++++++ ...-notification-ebe39912-c773-49de-8517-60113b84bffa.json | 7 +++++++ ...native-tester-4d43d5a4-42ae-4171-ae43-7b55188e67dc.json | 7 +++++++ 3 files changed, 21 insertions(+) create mode 100644 change/@fluentui-react-native-experimental-appearance-additions-bb61210e-5d2b-4ab0-a20e-97ec5b013b59.json create mode 100644 change/@fluentui-react-native-notification-ebe39912-c773-49de-8517-60113b84bffa.json create mode 100644 change/@fluentui-react-native-tester-4d43d5a4-42ae-4171-ae43-7b55188e67dc.json diff --git a/change/@fluentui-react-native-experimental-appearance-additions-bb61210e-5d2b-4ab0-a20e-97ec5b013b59.json b/change/@fluentui-react-native-experimental-appearance-additions-bb61210e-5d2b-4ab0-a20e-97ec5b013b59.json new file mode 100644 index 0000000000..292b0e530d --- /dev/null +++ b/change/@fluentui-react-native-experimental-appearance-additions-bb61210e-5d2b-4ab0-a20e-97ec5b013b59.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Support multiple trait collections on js side", + "packageName": "@fluentui-react-native/experimental-appearance-additions", + "email": "78454019+lyzhan7@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-native-notification-ebe39912-c773-49de-8517-60113b84bffa.json b/change/@fluentui-react-native-notification-ebe39912-c773-49de-8517-60113b84bffa.json new file mode 100644 index 0000000000..6dfb358173 --- /dev/null +++ b/change/@fluentui-react-native-notification-ebe39912-c773-49de-8517-60113b84bffa.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Support multiple trait collections on js side", + "packageName": "@fluentui-react-native/notification", + "email": "78454019+lyzhan7@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-native-tester-4d43d5a4-42ae-4171-ae43-7b55188e67dc.json b/change/@fluentui-react-native-tester-4d43d5a4-42ae-4171-ae43-7b55188e67dc.json new file mode 100644 index 0000000000..4137cd28ef --- /dev/null +++ b/change/@fluentui-react-native-tester-4d43d5a4-42ae-4171-ae43-7b55188e67dc.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Support multiple trait collections on js side", + "packageName": "@fluentui-react-native/tester", + "email": "78454019+lyzhan7@users.noreply.github.com", + "dependentChangeType": "patch" +} From c2fe8040758483bc272f1069af148985aa0918f1 Mon Sep 17 00:00:00 2001 From: lyzhan7 <78454019+lyzhan7@users.noreply.github.com> Date: Wed, 8 Mar 2023 12:39:13 -0800 Subject: [PATCH 06/11] Rename some trait collection methods to reflect change to return a map --- .../AppearanceAdditions/ios/FRNAppearanceAdditions.m | 6 +++--- .../AppearanceAdditions/src/NativeAppearanceAdditions.ts | 6 +++--- .../AppearanceAdditions/src/appearanceAdditions.ios.ts | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m b/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m index 0f5130aba0..92907ae941 100644 --- a/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m +++ b/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m @@ -78,17 +78,17 @@ + (BOOL)requiresMainQueueSetup { return YES; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(horizontalSizeClass) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(rootTagHorizontalSizeClassMap) { return _rootTagHorizontalSizeClassMap; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(userInterfaceLevel) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(rootTagUserInterfaceLevelMap) { return _rootTagUserInterfaceLevelMap; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(accessibilityContrastOption) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(rootTagAccessibilityContrastOptionMap) { return _rootTagAccessibilityContrastMap; } diff --git a/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.ts b/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.ts index 4e7d396b9d..2f2ec4bd7c 100644 --- a/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.ts +++ b/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.ts @@ -3,15 +3,15 @@ export const NativeAppearanceAdditions = { addListener: (_: string) => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function removeListeners: (_: number) => {}, - horizontalSizeClass: () => { + rootTagHorizontalSizeClassMap: () => { console.warn('NativeAppearanceAdditions is only available on iOS'); return {}; }, - userInterfaceLevel: () => { + rootTagUserInterfaceLevelMap: () => { console.warn('NativeAppearanceAdditions is only available on iOS'); return {}; }, - accessibilityContrastOption: () => { + rootTagAccessibilityContrastOptionMap: () => { console.warn('NativeAppearanceAdditions is only available on iOS'); return {}; }, diff --git a/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ios.ts b/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ios.ts index 8caf92ed18..3f4f50ba74 100644 --- a/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ios.ts +++ b/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ios.ts @@ -17,9 +17,9 @@ class AppearanceAdditionsImpl implements AppearanceAdditions { _rootTagAccessibilityContrastOptionMap: { [key: number]: AccessibilityContrastOption } = {}; constructor() { - this._rootTagHorizontalSizeClassMap = NativeAppearanceAdditions.horizontalSizeClass(); - this._rootTagUserInterfaceLevelMap = NativeAppearanceAdditions.userInterfaceLevel(); - this._rootTagAccessibilityContrastOptionMap = NativeAppearanceAdditions.accessibilityContrastOption(); + this._rootTagHorizontalSizeClassMap = NativeAppearanceAdditions.rootTagHorizontalSizeClassMap(); + this._rootTagUserInterfaceLevelMap = NativeAppearanceAdditions.rootTagUserInterfaceLevelMap(); + this._rootTagAccessibilityContrastOptionMap = NativeAppearanceAdditions.rootTagAccessibilityContrastOptionMap(); const eventEmitter = new NativeEventEmitter(NativeAppearanceAdditions as any); eventEmitter.addListener('appearanceChanged', (newValue) => { From 630d97bd43bc93f026d834f3a3102554e1a71785 Mon Sep 17 00:00:00 2001 From: lyzhan7 <78454019+lyzhan7@users.noreply.github.com> Date: Wed, 8 Mar 2023 12:42:31 -0800 Subject: [PATCH 07/11] Move rootTag context call into useHorizontalSizeClass hook --- apps/fluent-tester/src/FluentTesterApp.tsx | 5 ++--- packages/components/Notification/src/Notification.tsx | 6 ++---- .../AppearanceAdditions/src/getSizeClass.ios.ts | 9 ++++++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/fluent-tester/src/FluentTesterApp.tsx b/apps/fluent-tester/src/FluentTesterApp.tsx index 93566389ef..73d1034748 100644 --- a/apps/fluent-tester/src/FluentTesterApp.tsx +++ b/apps/fluent-tester/src/FluentTesterApp.tsx @@ -1,7 +1,7 @@ 'use strict'; import * as React from 'react'; -import { Platform, RootTagContext } from 'react-native'; +import { Platform } from 'react-native'; import { useHorizontalSizeClass } from '@fluentui-react-native/experimental-appearance-additions'; import { ThemeProvider } from '@fluentui-react-native/theme'; @@ -11,8 +11,7 @@ import { FluentTester } from './FluentTester'; import { testerTheme } from './theme/index'; export const FluentTesterApp: React.FunctionComponent = (props) => { - const rootTag = React.useContext(RootTagContext); - const sizeClass = useHorizontalSizeClass(rootTag); + const sizeClass = useHorizontalSizeClass(); const isMobile = Platform.OS === 'android' || (Platform.OS === 'ios' && Platform.isPad === false); // If on iPad we are presented in a Split View or Slide Over context, show the single pane view. diff --git a/packages/components/Notification/src/Notification.tsx b/packages/components/Notification/src/Notification.tsx index ec1d3eeb28..523295ef51 100644 --- a/packages/components/Notification/src/Notification.tsx +++ b/packages/components/Notification/src/Notification.tsx @@ -1,7 +1,6 @@ /** @jsx withSlots */ -import { useContext } from 'react'; import type { PressableProps, ViewStyle, ViewProps } from 'react-native'; -import { useWindowDimensions, View, RootTagContext } from 'react-native'; +import { useWindowDimensions, View } from 'react-native'; import type { SizeClassIOS } from '@fluentui-react-native/experimental-appearance-additions'; import { useHorizontalSizeClass } from '@fluentui-react-native/experimental-appearance-additions'; @@ -47,8 +46,7 @@ export const Notification = compose({ const Slots = useSlots(userProps, (layer) => notificationLookup(layer, userProps)); const isBar = ['primaryOutlineBar', 'primaryBar', 'neutralBar'].includes(userProps.variant); const width = useWindowDimensions().width / 2; - const rootTag = useContext(RootTagContext); - const sizeClass = useHorizontalSizeClass(rootTag); + const sizeClass = useHorizontalSizeClass(); const onActionPress = userProps.onActionPress; const rootStyle = getRootStyle(isBar, width, sizeClass); diff --git a/packages/experimental/AppearanceAdditions/src/getSizeClass.ios.ts b/packages/experimental/AppearanceAdditions/src/getSizeClass.ios.ts index a823caaab7..e7599e6351 100644 --- a/packages/experimental/AppearanceAdditions/src/getSizeClass.ios.ts +++ b/packages/experimental/AppearanceAdditions/src/getSizeClass.ios.ts @@ -1,5 +1,6 @@ +import React from 'react'; import { useMemo } from 'react'; -import { NativeEventEmitter } from 'react-native'; +import { NativeEventEmitter, RootTagContext } from 'react-native'; import { useSubscription } from 'use-subscription'; @@ -9,7 +10,9 @@ import type { SizeClass } from './NativeAppearanceAdditions.types'; const eventEmitter = NativeAppearanceAdditions ? new NativeEventEmitter(NativeAppearanceAdditions as any) : undefined; -export function useHorizontalSizeClass(rootTag: number): SizeClass { +export function useHorizontalSizeClass(_rootTag: number): SizeClass { + const rootTag = React.useContext(RootTagContext); + if (!eventEmitter) { return 'regular'; } @@ -26,7 +29,7 @@ export function useHorizontalSizeClass(rootTag: number): SizeClass { }; }, }), - [], + [rootTag], ); // Early return on eventEmitter will either always or never return within a single instance From 042c058f18281736c96e0085985b52003163b3cb Mon Sep 17 00:00:00 2001 From: lyzhan7 <78454019+lyzhan7@users.noreply.github.com> Date: Wed, 8 Mar 2023 13:00:39 -0800 Subject: [PATCH 08/11] Add class verification for rootview --- .../AppearanceAdditions/ios/FRNAppearanceAdditions.m | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m b/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m index 92907ae941..8874dec2f7 100644 --- a/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m +++ b/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m @@ -4,6 +4,7 @@ #import #import #import +#import NSString *const FRNAppearanceSizeClassCompact = @"compact"; NSString *const FRNAppearanceSizeClassRegular = @"regular"; @@ -142,14 +143,19 @@ - (void)appearanceChanged:(NSNotification *)notification { if (_hasListeners) { UITraitCollection *traitCollection = [[notification userInfo] valueForKey:RCTUserInterfaceStyleDidChangeNotificationTraitCollectionKey]; if (![traitCollection isKindOfClass:[UITraitCollection class]]) { - traitCollection = nil; + return; + } + + RCTRootView *rootView = [notification object]; + if (![rootView isKindOfClass:[RCTRootView class]]) { + return; } NSString *horizontalSizeClass = RCTHorizontalSizeClassPreference(traitCollection); NSString *userInterfaceLevel = RCTUserInterfaceLevelPreference(traitCollection); NSString *accessibilityContrastOption = RCTAccessibilityContrastPreference(traitCollection); - NSNumber *rootTag = [[[notification object] contentView] reactTag]; + NSNumber *rootTag = [[rootView contentView] reactTag]; if (![horizontalSizeClass isEqualToString: _rootTagHorizontalSizeClassMap[rootTag]] || ![userInterfaceLevel isEqualToString:_rootTagUserInterfaceLevelMap[rootTag]] || From 3ee6ac4f6a3e9ce265e9dbfa0b8c7afda14d08d7 Mon Sep 17 00:00:00 2001 From: lyzhan7 <78454019+lyzhan7@users.noreply.github.com> Date: Wed, 8 Mar 2023 13:05:25 -0800 Subject: [PATCH 09/11] Update rootTag type from number to RootTag --- .../src/NativeAppearanceAdditions.types.ts | 8 +++++--- .../AppearanceAdditions/src/appearanceAdditions.ios.ts | 7 ++++--- .../AppearanceAdditions/src/getSizeClass.ios.ts | 3 ++- .../experimental/AppearanceAdditions/src/getSizeClass.ts | 4 +++- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.types.ts b/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.types.ts index 7b19c285b6..c7ba09468a 100644 --- a/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.types.ts +++ b/packages/experimental/AppearanceAdditions/src/NativeAppearanceAdditions.types.ts @@ -1,7 +1,9 @@ +import type { RootTag } from 'react-native'; + export interface AppearanceAdditions { - horizontalSizeClassForRootTag(rootTag: number): SizeClass; - userInterfaceLevelForRootTag(rootTag: number): UserInterfaceLevel; - accessibilityContrastOptionForRootTag(rootTag: number): AccessibilityContrastOption; + horizontalSizeClassForRootTag(rootTag: RootTag): SizeClass; + userInterfaceLevelForRootTag(rootTag: RootTag): UserInterfaceLevel; + accessibilityContrastOptionForRootTag(rootTag: RootTag): AccessibilityContrastOption; } export const RootTagKey = 'rootTag'; diff --git a/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ios.ts b/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ios.ts index 3f4f50ba74..2b01439b9c 100644 --- a/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ios.ts +++ b/packages/experimental/AppearanceAdditions/src/appearanceAdditions.ios.ts @@ -1,4 +1,5 @@ import { NativeEventEmitter } from 'react-native'; +import type { RootTag } from 'react-native'; import { memoize } from '@fluentui-react-native/framework'; @@ -31,13 +32,13 @@ class AppearanceAdditionsImpl implements AppearanceAdditions { }); } - horizontalSizeClassForRootTag(rootTag: number): SizeClass { + horizontalSizeClassForRootTag(rootTag: RootTag): SizeClass { return this._rootTagHorizontalSizeClassMap[rootTag]; } - userInterfaceLevelForRootTag(rootTag: number): UserInterfaceLevel { + userInterfaceLevelForRootTag(rootTag: RootTag): UserInterfaceLevel { return this._rootTagUserInterfaceLevelMap[rootTag]; } - accessibilityContrastOptionForRootTag(rootTag: number): AccessibilityContrastOption { + accessibilityContrastOptionForRootTag(rootTag: RootTag): AccessibilityContrastOption { return this._rootTagAccessibilityContrastOptionMap[rootTag]; } } diff --git a/packages/experimental/AppearanceAdditions/src/getSizeClass.ios.ts b/packages/experimental/AppearanceAdditions/src/getSizeClass.ios.ts index e7599e6351..93f96b7997 100644 --- a/packages/experimental/AppearanceAdditions/src/getSizeClass.ios.ts +++ b/packages/experimental/AppearanceAdditions/src/getSizeClass.ios.ts @@ -1,6 +1,7 @@ import React from 'react'; import { useMemo } from 'react'; import { NativeEventEmitter, RootTagContext } from 'react-native'; +import type { RootTag } from 'react-native'; import { useSubscription } from 'use-subscription'; @@ -10,7 +11,7 @@ import type { SizeClass } from './NativeAppearanceAdditions.types'; const eventEmitter = NativeAppearanceAdditions ? new NativeEventEmitter(NativeAppearanceAdditions as any) : undefined; -export function useHorizontalSizeClass(_rootTag: number): SizeClass { +export function useHorizontalSizeClass(_rootTag: RootTag): SizeClass { const rootTag = React.useContext(RootTagContext); if (!eventEmitter) { diff --git a/packages/experimental/AppearanceAdditions/src/getSizeClass.ts b/packages/experimental/AppearanceAdditions/src/getSizeClass.ts index d5e308e2da..e7a0eb6309 100644 --- a/packages/experimental/AppearanceAdditions/src/getSizeClass.ts +++ b/packages/experimental/AppearanceAdditions/src/getSizeClass.ts @@ -1,6 +1,8 @@ +import type { RootTag } from 'react-native'; + import type { SizeClass } from './NativeAppearanceAdditions.types'; -export function useHorizontalSizeClass(_rootTag: number): SizeClass { +export function useHorizontalSizeClass(_rootTag: RootTag): SizeClass { // Stubbed out for non-iOS platforms return 'regular'; } From 98349f71f9aa096daee5feb16c9a916d2cafb85c Mon Sep 17 00:00:00 2001 From: lyzhan7 <78454019+lyzhan7@users.noreply.github.com> Date: Wed, 8 Mar 2023 13:10:30 -0800 Subject: [PATCH 10/11] Move curly braces up (not on new line) --- .../ios/FRNAppearanceAdditions.m | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m b/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m index 8874dec2f7..29038d8708 100644 --- a/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m +++ b/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m @@ -79,18 +79,15 @@ + (BOOL)requiresMainQueueSetup { return YES; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(rootTagHorizontalSizeClassMap) -{ +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(rootTagHorizontalSizeClassMap) { return _rootTagHorizontalSizeClassMap; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(rootTagUserInterfaceLevelMap) -{ +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(rootTagUserInterfaceLevelMap) { return _rootTagUserInterfaceLevelMap; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(rootTagAccessibilityContrastOptionMap) -{ +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(rootTagAccessibilityContrastOptionMap) { return _rootTagAccessibilityContrastMap; } @@ -100,8 +97,7 @@ + (BOOL)requiresMainQueueSetup { return @[ @"appearanceChanged" ]; } -- (dispatch_queue_t)methodQueue -{ +- (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); } @@ -112,12 +108,10 @@ - (void)startObserving { _rootTagUserInterfaceLevelMap = [NSMutableDictionary new]; _rootTagAccessibilityContrastMap = [NSMutableDictionary new]; - for (UIWindow *window in RCTSharedApplication().windows) - { + for (UIWindow *window in RCTSharedApplication().windows) { NSNumber *rootTag = [[[window rootViewController] view] reactTag]; - if (rootTag != nil) - { + if (rootTag != nil) { UITraitCollection *windowTraitCollection = [window traitCollection]; _rootTagHorizontalSizeClassMap[rootTag] = RCTHorizontalSizeClassPreference(windowTraitCollection); From 487275a0cb3008d2f2db20562b3b022f9ca7fc7f Mon Sep 17 00:00:00 2001 From: lyzhan7 <78454019+lyzhan7@users.noreply.github.com> Date: Wed, 8 Mar 2023 13:42:41 -0800 Subject: [PATCH 11/11] Replace type of rootTag, from NSNumber to id --- .../AppearanceAdditions/ios/FRNAppearanceAdditions.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m b/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m index 29038d8708..9e57dcfd64 100644 --- a/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m +++ b/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m @@ -70,9 +70,9 @@ @implementation FRNAppearanceAdditions { BOOL _hasListeners; - NSMutableDictionary * _rootTagHorizontalSizeClassMap; - NSMutableDictionary * _rootTagUserInterfaceLevelMap; - NSMutableDictionary * _rootTagAccessibilityContrastMap; + NSMutableDictionary, NSString *> * _rootTagHorizontalSizeClassMap; + NSMutableDictionary, NSString *> * _rootTagUserInterfaceLevelMap; + NSMutableDictionary, NSString *> * _rootTagAccessibilityContrastMap; } + (BOOL)requiresMainQueueSetup { @@ -109,7 +109,7 @@ - (void)startObserving { _rootTagAccessibilityContrastMap = [NSMutableDictionary new]; for (UIWindow *window in RCTSharedApplication().windows) { - NSNumber *rootTag = [[[window rootViewController] view] reactTag]; + id rootTag = [[[window rootViewController] view] reactTag]; if (rootTag != nil) { UITraitCollection *windowTraitCollection = [window traitCollection]; @@ -149,7 +149,7 @@ - (void)appearanceChanged:(NSNotification *)notification { NSString *userInterfaceLevel = RCTUserInterfaceLevelPreference(traitCollection); NSString *accessibilityContrastOption = RCTAccessibilityContrastPreference(traitCollection); - NSNumber *rootTag = [[rootView contentView] reactTag]; + id rootTag = [[rootView contentView] reactTag]; if (![horizontalSizeClass isEqualToString: _rootTagHorizontalSizeClassMap[rootTag]] || ![userInterfaceLevel isEqualToString:_rootTagUserInterfaceLevelMap[rootTag]] ||