@q9labsai/chalk-react-native 4.1.11 → 4.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/android.js +4 -2
  2. package/dist/{chunk-MMBE2BVK.js → chunk-4NLA35HE.js} +649 -407
  3. package/dist/{chunk-LZ67SXGB.js → chunk-MK6OO4V7.js} +6 -0
  4. package/dist/client.js +1 -1
  5. package/dist/components/Entrance.d.ts.map +1 -1
  6. package/dist/components/logo/Logo.d.ts +11 -0
  7. package/dist/components/logo/Logo.d.ts.map +1 -0
  8. package/dist/components/native-space-view/SpaceTopBar.android.d.ts.map +1 -1
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +4 -2
  12. package/dist/ios-pad.js +4 -2
  13. package/dist/ios-phone.js +4 -2
  14. package/dist/ios.js +4 -2
  15. package/dist/macos.js +4 -2
  16. package/package.json +4 -4
  17. package/src/callkit/callkit.test.ts +12 -10
  18. package/src/components/Entrance.tsx +2 -1
  19. package/src/components/logo/Logo.tsx +309 -0
  20. package/src/components/native-space-view/SpaceTopBar.android.tsx +2 -1
  21. package/src/index.ts +2 -0
  22. package/src/space-client/create-native-space-client.ts +8 -0
  23. package/dist/components/LogoElements.d.ts +0 -6
  24. package/dist/components/LogoElements.d.ts.map +0 -1
  25. package/dist/test-support/test-renderer.d.ts +0 -20
  26. package/dist/test-support/test-renderer.d.ts.map +0 -1
  27. package/src/android/connection-service.test.ts +0 -114
  28. package/src/callkit/resolve-space-callkit-options.test.ts +0 -68
  29. package/src/client.test.ts +0 -12
  30. package/src/clipboard.test.ts +0 -79
  31. package/src/components/Chalk.test.tsx +0 -223
  32. package/src/components/EmbeddedWhiteboard.test.tsx +0 -92
  33. package/src/components/Entrance.test.tsx +0 -19
  34. package/src/components/EntranceView.android.test.tsx +0 -100
  35. package/src/components/EntranceView.ios-pad.test.tsx +0 -160
  36. package/src/components/EntranceView.ios-phone.test.tsx +0 -137
  37. package/src/components/EntranceView.test.tsx +0 -15
  38. package/src/components/LogoElements.test.tsx +0 -60
  39. package/src/components/LogoElements.tsx +0 -119
  40. package/src/components/ReactionPicker.test.tsx +0 -19
  41. package/src/components/RtcVideoView.test.tsx +0 -12
  42. package/src/components/SpaceView.android.test.tsx +0 -11
  43. package/src/components/SpaceView.ios-pad.test.tsx +0 -11
  44. package/src/components/SpaceView.ios-phone.test.tsx +0 -11
  45. package/src/components/SpaceView.macos.test.tsx +0 -11
  46. package/src/components/SpaceView.shared.test.tsx +0 -116
  47. package/src/components/SpaceView.test.tsx +0 -15
  48. package/src/components/android-connection-service-controller.test.ts +0 -80
  49. package/src/components/episode-diagnostic-render-observer.test.ts +0 -24
  50. package/src/components/native-animation-controller.test.ts +0 -47
  51. package/src/components/native-chat.test.ts +0 -22
  52. package/src/components/native-entrance/entrance-controller-store.test.ts +0 -75
  53. package/src/components/native-media-visibility.test.ts +0 -77
  54. package/src/components/native-space-callkit-controller.test.ts +0 -62
  55. package/src/components/native-space-view/AppearanceSettings.test.tsx +0 -56
  56. package/src/components/native-space-view/ParticipantActionSheet.test.tsx +0 -18
  57. package/src/components/native-space-view/SettingsSheet.test.tsx +0 -17
  58. package/src/components/native-space-view/SpaceActionMenu.test.tsx +0 -126
  59. package/src/components/native-space-view/SpaceBottomDock.android.test.tsx +0 -61
  60. package/src/components/native-space-view/SpaceChatSheet.test.tsx +0 -18
  61. package/src/components/native-space-view/SpaceMoreSheet.test.tsx +0 -15
  62. package/src/components/native-space-view/SpacePanelSheet.test.tsx +0 -15
  63. package/src/components/native-space-view/SpacePeopleSheet.test.tsx +0 -13
  64. package/src/components/native-space-view/SpaceSurfacePrimitives.test.tsx +0 -12
  65. package/src/components/native-space-view/SpaceTopBar.android.test.tsx +0 -43
  66. package/src/components/native-space-view/SpaceWhiteboardSurface.test.tsx +0 -85
  67. package/src/components/native-space-view/diagnostics.test.ts +0 -31
  68. package/src/components/native-space-view/format-space-duration.test.ts +0 -13
  69. package/src/components/native-space-view/native-space-grid-keys.test.ts +0 -11
  70. package/src/components/native-space-view/native-space-page-store.test.ts +0 -23
  71. package/src/components/native-space-view/space-chat-attachments.test.ts +0 -33
  72. package/src/components/native-space-view/space-progressive-surface-helpers.test.ts +0 -24
  73. package/src/components/native-space-view/useSpaceViewController.contract.test.ts +0 -10
  74. package/src/components/native-space-view/useSpaceViewController.test.ts +0 -106
  75. package/src/components/native-space-view/useSpaceViewDerived.test.ts +0 -58
  76. package/src/components/native-space-view/useSpaceViewPanels.controlled.test.ts +0 -40
  77. package/src/components/native-space-view/useSpaceViewPanels.test.ts +0 -73
  78. package/src/components/space-lifecycle.test.ts +0 -19
  79. package/src/context/space-client-context.test.tsx +0 -10
  80. package/src/diagnostics.test.ts +0 -29
  81. package/src/hooks/entrance-preview-store.test.ts +0 -79
  82. package/src/hooks/space-hooks.test.ts +0 -76
  83. package/src/hooks/useLayout.test.ts +0 -16
  84. package/src/invites.test.ts +0 -51
  85. package/src/public-surface.test.ts +0 -26
  86. package/src/runtime.test.ts +0 -83
  87. package/src/space-client/cloudflare-rtk-native.test.ts +0 -118
  88. package/src/space-client/create-native-space-client.test.ts +0 -300
  89. package/src/telemetry.test.ts +0 -143
  90. package/src/test-support/test-renderer.test.ts +0 -9
  91. package/src/test-support/test-renderer.ts +0 -28
  92. package/src/ui/appearance.test.ts +0 -61
  93. package/src/ui/native-appearance-context.controlled.test.tsx +0 -41
  94. package/src/ui/native-appearance-context.test.tsx +0 -14
  95. package/src/ui/native-theme.test.ts +0 -34
  96. package/src/ui/native-theme.test.tsx +0 -14
  97. package/src/ui/participant-avatar.test.ts +0 -29
  98. package/src/ui/theme-tokens.test.ts +0 -14
  99. package/src/ui/theme.test.ts +0 -36
  100. package/src/utils/build-chalk-invite-link.test.ts +0 -12
  101. package/src/utils/ios-simulator.test.ts +0 -96
  102. package/src/utils/native-space-layout.test.ts +0 -170
  103. package/src/whiteboard/embedded-whiteboard-assets.test.ts +0 -31
