@applicaster/zapp-react-native-ui-components 16.0.0-alpha.1593888345 → 16.0.0-alpha.1701179921
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.
- package/Components/LinkHandler/LinkHandler.tsx +2 -7
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
- package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -3
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -2
- package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +4 -2
- package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
- package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +5 -2
- package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +4 -1
- package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +58 -73
- package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +65 -56
- package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +140 -65
- package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +93 -46
- package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +148 -32
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
- package/Components/ModalComponent/AudioPlayer/Components/index.ts +0 -2
- package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +132 -6
- package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +202 -13
- package/Components/ModalComponent/BottomSheetModalContent.tsx +187 -73
- package/Components/ModalComponent/SortableList.tsx +36 -22
- package/Components/ModalComponent/TextInputContent.tsx +42 -7
- package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +111 -6
- package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +50 -1
- package/Components/ModalComponent/index.tsx +7 -9
- package/Components/ModalComponent/presets/__tests__/getCell.test.ts +24 -0
- package/Components/ModalComponent/presets/selectableCell.tsx +1 -1
- package/Components/ModalComponent/showTextInput.ts +11 -2
- package/Components/PlayerContainer/PlayerContainer.tsx +8 -1
- package/Components/River/ComponentsMap/ComponentsMap.tsx +10 -12
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +138 -75
- package/Components/River/__tests__/componentsMap.test.js +8 -3
- package/Components/Screen/__tests__/Screen.test.tsx +24 -0
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
- package/Components/Screen/index.tsx +5 -0
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +1 -1
- package/Components/Transitioner/Transitioner.tsx +2 -3
- package/Components/Transitioner/__tests__/transitioner.test.js +83 -2
- package/Components/VideoLive/LiveImageManager.ts +8 -2
- package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
- package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
- package/Contexts/ModalNavigationContext/index.tsx +2 -2
- package/assets/empty-Info.png +0 -0
- package/package.json +5 -5
- package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +0 -314
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
|
|
4
|
+
import { getPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
6
5
|
import {
|
|
7
6
|
inflateString,
|
|
8
7
|
objectToReadableString,
|
|
@@ -40,12 +39,8 @@ export async function inflateUrl(url) {
|
|
|
40
39
|
|
|
41
40
|
export function LinkHandler(props: Props) {
|
|
42
41
|
const screenData = props?.screenData;
|
|
43
|
-
const plugins = usePlugins();
|
|
44
42
|
|
|
45
|
-
const ScreenPlugin =
|
|
46
|
-
WEBVIEW_SCREEN_IDENTIFIER,
|
|
47
|
-
plugins
|
|
48
|
-
);
|
|
43
|
+
const ScreenPlugin = getPluginByIdentifier(WEBVIEW_SCREEN_IDENTIFIER);
|
|
49
44
|
|
|
50
45
|
const Component =
|
|
51
46
|
R.path(["module", "Component"], ScreenPlugin) ||
|
|
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect } from "react";
|
|
|
2
2
|
import { TouchableOpacity, ViewStyle } from "react-native";
|
|
3
3
|
|
|
4
4
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
5
|
+
import { useUIComponentContext } from "@applicaster/zapp-react-native-ui-components/Contexts/UIComponentContext";
|
|
5
6
|
|
|
6
7
|
import Image from "./Image";
|
|
7
8
|
type Props = {
|
|
@@ -57,6 +58,7 @@ export const ActionButton = React.memo(function ActionButtonComponent(
|
|
|
57
58
|
) {
|
|
58
59
|
const { item, action, asset, flavour = "flavour_1", cellUUID } = props;
|
|
59
60
|
const actionContext = useActions(action?.identifier);
|
|
61
|
+
const component = useUIComponentContext();
|
|
60
62
|
|
|
61
63
|
// TODO: add subscription API for action availability
|
|
62
64
|
const actionDisabled =
|
|
@@ -81,8 +83,9 @@ export const ActionButton = React.memo(function ActionButtonComponent(
|
|
|
81
83
|
updateState: (state) => {
|
|
82
84
|
setActionState(state);
|
|
83
85
|
},
|
|
86
|
+
...(component ? { component } : {}),
|
|
84
87
|
});
|
|
85
|
-
}, [actionState, actionContext?.state, item
|
|
88
|
+
}, [actionState, actionContext?.state, item, component]);
|
|
86
89
|
|
|
87
90
|
useEffect(() => {
|
|
88
91
|
if (typeof actionContext?.addListener === "function") {
|
|
@@ -6,6 +6,7 @@ import React, {
|
|
|
6
6
|
useState,
|
|
7
7
|
} from "react";
|
|
8
8
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
9
|
+
import { useUIComponentContext } from "@applicaster/zapp-react-native-ui-components/Contexts/UIComponentContext";
|
|
9
10
|
|
|
10
11
|
type ActionDefinition = {
|
|
11
12
|
identifier?: string;
|
|
@@ -61,6 +62,7 @@ export function ActionButtonController({
|
|
|
61
62
|
const resolvedEntry = entry;
|
|
62
63
|
const resolvedIdentifier = action?.identifier || pluginIdentifier || "";
|
|
63
64
|
const actionContext = useActions(resolvedIdentifier);
|
|
65
|
+
const component = useUIComponentContext();
|
|
64
66
|
|
|
65
67
|
const actionDisabled =
|
|
66
68
|
typeof actionContext?.isActionAvailable === "function" &&
|
|
@@ -128,20 +130,33 @@ export function ActionButtonController({
|
|
|
128
130
|
return;
|
|
129
131
|
}
|
|
130
132
|
|
|
133
|
+
const invokeOptions: InvokeArgsOptions = {
|
|
134
|
+
...(component ? { component } : {}),
|
|
135
|
+
};
|
|
136
|
+
|
|
131
137
|
if (supportsEntryState) {
|
|
132
138
|
return actionContext.invokeAction?.(resolvedEntry, {
|
|
139
|
+
...invokeOptions,
|
|
133
140
|
updateState: setActionState,
|
|
134
141
|
});
|
|
135
142
|
}
|
|
136
143
|
|
|
144
|
+
if (typeof actionContext.invokeAction === "function") {
|
|
145
|
+
return actionContext.invokeAction(resolvedEntry, invokeOptions);
|
|
146
|
+
}
|
|
147
|
+
|
|
137
148
|
const favouritesAction = legacySelected
|
|
138
149
|
? actionContext.removeFavourite
|
|
139
150
|
: actionContext.addFavourite;
|
|
140
151
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
return favouritesAction?.(resolvedEntry);
|
|
153
|
+
}, [
|
|
154
|
+
actionContext,
|
|
155
|
+
supportsEntryState,
|
|
156
|
+
resolvedEntry,
|
|
157
|
+
legacySelected,
|
|
158
|
+
component,
|
|
159
|
+
]);
|
|
145
160
|
|
|
146
161
|
if (!actionContext || actionDisabled) {
|
|
147
162
|
if (!actionContext) {
|
|
@@ -2,6 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { Text } from "react-native";
|
|
3
3
|
import { render, fireEvent } from "@testing-library/react-native";
|
|
4
4
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
5
|
+
import { UIComponentContext } from "@applicaster/zapp-react-native-ui-components/Contexts/UIComponentContext";
|
|
5
6
|
|
|
6
7
|
import { ActionButtonController } from "../ActionButtonController";
|
|
7
8
|
|
|
@@ -89,6 +90,47 @@ describe("ActionButtonController", () => {
|
|
|
89
90
|
);
|
|
90
91
|
});
|
|
91
92
|
|
|
93
|
+
it("passes the List UIComponentContext into invokeAction so the sheet can refresh it", () => {
|
|
94
|
+
const listComponent = {
|
|
95
|
+
id: "list-1",
|
|
96
|
+
data: { source: "https://example.com/user/collections/abc" },
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const actionContext = {
|
|
100
|
+
isActionAvailable: jest.fn(() => true),
|
|
101
|
+
initialEntryState: jest.fn(() => ({ active: false })),
|
|
102
|
+
invokeAction: jest.fn(),
|
|
103
|
+
addListener: jest.fn(() => jest.fn()),
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
mockUseActions.mockReturnValue(actionContext);
|
|
107
|
+
|
|
108
|
+
const { getByTestId } = render(
|
|
109
|
+
<UIComponentContext.Provider value={listComponent}>
|
|
110
|
+
<ActionButtonController
|
|
111
|
+
action={{ identifier: "open-modal-bottom-sheet-cell-action" }}
|
|
112
|
+
entry={entry}
|
|
113
|
+
>
|
|
114
|
+
{({ onPress }) => (
|
|
115
|
+
<Text testID="content" onPress={onPress}>
|
|
116
|
+
press
|
|
117
|
+
</Text>
|
|
118
|
+
)}
|
|
119
|
+
</ActionButtonController>
|
|
120
|
+
</UIComponentContext.Provider>
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
fireEvent.press(getByTestId("content"));
|
|
124
|
+
|
|
125
|
+
expect(actionContext.invokeAction).toHaveBeenCalledWith(
|
|
126
|
+
entry,
|
|
127
|
+
expect.objectContaining({
|
|
128
|
+
updateState: expect.any(Function),
|
|
129
|
+
component: listComponent,
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
92
134
|
it("updates state from addListener", () => {
|
|
93
135
|
let listener;
|
|
94
136
|
|
|
@@ -328,7 +370,7 @@ describe("ActionButtonController", () => {
|
|
|
328
370
|
|
|
329
371
|
fireEvent.press(getByTestId("content"));
|
|
330
372
|
|
|
331
|
-
expect(invokeAction).toHaveBeenCalledWith(entry);
|
|
373
|
+
expect(invokeAction).toHaveBeenCalledWith(entry, {});
|
|
332
374
|
expect(addFavourite).not.toHaveBeenCalled();
|
|
333
375
|
expect(removeFavourite).not.toHaveBeenCalled();
|
|
334
376
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
1
|
+
import { getPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
3
2
|
import { loggerLiveImageManager } from "../../../VideoLive/loggerHelper";
|
|
4
3
|
|
|
5
4
|
const { log_debug, log_error } = loggerLiveImageManager;
|
|
@@ -74,10 +73,9 @@ export async function executePreloadHooks({
|
|
|
74
73
|
let payload: any = entry;
|
|
75
74
|
|
|
76
75
|
let needsAbort = false;
|
|
77
|
-
const plugins = appStore.get("plugins");
|
|
78
76
|
|
|
79
77
|
for (const hookConfig of sortedHooks) {
|
|
80
|
-
const plugin =
|
|
78
|
+
const plugin = getPluginByIdentifier(hookConfig.identifier, true);
|
|
81
79
|
|
|
82
80
|
if (!plugin?.module) {
|
|
83
81
|
log_debug(
|
|
@@ -8,7 +8,7 @@ import Image from "../Image";
|
|
|
8
8
|
import { useTrackCurrentAutoScrollingElement } from "@applicaster/zapp-react-native-utils/reactHooks/autoscrolling/useTrackCurrentAutoScrollingElement";
|
|
9
9
|
import { useUIComponentContext } from "@applicaster/zapp-react-native-ui-components/Contexts/UIComponentContext";
|
|
10
10
|
import { getPropComponentType } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
11
|
-
import {
|
|
11
|
+
import { getPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
12
12
|
import { useAccessibilityState } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
|
|
13
13
|
|
|
14
14
|
type LiveImageProps = {
|
|
@@ -249,9 +249,8 @@ const LiveImageComponent = (props: LiveImageProps) => {
|
|
|
249
249
|
getPlayerConfig(player_screen_id, actionIdentifier) ?? {};
|
|
250
250
|
|
|
251
251
|
const playableEntry = playerPluginId ? getPlayableEntry(item) : null;
|
|
252
|
-
const plugins = appStore.get("plugins");
|
|
253
252
|
|
|
254
|
-
const playerPlugin =
|
|
253
|
+
const playerPlugin = getPluginByIdentifier(playerPluginId);
|
|
255
254
|
|
|
256
255
|
const isShouldRender =
|
|
257
256
|
playerPlugin &&
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { CollapsibleTextContainer } from "../index";
|
|
2
|
+
|
|
3
|
+
const getValue = (key: string) => {
|
|
4
|
+
const values: Record<string, unknown> = {
|
|
5
|
+
text_alignment: "left",
|
|
6
|
+
margin_top: 0,
|
|
7
|
+
margin_right: 0,
|
|
8
|
+
margin_bottom: 0,
|
|
9
|
+
margin_left: 0,
|
|
10
|
+
padding_top: 0,
|
|
11
|
+
padding_right: 0,
|
|
12
|
+
padding_bottom: 0,
|
|
13
|
+
padding_left: 0,
|
|
14
|
+
corner_radius: 0,
|
|
15
|
+
data_key: "title",
|
|
16
|
+
custom_data_key: undefined,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return values[key];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe("CollapsibleTextContainer view tree", () => {
|
|
23
|
+
it("stretches to the parent width so labels wrap using the cell width", () => {
|
|
24
|
+
const result = CollapsibleTextContainer({
|
|
25
|
+
getValue,
|
|
26
|
+
elements: [],
|
|
27
|
+
additionalStyles: { backgroundColor: "transparent" },
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
expect(result.style.alignSelf).toBe("stretch");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -3,7 +3,6 @@ import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/he
|
|
|
3
3
|
import {
|
|
4
4
|
getColorFromData,
|
|
5
5
|
getKeyForLabel,
|
|
6
|
-
mapSelfAlignment,
|
|
7
6
|
} from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
8
7
|
import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
9
8
|
|
|
@@ -23,7 +22,7 @@ export const CollapsibleTextContainer = ({
|
|
|
23
22
|
return {
|
|
24
23
|
type: "CollapsibleTextContainer", // Collapsible Text Container,
|
|
25
24
|
style: {
|
|
26
|
-
alignSelf:
|
|
25
|
+
alignSelf: "stretch",
|
|
27
26
|
marginTop: toNumberWithDefaultZero(getValue("margin_top")),
|
|
28
27
|
marginRight: toNumberWithDefaultZero(getValue("margin_right")),
|
|
29
28
|
marginBottom: toNumberWithDefaultZero(getValue("margin_bottom")),
|
|
@@ -12,6 +12,7 @@ export type ActionButtonProps = {
|
|
|
12
12
|
item: RegisteredAction;
|
|
13
13
|
configuration: any;
|
|
14
14
|
width: number;
|
|
15
|
+
component?: ZappUIComponent & { parent?: ZappUIComponent };
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
type Props = ActionButtonProps & {
|
|
@@ -27,7 +28,7 @@ type Props = ActionButtonProps & {
|
|
|
27
28
|
* (backed by the action's observable / `addListener`).
|
|
28
29
|
*/
|
|
29
30
|
export function ActionButton(props: Props) {
|
|
30
|
-
const { item, entry, onPress: onItemPress } = props;
|
|
31
|
+
const { item, entry, onPress: onItemPress, component } = props;
|
|
31
32
|
|
|
32
33
|
const action = item?.action;
|
|
33
34
|
|
|
@@ -38,9 +39,10 @@ export function ActionButton(props: Props) {
|
|
|
38
39
|
invokeAction({
|
|
39
40
|
context: "toast",
|
|
40
41
|
dismiss: () => onItemPress?.(item),
|
|
42
|
+
...(component ? { component } : {}),
|
|
41
43
|
});
|
|
42
44
|
},
|
|
43
|
-
[invokeAction, item, onItemPress]
|
|
45
|
+
[invokeAction, item, onItemPress, component]
|
|
44
46
|
);
|
|
45
47
|
|
|
46
48
|
if (!isActionAvailableFor(action, entry as ZappEntry)) return null;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { openBottomSheetModal } from "@applicaster/zapp-react-native-utils/modalState";
|
|
2
|
+
import { boundActionButton } from "../boundActionButton";
|
|
3
|
+
import { openActionsBottomSheet } from "../openActionsBottomSheet";
|
|
4
|
+
|
|
5
|
+
jest.mock("@applicaster/zapp-react-native-utils/modalState", () => ({
|
|
6
|
+
openBottomSheetModal: jest.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
jest.mock("../boundActionButton", () => ({
|
|
10
|
+
boundActionButton: jest.fn(() => jest.fn()),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
describe("openActionsBottomSheet", () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
jest.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("binds the List component into the sheet row button", () => {
|
|
19
|
+
const entry = { id: "entry-1" } as ZappEntry;
|
|
20
|
+
|
|
21
|
+
const component = {
|
|
22
|
+
data: { source: "https://example.com/user/collections/abc" },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const actions = [
|
|
26
|
+
{ identifier: "entry_action_0", action: { invokeAction: jest.fn() } },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
openActionsBottomSheet({
|
|
30
|
+
entry,
|
|
31
|
+
actions: actions as any,
|
|
32
|
+
component: component as any,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(boundActionButton).toHaveBeenCalledWith(entry, component);
|
|
36
|
+
expect(openBottomSheetModal).toHaveBeenCalledTimes(1);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -8,9 +8,12 @@ import { ActionButton, ActionButtonProps } from "./ActionButton";
|
|
|
8
8
|
* `BottomSheetModalContent` renders `buttonComponent` per item and does not
|
|
9
9
|
* know about entries, so the entry is closed over here instead.
|
|
10
10
|
*/
|
|
11
|
-
export const boundActionButton = (
|
|
11
|
+
export const boundActionButton = (
|
|
12
|
+
entry: ZappEntry | ZappFeed,
|
|
13
|
+
component?: ZappUIComponent & { parent?: ZappUIComponent }
|
|
14
|
+
) => {
|
|
12
15
|
const ActionButtonWithEntry = (props: ActionButtonProps) => (
|
|
13
|
-
<ActionButton {...props} entry={entry as ZappEntry} />
|
|
16
|
+
<ActionButton {...props} entry={entry as ZappEntry} component={component} />
|
|
14
17
|
);
|
|
15
18
|
|
|
16
19
|
ActionButtonWithEntry.displayName = "ActionButtonWithEntry";
|
|
@@ -17,6 +17,8 @@ export type OpenActionsBottomSheetArgs = {
|
|
|
17
17
|
actions: RegisteredAction[];
|
|
18
18
|
title?: string;
|
|
19
19
|
summary?: string;
|
|
20
|
+
/** List that opened the sheet; forwarded into row invokeAction context. */
|
|
21
|
+
component?: ZappUIComponent & { parent?: ZappUIComponent };
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
/**
|
|
@@ -32,6 +34,7 @@ export function openActionsBottomSheet({
|
|
|
32
34
|
actions,
|
|
33
35
|
title,
|
|
34
36
|
summary,
|
|
37
|
+
component,
|
|
35
38
|
}: OpenActionsBottomSheetArgs): void {
|
|
36
39
|
if (!actions?.length) {
|
|
37
40
|
// Every action resolved away - unavailable for this entry, or not
|
|
@@ -50,7 +53,7 @@ export function openActionsBottomSheet({
|
|
|
50
53
|
// BottomSheetModalContent still requires an onPress.
|
|
51
54
|
onPress: noop,
|
|
52
55
|
items: actions,
|
|
53
|
-
buttonComponent: boundActionButton(entry),
|
|
56
|
+
buttonComponent: boundActionButton(entry, component),
|
|
54
57
|
title,
|
|
55
58
|
summary,
|
|
56
59
|
},
|
|
@@ -17,6 +17,9 @@ import {
|
|
|
17
17
|
Text,
|
|
18
18
|
View,
|
|
19
19
|
} from "react-native";
|
|
20
|
+
import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
21
|
+
|
|
22
|
+
const logger = createLogger({ category: "ModalComponent:AudioPlayerButton" });
|
|
20
23
|
|
|
21
24
|
const styles = StyleSheet.create({
|
|
22
25
|
container: {
|
|
@@ -130,6 +133,28 @@ export const DEFAULT_ICONS_CONFIGURATION: IconsConfiguration = {
|
|
|
130
133
|
},
|
|
131
134
|
};
|
|
132
135
|
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Variant configurations
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
export const VARIANT_TEXT_ONLY: ButtonConfiguration = {
|
|
141
|
+
background: DEFAULT_BACKGROUND_CONFIGURATION,
|
|
142
|
+
title: DEFAULT_TITLE_CONFIGURATION,
|
|
143
|
+
icons: DEFAULT_ICONS_CONFIGURATION,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export const VARIANT_WITH_ICON: ButtonConfiguration = {
|
|
147
|
+
background: DEFAULT_BACKGROUND_CONFIGURATION,
|
|
148
|
+
title: DEFAULT_TITLE_CONFIGURATION,
|
|
149
|
+
icons: {
|
|
150
|
+
...DEFAULT_ICONS_CONFIGURATION,
|
|
151
|
+
leadingIcon: {
|
|
152
|
+
enabled: true,
|
|
153
|
+
actionType: "addToPlaylist",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
133
158
|
// ---------------------------------------------------------------------------
|
|
134
159
|
// Data (runtime / localization — not styling config)
|
|
135
160
|
// ---------------------------------------------------------------------------
|
|
@@ -140,30 +165,15 @@ export type ButtonData = {
|
|
|
140
165
|
};
|
|
141
166
|
|
|
142
167
|
export type AudioPlayerButtonProps = {
|
|
143
|
-
configuration
|
|
144
|
-
data
|
|
168
|
+
configuration?: ButtonConfiguration;
|
|
169
|
+
data?: ButtonData;
|
|
145
170
|
disabled?: boolean;
|
|
146
171
|
focused?: boolean;
|
|
147
172
|
onPress?: () => void;
|
|
148
173
|
};
|
|
149
174
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
// ---------------------------------------------------------------------------
|
|
153
|
-
|
|
154
|
-
// Hardcoded: action type → icon asset mapping (base64 PNG)
|
|
155
|
-
const ACTION_ICON_ASSETS: Record<ButtonActionType, string> = {
|
|
156
|
-
addToQueue:
|
|
157
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAKUlEQVR42mNgGAVDCfxHw6MWjFpAvEGk4lELKLdgNJmOWkA7C0YBbQAAwiZvkR9eFgQAAAAASUVORK5CYII=",
|
|
158
|
-
addToPlaylist:
|
|
159
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAJklEQVR42mNgGAUjFvzHgmluATF4cFswGsmjkTwayaORPAqGLwAA6Jpxj9Ms9QMAAAAASUVORK5CYII=",
|
|
160
|
-
goToPlaylist:
|
|
161
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAALklEQVR42mNgGAWjYNiD/7Q2/D8tLflPiSX/ycQ0t+D/yLGAEkeMWjAKRgG9AQB4zlSshwSzWwAAAABJRU5ErkJggg==",
|
|
162
|
-
confirm:
|
|
163
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAJElEQVR42mNgGAWjYBSQCv4TwKMWDHELho/hNE/3o2AUDDcAAPv0QMAyy1CtAAAAAElFTkSuQmCC",
|
|
164
|
-
cancel:
|
|
165
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAASElEQVR42mNgGAWjGvynkVoUTf+pqA6nxv8UqiHbEooNx2cQ1QzHZiDVDcdlCcOQsoCmQUTTSKZpMqV5RqNLUUHTwm4UDBMAALBLQ73LTvoyAAAAAElFTkSuQmCC",
|
|
166
|
-
};
|
|
175
|
+
const PLACEHOLDER_RED_BOX =
|
|
176
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAJ0lEQVR4nO3NMQEAAAjDsIF/z2ACvlRAU5NMHuvPOQAAAAAAAMBdC/07Ai5sNw+CAAAAAElFTkSuQmCC";
|
|
167
177
|
|
|
168
178
|
function getTextTransformStyle(
|
|
169
179
|
textTransform: TextTransform
|
|
@@ -276,13 +286,19 @@ type ButtonLeadingIconProps = {
|
|
|
276
286
|
configuration: IconsConfiguration;
|
|
277
287
|
};
|
|
278
288
|
|
|
279
|
-
function ButtonLeadingIcon({
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
289
|
+
function ButtonLeadingIcon({ configuration }: ButtonLeadingIconProps) {
|
|
290
|
+
const asset = configuration.leadingIcon.asset || PLACEHOLDER_RED_BOX;
|
|
291
|
+
|
|
292
|
+
React.useEffect(() => {
|
|
293
|
+
if (!configuration.leadingIcon.asset) {
|
|
294
|
+
logger.log_warning(
|
|
295
|
+
"AudioPlayerButton has leadingIcon enabled but no asset was provided. Falling back to placeholder red box. Ensure the relevant plugin (e.g. playlist-experience) provides leadingIcon.asset in styleOverrides.",
|
|
296
|
+
{
|
|
297
|
+
leadingIcon: configuration.leadingIcon,
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}, [configuration.leadingIcon.asset]);
|
|
286
302
|
|
|
287
303
|
if (!asset) {
|
|
288
304
|
return null;
|
|
@@ -308,13 +324,14 @@ import { useModalBlocksStyle } from "../../ModalBlocksStyleContext";
|
|
|
308
324
|
import { mergeButtonConfiguration } from "../utils/mergeStyles";
|
|
309
325
|
|
|
310
326
|
export function AudioPlayerButton({
|
|
311
|
-
configuration,
|
|
312
|
-
data,
|
|
327
|
+
configuration = VARIANT_WITH_ICON,
|
|
328
|
+
data = { title: undefined },
|
|
313
329
|
disabled,
|
|
314
330
|
focused = false,
|
|
315
331
|
onPress,
|
|
316
332
|
}: AudioPlayerButtonProps) {
|
|
317
333
|
const modalBlocksStyle = useModalBlocksStyle();
|
|
334
|
+
|
|
318
335
|
const buttonBlock = modalBlocksStyle?.button;
|
|
319
336
|
const actionBlock = modalBlocksStyle?.action;
|
|
320
337
|
|
|
@@ -328,9 +345,21 @@ export function AudioPlayerButton({
|
|
|
328
345
|
icons: iconsConfiguration,
|
|
329
346
|
} = effectiveConfig;
|
|
330
347
|
|
|
331
|
-
const
|
|
348
|
+
const title = data?.title || buttonBlock?.title.default;
|
|
332
349
|
const isDisabled = disabled ?? data.disabled ?? false;
|
|
333
350
|
|
|
351
|
+
React.useEffect(() => {
|
|
352
|
+
if (!title) {
|
|
353
|
+
logger.log_warning(
|
|
354
|
+
"Create New Playlist button rendering with empty title. Ensure data.title is set for AudioPlayerButton or styleOverrides providing button title.",
|
|
355
|
+
{
|
|
356
|
+
leadingIcon: iconsConfiguration.leadingIcon,
|
|
357
|
+
modalBlocksStyle: modalBlocksStyle,
|
|
358
|
+
}
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
}, [title]);
|
|
362
|
+
|
|
334
363
|
const { leadingIcon } = iconsConfiguration;
|
|
335
364
|
const showIcon = shouldShowLeadingIcon(leadingIcon);
|
|
336
365
|
|
|
@@ -361,47 +390,3 @@ export function AudioPlayerButton({
|
|
|
361
390
|
</ButtonContainer>
|
|
362
391
|
);
|
|
363
392
|
}
|
|
364
|
-
|
|
365
|
-
// ---------------------------------------------------------------------------
|
|
366
|
-
// Snack demo — two Button variants from configuration only
|
|
367
|
-
// ---------------------------------------------------------------------------
|
|
368
|
-
|
|
369
|
-
export const VARIANT_TEXT_ONLY: ButtonConfiguration = {
|
|
370
|
-
background: DEFAULT_BACKGROUND_CONFIGURATION,
|
|
371
|
-
title: DEFAULT_TITLE_CONFIGURATION,
|
|
372
|
-
icons: DEFAULT_ICONS_CONFIGURATION,
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
export const VARIANT_WITH_ICON: ButtonConfiguration = {
|
|
376
|
-
background: DEFAULT_BACKGROUND_CONFIGURATION,
|
|
377
|
-
title: DEFAULT_TITLE_CONFIGURATION,
|
|
378
|
-
icons: {
|
|
379
|
-
...DEFAULT_ICONS_CONFIGURATION,
|
|
380
|
-
leadingIcon: {
|
|
381
|
-
enabled: true,
|
|
382
|
-
actionType: "addToPlaylist",
|
|
383
|
-
},
|
|
384
|
-
},
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
export const VARIANT_UPPERCASE_TITLE: ButtonConfiguration = {
|
|
388
|
-
background: DEFAULT_BACKGROUND_CONFIGURATION,
|
|
389
|
-
title: {
|
|
390
|
-
...DEFAULT_TITLE_CONFIGURATION,
|
|
391
|
-
textTransform: "uppercase",
|
|
392
|
-
},
|
|
393
|
-
icons: DEFAULT_ICONS_CONFIGURATION,
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
export const VARIANT_LARGE_ICON: ButtonConfiguration = {
|
|
397
|
-
background: DEFAULT_BACKGROUND_CONFIGURATION,
|
|
398
|
-
title: DEFAULT_TITLE_CONFIGURATION,
|
|
399
|
-
icons: {
|
|
400
|
-
width: 32,
|
|
401
|
-
height: 32,
|
|
402
|
-
leadingIcon: {
|
|
403
|
-
enabled: true,
|
|
404
|
-
actionType: "addToQueue",
|
|
405
|
-
},
|
|
406
|
-
},
|
|
407
|
-
};
|