@@ -1,19 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
-
3
- vi.mock("./EntranceView", () => ({ EntranceView: "EntranceView" }));
4
- vi.mock("react-native", () => ({ Image: "Image" }));
5
-
6
- import { Entrance } from "./Entrance";
7
-
8
- describe("Entrance", () => {
9
- it("exposes the preparation component", () => {
10
- expect(Entrance).toBeTypeOf("function");
11
- });
12
-
13
- it("forwards the public cancel callback to the native Entrance view", () => {
14
- const onCancel = vi.fn();
15
- const element = Entrance({ onCancel, onJoin: vi.fn(), spaceName: "Space" });
16
-
17
- expect(element.props.onCancel).toBe(onCancel);
18
- });
19
- });
@@ -1,100 +0,0 @@
1
- import { isValidElement, type ReactElement, type ReactNode } from "react";
2
- import { describe, expect, it, vi } from "vitest";
3
-
4
- const controller = vi.hoisted(() => ({
5
- displayName: "Ada",
6
- audioEnabled: true,
7
- videoEnabled: true,
8
- isSubmitting: false,
9
- isInputFocused: false,
10
- previewError: null,
11
- previewStream: null,
12
- simulatorMediaDisabled: false,
13
- setDisplayName: vi.fn(),
14
- setInputFocused: vi.fn(),
15
- toggleAudio: vi.fn(),
16
- toggleVideo: vi.fn(),
17
- handleJoin: vi.fn(),
18
- }));
19
-
20
- const theme = vi.hoisted(() => ({
21
- colors: {
22
- background: "#101112",
23
- foreground: "#f7f8f9",
24
- primary: "#12ab89",
25
- primaryForeground: "#ffffff",
26
- muted: "#343536",
27
- mutedForeground: "#909192",
28
- error: "#dc2626",
29
- card: "#202122",
30
- surface: "#303132",
31
- raisedSurface: "#404142",
32
- insetSurface: "#505152",
33
- controlsBackground: "#606162",
34
- border: "#707172",
35
- ring: "#808182",
36
- darkCanvas: "#010203",
37
- },
38
- }));
39
-
40
- vi.mock("react-native", () => ({
41
- KeyboardAvoidingView: "KeyboardAvoidingView",
42
- Platform: { OS: "android" },
43
- Pressable: "Pressable",
44
- StyleSheet: { absoluteFillObject: {}, create: <T,>(styles: T) => styles },
45
- Text: "Text",
46
- TextInput: "TextInput",
47
- View: "View",
48
- }));
49
- vi.mock("@hugeicons/react-native", () => ({ HugeiconsIcon: "HugeiconsIcon" }));
50
- vi.mock("../media/native-webrtc", () => ({ RTCView: "RTCView" }));
51
- vi.mock("../ui/native-theme", () => ({ useNativeTheme: () => theme }));
52
- vi.mock("./FaceAvatar", () => ({ FaceAvatar: "FaceAvatar" }));
53
- vi.mock("./native-entrance/useEntranceController", () => ({ useEntranceController: () => controller }));
54
-
55
- import { EntranceViewAndroid } from "./EntranceView.android";
56
-
57
- describe("EntranceViewAndroid", () => {
58
- it("applies the active theme to its native surface and media controls", () => {
59
- const entrance = EntranceViewAndroid({ onJoin: vi.fn(), spaceName: "Design review" });
60
- const screen = findElements(entrance, (element) => element.type === "KeyboardAvoidingView")[0];
61
- const mediaControl = findElements(entrance, (element) => element.type === "Pressable" && !element.props.accessibilityLabel)[0];
62
- const mediaStyle = mediaControl?.props.style as ((state: { readonly pressed: boolean }) => unknown) | undefined;
63
-
64
- expect(JSON.stringify(screen?.props.style)).toContain(theme.colors.background);
65
- expect(JSON.stringify(mediaStyle?.({ pressed: false }))).toContain(theme.colors.controlsBackground);
66
- });
67
-
68
- it("omits the Android cancel control when no cancellation handler is supplied", () => {
69
- const withoutCancel = EntranceViewAndroid({ onJoin: vi.fn(), spaceName: "Design review" });
70
- const withCancel = EntranceViewAndroid({ onCancel: vi.fn(), onJoin: vi.fn(), spaceName: "Design review" });
71
-
72
- expect(findElements(withoutCancel, (element) => element.props.accessibilityLabel === "Cancel and leave Entrance")).toHaveLength(0);
73
- expect(findElements(withCancel, (element) => element.props.accessibilityLabel === "Cancel and leave Entrance")).toHaveLength(1);
74
- });
75
- });
76
-
77
- interface NativeElementProps {
78
- readonly accessibilityLabel?: string;
79
- readonly children?: ReactNode;
80
- readonly style?: unknown;
81
- }
82
-
83
- function findElements(node: ReactNode, matches: (element: ReactElement<NativeElementProps>) => boolean): ReactElement<NativeElementProps>[] {
84
- const found: ReactElement<NativeElementProps>[] = [];
85
- if (!isValidElement<NativeElementProps>(node)) {
86
- return found;
87
- }
88
-
89
- if (matches(node)) {
90
- found.push(node);
91
- }
92
-
93
- const children = node.props.children;
94
- for (const child of Array.isArray(children) ? children : [children]) {
95
- if (child !== undefined && child !== null) {
96
- found.push(...findElements(child, matches));
97
- }
98
- }
99
- return found;
100
- }
@@ -1,160 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import type { ReactElement, ReactNode } from "react";
3
- import { beforeEach, describe, expect, it, vi } from "vitest";
4
-
5
- import { renderHook } from "../test-support/test-renderer";
6
-
7
- const controller = vi.hoisted(() => ({
8
- displayName: "Ada",
9
- audioEnabled: true,
10
- videoEnabled: true,
11
- isSubmitting: false,
12
- isInputFocused: false,
13
- previewError: null,
14
- previewStream: null,
15
- simulatorMediaDisabled: false,
16
- setDisplayName: vi.fn(),
17
- setInputFocused: vi.fn(),
18
- toggleAudio: vi.fn(),
19
- toggleVideo: vi.fn(),
20
- handleJoin: vi.fn(),
21
- }));
22
-
23
- const theme = vi.hoisted(() => ({
24
- colors: {
25
- background: "#101112",
26
- foreground: "#f7f8f9",
27
- primary: "#12ab89",
28
- primaryForeground: "#ffffff",
29
- mutedForeground: "#909192",
30
- error: "#dc2626",
31
- card: "#202122",
32
- surface: "#303132",
33
- border: "#707172",
34
- ring: "#808182",
35
- darkCanvas: "#010203",
36
- controlsBackground: "#606162",
37
- },
38
- }));
39
-
40
- vi.mock("react-native", () => {
41
- class AnimatedValue {
42
- interpolate(input: unknown): { readonly input: unknown } {
43
- return { input };
44
- }
45
- }
46
-
47
- return {
48
- ActivityIndicator: "ActivityIndicator",
49
- Animated: {
50
- Value: AnimatedValue,
51
- View: "Animated.View",
52
- delay: vi.fn(),
53
- loop: vi.fn(),
54
- sequence: vi.fn(),
55
- spring: vi.fn(),
56
- stagger: vi.fn(),
57
- timing: vi.fn(),
58
- },
59
- KeyboardAvoidingView: "KeyboardAvoidingView",
60
- Platform: { OS: "ios" },
61
- Pressable: "Pressable",
62
- StyleSheet: { absoluteFillObject: {}, create: <T,>(styles: T) => styles },
63
- Text: "Text",
64
- TextInput: "TextInput",
65
- View: "View",
66
- };
67
- });
68
- vi.mock("@hugeicons/react-native", () => ({ HugeiconsIcon: "HugeiconsIcon" }));
69
- vi.mock("./FaceAvatar", () => ({ FaceAvatar: "FaceAvatar" }));
70
- vi.mock("./RtcVideoView", () => ({ RtcVideoView: "RtcVideoView", hasRtcVideoView: () => false }));
71
- vi.mock("../ui/native-theme", () => ({ useNativeTheme: () => theme }));
72
- vi.mock("../utils/ios-simulator", () => ({ getIosSimulatorMediaMessage: () => "Media is unavailable in the simulator." }));
73
- vi.mock("./native-animation-controller", () => ({ createAnimationRefController: () => () => undefined }));
74
- vi.mock("./native-entrance/useEntranceController", () => ({ useEntranceController: () => controller }));
75
-
76
- import { EntranceViewIosPad } from "./EntranceView.ios-pad";
77
-
78
- beforeEach(() => {
79
- controller.displayName = "Ada";
80
- controller.audioEnabled = true;
81
- controller.videoEnabled = true;
82
- controller.isSubmitting = false;
83
- controller.isInputFocused = false;
84
- controller.previewError = null;
85
- controller.previewStream = null;
86
- controller.simulatorMediaDisabled = false;
87
- controller.setDisplayName.mockReset();
88
- controller.setInputFocused.mockReset();
89
- controller.toggleAudio.mockReset();
90
- controller.toggleVideo.mockReset();
91
- controller.handleJoin.mockReset();
92
- });
93
-
94
- describe("EntranceViewIosPad", () => {
95
- it("composes the immersive surface with themed title, media controls, and cancel action", () => {
96
- const onCancel = vi.fn();
97
- const { result } = renderHook(() => EntranceViewIosPad({ onCancel, onJoin: vi.fn(), spaceName: "Design review" }));
98
- const tree = result.current;
99
-
100
- const screen = findElement(tree, "KeyboardAvoidingView");
101
- const cancel = findElement(tree, "Pressable", (element) => element.props.accessibilityLabel === "Cancel and leave Entrance");
102
- const microphone = findElement(tree, "Pressable", (element) => element.props.accessibilityLabel === "Mute microphone");
103
- const camera = findElement(tree, "Pressable", (element) => element.props.accessibilityLabel === "Turn camera off");
104
- const join = findElement(tree, "Pressable", (element) => element.props.accessibilityLabel === "Enter Space");
105
-
106
- expect(screen?.props.style).toEqual(expect.arrayContaining([expect.objectContaining({ backgroundColor: theme.colors.background })]));
107
- expect(findText(tree)).toContain("Design review");
108
- expect(cancel).toBeDefined();
109
- expect(microphone).toBeDefined();
110
- expect(camera).toBeDefined();
111
- expect(join?.props.accessibilityState).toEqual({ disabled: false });
112
-
113
- cancel?.props.onPress();
114
- expect(onCancel).toHaveBeenCalledOnce();
115
- });
116
-
117
- it("uses the avatar fallback and disables joining until a name is entered", () => {
118
- controller.displayName = "";
119
-
120
- const { result } = renderHook(() => EntranceViewIosPad({ onJoin: vi.fn(), spaceName: "Quiet Space" }));
121
- const tree = result.current;
122
- const join = findElement(tree, "Pressable", (element) => element.props.accessibilityLabel === "Enter Space");
123
-
124
- expect(findElement(tree, "FaceAvatar")).toBeDefined();
125
- expect(findElement(tree, "RtcVideoView")).toBeUndefined();
126
- expect(join?.props.accessibilityState).toEqual({ disabled: true });
127
- expect(findText(tree)).toContain("Quiet Space");
128
- });
129
- });
130
-
131
- interface NativeElementProps {
132
- readonly accessibilityLabel?: string;
133
- readonly accessibilityState?: unknown;
134
- readonly children?: ReactNode;
135
- readonly onPress?: () => void;
136
- readonly style?: unknown;
137
- }
138
-
139
- function findElement(node: ReactNode, type: string, matches: (element: ReactElement<NativeElementProps>) => boolean = () => true): ReactElement<NativeElementProps> | undefined {
140
- if (!isElement(node)) return undefined;
141
- if (node.type === type && matches(node)) return node;
142
-
143
- const children = node.props.children;
144
- for (const child of Array.isArray(children) ? children : [children]) {
145
- const found = findElement(child, type, matches);
146
- if (found) return found;
147
- }
148
- return undefined;
149
- }
150
-
151
- function findText(node: ReactNode): string[] {
152
- if (typeof node === "string") return [node];
153
- if (!isElement(node)) return [];
154
- const children = node.props.children;
155
- return (Array.isArray(children) ? children : [children]).flatMap(findText);
156
- }
157
-
158
- function isElement(node: ReactNode): node is ReactElement<NativeElementProps> {
159
- return typeof node === "object" && node !== null && "type" in node && "props" in node;
160
- }
@@ -1,137 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import type { ReactElement, ReactNode } from "react";
3
- import { beforeEach, describe, expect, it, vi } from "vitest";
4
-
5
- import { renderHook } from "../test-support/test-renderer";
6
-
7
- const controller = vi.hoisted(() => ({
8
- displayName: "Ada",
9
- audioEnabled: true,
10
- videoEnabled: true,
11
- isSubmitting: false,
12
- isInputFocused: false,
13
- previewError: null,
14
- previewStream: null as { toURL: () => string } | null,
15
- simulatorMediaDisabled: false,
16
- setDisplayName: vi.fn(),
17
- setInputFocused: vi.fn(),
18
- toggleAudio: vi.fn(),
19
- toggleVideo: vi.fn(),
20
- handleJoin: vi.fn(),
21
- }));
22
-
23
- const theme = vi.hoisted(() => ({
24
- colors: {
25
- background: "#101112",
26
- foreground: "#f7f8f9",
27
- primary: "#12ab89",
28
- primaryForeground: "#ffffff",
29
- muted: "#343536",
30
- mutedForeground: "#909192",
31
- error: "#dc2626",
32
- card: "#202122",
33
- surface: "#303132",
34
- raisedSurface: "#404142",
35
- insetSurface: "#505152",
36
- controlsBackground: "#606162",
37
- border: "#707172",
38
- ring: "#808182",
39
- darkCanvas: "#010203",
40
- },
41
- }));
42
-
43
- vi.mock("react-native", () => ({
44
- KeyboardAvoidingView: "KeyboardAvoidingView",
45
- Platform: { OS: "ios" },
46
- Pressable: "Pressable",
47
- StyleSheet: { absoluteFillObject: {}, create: <T,>(styles: T) => styles },
48
- Text: "Text",
49
- TextInput: "TextInput",
50
- View: "View",
51
- }));
52
- vi.mock("@cloudflare/react-native-webrtc", () => ({ RTCView: "RTCView" }));
53
- vi.mock("@hugeicons/react-native", () => ({ HugeiconsIcon: "HugeiconsIcon" }));
54
- vi.mock("./FaceAvatar", () => ({ FaceAvatar: "FaceAvatar" }));
55
- vi.mock("../ui/native-theme", () => ({ useNativeTheme: () => theme }));
56
- vi.mock("../utils/ios-simulator", () => ({ getIosSimulatorMediaMessage: () => "Media is unavailable in the simulator." }));
57
- vi.mock("./native-entrance/useEntranceController", () => ({ useEntranceController: () => controller }));
58
-
59
- import { EntranceViewIosPhone } from "./EntranceView.ios-phone";
60
-
61
- beforeEach(() => {
62
- controller.displayName = "Ada";
63
- controller.audioEnabled = true;
64
- controller.videoEnabled = true;
65
- controller.isSubmitting = false;
66
- controller.isInputFocused = false;
67
- controller.previewError = null;
68
- controller.previewStream = null;
69
- controller.simulatorMediaDisabled = false;
70
- controller.setDisplayName.mockReset();
71
- controller.setInputFocused.mockReset();
72
- controller.toggleAudio.mockReset();
73
- controller.toggleVideo.mockReset();
74
- controller.handleJoin.mockReset();
75
- });
76
-
77
- describe("EntranceViewIosPhone", () => {
78
- it("renders the phone preview sheet with the canonical Space title and join controls", () => {
79
- const onCancel = vi.fn();
80
- const { result } = renderHook(() => EntranceViewIosPhone({ onCancel, onJoin: vi.fn(), spaceName: "Design review" }));
81
- const tree = result.current;
82
- const screen = findElement(tree, "KeyboardAvoidingView");
83
- const cancel = findElement(tree, "Pressable", (element) => element.props.accessibilityLabel === "Cancel and leave Entrance");
84
- const textInput = findElement(tree, "TextInput");
85
- const join = findText(tree).find((text) => text === "Enter Space");
86
-
87
- expect(screen?.props.style).toEqual(expect.arrayContaining([expect.objectContaining({ backgroundColor: theme.colors.background })]));
88
- expect(findText(tree)).toContain("Design review");
89
- expect(cancel).toBeDefined();
90
- expect(textInput?.props.placeholder).toBe("Enter your name");
91
- expect(join).toBe("Enter Space");
92
-
93
- cancel?.props.onPress();
94
- expect(onCancel).toHaveBeenCalledOnce();
95
- });
96
-
97
- it("mounts the RTC preview only when the controller supplies an enabled stream", () => {
98
- controller.previewStream = { toURL: () => "stream://preview" };
99
-
100
- const { result } = renderHook(() => EntranceViewIosPhone({ onJoin: vi.fn(), spaceName: "Live Space" }));
101
- const preview = findElement(result.current, "RTCView");
102
-
103
- expect(preview?.props).toMatchObject({ mirror: true, objectFit: "cover", streamURL: "stream://preview" });
104
- });
105
- });
106
-
107
- interface NativeElementProps {
108
- readonly accessibilityLabel?: string;
109
- readonly children?: ReactNode;
110
- readonly onPress?: () => void;
111
- readonly placeholder?: string;
112
- readonly props?: unknown;
113
- readonly style?: unknown;
114
- }
115
-
116
- function findElement(node: ReactNode, type: string, matches: (element: ReactElement<NativeElementProps>) => boolean = () => true): ReactElement<NativeElementProps> | undefined {
117
- if (!isElement(node)) return undefined;
118
- if (node.type === type && matches(node)) return node;
119
-
120
- const children = node.props.children;
121
- for (const child of Array.isArray(children) ? children : [children]) {
122
- const found = findElement(child, type, matches);
123
- if (found) return found;
124
- }
125
- return undefined;
126
- }
127
-
128
- function findText(node: ReactNode): string[] {
129
- if (typeof node === "string") return [node];
130
- if (!isElement(node)) return [];
131
- const children = node.props.children;
132
- return (Array.isArray(children) ? children : [children]).flatMap(findText);
133
- }
134
-
135
- function isElement(node: ReactNode): node is ReactElement<NativeElementProps> {
136
- return typeof node === "object" && node !== null && "type" in node && "props" in node;
137
- }
@@ -1,15 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
-
3
- vi.mock("../platform/platform", () => ({ resolvePlatformVariant: () => "android" }));
4
- vi.mock("./EntranceView.android", () => ({ EntranceViewAndroid: () => null }));
5
- vi.mock("./EntranceView.ios-pad", () => ({ EntranceViewIosPad: () => null }));
6
- vi.mock("./EntranceView.ios-phone", () => ({ EntranceViewIosPhone: () => null }));
7
- vi.mock("./EntranceView.macos", () => ({ EntranceViewMacos: () => null }));
8
-
9
- describe("EntranceView", () => {
10
- it("selects the platform-specific entrance", async () => {
11
- const { EntranceView } = await import("./EntranceView");
12
-
13
- expect(EntranceView({ spaceName: "Space", onJoin: vi.fn() })).toBeTruthy();
14
- });
15
- });
@@ -1,60 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import type { ReactElement, ReactNode } from "react";
3
- import { describe, expect, it, vi } from "vitest";
4
-
5
- import { renderHook } from "../test-support/test-renderer";
6
-
7
- vi.mock("react-native", () => {
8
- class AnimatedValue {
9
- interpolate(input: unknown): { readonly input: unknown } {
10
- return { input };
11
- }
12
- }
13
-
14
- return {
15
- Animated: {
16
- Value: AnimatedValue,
17
- createAnimatedComponent: (component: unknown) => component,
18
- },
19
- };
20
- });
21
- vi.mock("react-native-svg", () => ({ Circle: "Circle", Ellipse: "Ellipse", G: "G", Path: "Path", Rect: "Rect", default: "Svg" }));
22
- vi.mock("./native-animation-controller", () => ({ createAnimationRefController: () => () => undefined }));
23
-
24
- import Svg from "react-native-svg";
25
- import { LogoElements } from "./LogoElements";
26
-
27
- describe("LogoElements", () => {
28
- it("uses the requested size and keeps the complete canonical mark composition", () => {
29
- const { result } = renderHook(() => LogoElements({ size: 96 }));
30
- const tree = result.current;
31
-
32
- expect(tree.type).toBe(Svg);
33
- expect(tree.props).toMatchObject({ height: 96, viewBox: "0 0 64 64", width: 96 });
34
- expect(findElements(tree, "Path")).toHaveLength(1);
35
- expect(findElements(tree, "Circle")).toHaveLength(3);
36
- expect(findElements(tree, "Rect")).toHaveLength(4);
37
- expect(findElements(tree, "Ellipse")).toHaveLength(4);
38
- });
39
-
40
- it("preserves the brand palette across each logo bar and particle", () => {
41
- const { result } = renderHook(() => LogoElements({}));
42
- const tree = result.current;
43
- const fills = findElements(tree, "Rect").map((element) => element.props.fill);
44
- const particleFills = findElements(tree, "Circle").map((element) => element.props.fill);
45
-
46
- expect(fills).toEqual(["#A8D5A2", "#F5D76E", "#7EC8E3", "#F0A0A0"]);
47
- expect(particleFills).toEqual(["#A8D5A2", "#F5D76E", "#7EC8E3"]);
48
- });
49
- });
50
-
51
- function findElements(node: ReactNode, type: string): ReactElement<{ readonly fill?: string }>[] {
52
- if (!isElement(node)) return [];
53
- const matches = node.type === type ? [node] : [];
54
- const children = node.props.children;
55
- return matches.concat((Array.isArray(children) ? children : [children]).flatMap((child) => findElements(child, type)));
56
- }
57
-
58
- function isElement(node: ReactNode): node is ReactElement<{ readonly children?: ReactNode; readonly fill?: string }> {
59
- return typeof node === "object" && node !== null && "type" in node && "props" in node;
60
- }
@@ -1,119 +0,0 @@
1
- import { useRef } from "react";
2
- import { Animated } from "react-native";
3
- import Svg, { Circle, Ellipse, G, Path, Rect } from "react-native-svg";
4
- import { Theme } from "../ui/theme";
5
- import { createAnimationRefController, type AnimationRefCallback } from "./native-animation-controller";
6
-
7
- const AnimatedCircle = Animated.createAnimatedComponent(Circle);
8
- const AnimatedPath = Animated.createAnimatedComponent(Path);
9
-
10
- interface LogoElementsProps {
11
- size?: number;
12
- }
13
-
14
- export function LogoElements({ size = 64 }: LogoElementsProps): React.JSX.Element {
15
- const particle1 = useRef(new Animated.Value(0)).current;
16
- const particle2 = useRef(new Animated.Value(0)).current;
17
- const particle3 = useRef(new Animated.Value(0)).current;
18
- const arcProgress = useRef(new Animated.Value(0)).current;
19
-
20
- const animationRef = useRef<AnimationRefCallback<unknown> | null>(null);
21
- const attachAnimations =
22
- animationRef.current ??
23
- (animationRef.current = createAnimationRefController<unknown>(() => {
24
- const createParticleAnimation = (animation: Animated.Value, delay: number) =>
25
- Animated.loop(
26
- Animated.sequence([
27
- Animated.delay(delay),
28
- Animated.timing(animation, {
29
- toValue: 1,
30
- duration: 2500,
31
- useNativeDriver: true,
32
- }),
33
- ]),
34
- );
35
-
36
- const particleAnimations = [createParticleAnimation(particle1, 0), createParticleAnimation(particle2, 800), createParticleAnimation(particle3, 1600)];
37
- const arcAnimation = Animated.loop(
38
- Animated.sequence([
39
- Animated.timing(arcProgress, {
40
- toValue: 1,
41
- duration: 3500,
42
- useNativeDriver: true,
43
- }),
44
- Animated.delay(2500),
45
- ]),
46
- );
47
-
48
- return [...particleAnimations, arcAnimation];
49
- }));
50
-
51
- const createParticleProps = (animation: Animated.Value, xOffset: number) => ({
52
- opacity: animation.interpolate({
53
- inputRange: [0, 0.2, 0.8, 1],
54
- outputRange: [0, 0.5, 0.5, 0],
55
- }),
56
- transform: [
57
- {
58
- translateY: animation.interpolate({
59
- inputRange: [0, 1],
60
- outputRange: [0, -20],
61
- }),
62
- },
63
- {
64
- translateX: animation.interpolate({
65
- inputRange: [0, 1],
66
- outputRange: [0, xOffset],
67
- }),
68
- },
69
- ],
70
- });
71
-
72
- return (
73
- <Svg ref={attachAnimations} width={size} height={size} viewBox="0 0 64 64" fill="none">
74
- <AnimatedPath
75
- d="M 12 16 Q 32 -2 52 16"
76
- stroke={Theme.logo.arc}
77
- strokeWidth="1.5"
78
- strokeLinecap="round"
79
- fill="none"
80
- opacity={arcProgress.interpolate({
81
- inputRange: [0, 0.2, 0.7, 1],
82
- outputRange: [0, 1, 1, 0],
83
- })}
84
- transform={[
85
- {
86
- translateY: arcProgress.interpolate({
87
- inputRange: [0, 1],
88
- outputRange: [4, -4],
89
- }),
90
- },
91
- ]}
92
- />
93
-
94
- <AnimatedCircle cx="16" cy="12" r="1.5" fill={Theme.logo.mint} {...createParticleProps(particle1, -8)} />
95
- <AnimatedCircle cx="28" cy="8" r="2" fill={Theme.logo.gold} {...createParticleProps(particle2, 4)} />
96
- <AnimatedCircle cx="40" cy="6" r="1.5" fill={Theme.logo.sky} {...createParticleProps(particle3, 8)} />
97
-
98
- <G transform="rotate(-20 16 48)">
99
- <Rect x="8" y="16" width="12" height="40" rx="6" fill={Theme.logo.mint} />
100
- <Ellipse cx="14" cy="16" rx="6" ry="3.5" fill={Theme.logo.mintDark} />
101
- </G>
102
-
103
- <G transform="rotate(-5 24 44)">
104
- <Rect x="18" y="12" width="12" height="44" rx="6" fill={Theme.logo.gold} />
105
- <Ellipse cx="24" cy="12" rx="6" ry="3.5" fill={Theme.logo.goldDark} />
106
- </G>
107
-
108
- <G transform="rotate(25 44 20)">
109
- <Rect x="28" y="4" width="12" height="42" rx="6" fill={Theme.logo.sky} />
110
- <Ellipse cx="34" cy="4" rx="6" ry="3.5" fill={Theme.logo.skyDark} />
111
- </G>
112
-
113
- <G transform="rotate(10 44 40)">
114
- <Rect x="38" y="18" width="12" height="38" rx="6" fill={Theme.logo.coral} />
115
- <Ellipse cx="44" cy="56" rx="6" ry="3.5" fill={Theme.logo.coralDark} />
116
- </G>
117
- </Svg>
118
- );
119
- }
@@ -1,19 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { describe, expect, it } from "vitest";
3
-
4
- const source = readFileSync(new URL("./ReactionPicker.tsx", import.meta.url), "utf8");
5
-
6
- describe("ReactionPicker", () => {
7
- it("covers the persistent dock while keeping the reaction card inset", () => {
8
- expect(source).toContain("StyleSheet.absoluteFillObject");
9
- expect(source).not.toContain("paddingBottom: 110");
10
- expect(source).not.toContain("bottom: 94");
11
- });
12
-
13
- it("keeps close-button press feedback defined", () => {
14
- expect(source).toContain("pressed && styles.pressed");
15
- expect(source).toContain("pressed: {");
16
- expect(source).toContain("opacity: 0.72");
17
- expect(source).toContain("transform: [{ scale: 0.96 }]");
18
- });
19
- });
@@ -1,12 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
-
3
- vi.mock("../media/native-webrtc", () => ({ RTCView: () => null }));
4
-
5
- describe("RtcVideoView", () => {
6
- it("reports the availability of the RTC view", async () => {
7
- const { hasRtcVideoView, RtcVideoView } = await import("./RtcVideoView");
8
-
9
- expect(hasRtcVideoView()).toBe(true);
10
- expect(RtcVideoView({ streamURL: "stream" })).toBeTruthy();
11
- });
12
- });
@@ -1,11 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
-
3
- vi.mock("./SpaceView.shared", () => ({ SpaceViewShared: () => null }));
4
-
5
- describe("SpaceView.android", () => {
6
- it("exposes the canonical platform component name", async () => {
7
- const { SpaceViewAndroid } = await import("./SpaceView.android");
8
-
9
- expect(SpaceViewAndroid).toBeTypeOf("function");
10
- });
11
- });
@@ -1,11 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
-
3
- vi.mock("./SpaceView.shared", () => ({ SpaceViewShared: () => null }));
4
-
5
- describe("SpaceView.ios-pad", () => {
6
- it("exposes the canonical platform component name", async () => {
7
- const { SpaceViewIosPad } = await import("./SpaceView.ios-pad");
8
-
9
- expect(SpaceViewIosPad).toBeTypeOf("function");
10
- });
11
- });