feat: Add AudioWidget (#7179)
* Create initial version of AudioWidget by copying VideoWidget * Add EventType for AUDIO * Change default Audio URL to a podcast related to Appsmith * Add AudioWidget icon * Change Entity definition for AudioWidget * Add cypress test * Add jest test * fix: typo
This commit is contained in:
parent
f3afa81afe
commit
8a45e1507e
49
app/client/cypress/fixtures/audioWidgetDsl.json
Normal file
49
app/client/cypress/fixtures/audioWidgetDsl.json
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"dsl": {
|
||||||
|
"widgetName": "MainContainer",
|
||||||
|
"backgroundColor": "none",
|
||||||
|
"rightColumn": 915.6499999999999,
|
||||||
|
"snapColumns": 64,
|
||||||
|
"detachFromLayout": true,
|
||||||
|
"widgetId": "0",
|
||||||
|
"topRow": 0,
|
||||||
|
"bottomRow": 750,
|
||||||
|
"containerStyle": "none",
|
||||||
|
"snapRows": 125,
|
||||||
|
"parentRowSpace": 1,
|
||||||
|
"type": "CANVAS_WIDGET",
|
||||||
|
"canExtend": true,
|
||||||
|
"version": 38,
|
||||||
|
"minHeight": 760,
|
||||||
|
"parentColumnSpace": 1,
|
||||||
|
"dynamicBindingPathList": [],
|
||||||
|
"leftColumn": 0,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"isVisible": true,
|
||||||
|
"widgetName": "Audio1",
|
||||||
|
"url": "https://cdn.simplecast.com/audio/10488ddf-3ca4-4300-9391-c2967d806334/episodes/8c8341f0-0a3a-4f2c-bfe0-0abb6b3c1c87/audio/03e2e3d8-e703-4953-adc0-e72687f31178/default_tc.mp3",
|
||||||
|
"autoPlay": false,
|
||||||
|
"version": 1,
|
||||||
|
"type": "AUDIO_WIDGET",
|
||||||
|
"hideCard": false,
|
||||||
|
"displayName": "Audio",
|
||||||
|
"key": "qdo3rra7pr",
|
||||||
|
"iconSVG": "/static/media/icon.26d597b5.svg",
|
||||||
|
"widgetId": "ttnnyvbw2c",
|
||||||
|
"renderMode": "CANVAS",
|
||||||
|
"isLoading": false,
|
||||||
|
"parentColumnSpace": 14.609375,
|
||||||
|
"parentRowSpace": 10,
|
||||||
|
"leftColumn": 5,
|
||||||
|
"rightColumn": 33,
|
||||||
|
"topRow": 11,
|
||||||
|
"bottomRow": 15,
|
||||||
|
"parentId": "0",
|
||||||
|
"dynamicBindingPathList": [],
|
||||||
|
"dynamicTriggerPathList": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dynamicTriggerPathList": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -69,6 +69,7 @@
|
||||||
"input2": "(//div[@class='bp3-input-group']//input)[1]",
|
"input2": "(//div[@class='bp3-input-group']//input)[1]",
|
||||||
"input3": "(//div[@class='bp3-input-group']//input)[2]",
|
"input3": "(//div[@class='bp3-input-group']//input)[2]",
|
||||||
"videoUrl": "https://www.youtube.com/watch?v=S5musXykVs0",
|
"videoUrl": "https://www.youtube.com/watch?v=S5musXykVs0",
|
||||||
|
"audioUrl": "https://cdn.simplecast.com/audio/10488ddf-3ca4-4300-9391-c2967d806334/episodes/8c8341f0-0a3a-4f2c-bfe0-0abb6b3c1c87/audio/03e2e3d8-e703-4953-adc0-e72687f31178/default_tc.mp3",
|
||||||
"TablePagination": [
|
"TablePagination": [
|
||||||
{
|
{
|
||||||
"id": 2381224,
|
"id": 2381224,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
const widgetsPage = require("../../../../locators/Widgets.json");
|
||||||
|
const commonlocators = require("../../../../locators/commonlocators.json");
|
||||||
|
const dsl = require("../../../../fixtures/audioWidgetDsl.json");
|
||||||
|
const testdata = require("../../../../fixtures/testdata.json");
|
||||||
|
|
||||||
|
describe("Audio Widget Functionality", function() {
|
||||||
|
before(() => {
|
||||||
|
cy.addDsl(dsl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Audio Widget play functionality validation", function() {
|
||||||
|
cy.openPropertyPane("audiowidget");
|
||||||
|
cy.widgetText("Audio1", widgetsPage.audioWidget, commonlocators.audioInner);
|
||||||
|
cy.get(commonlocators.onPlay).click();
|
||||||
|
cy.selectShowMsg();
|
||||||
|
cy.addSuccessMessage("Play success");
|
||||||
|
cy.get(widgetsPage.autoPlay).click();
|
||||||
|
cy.wait("@updateLayout").should(
|
||||||
|
"have.nested.property",
|
||||||
|
"response.body.responseMeta.status",
|
||||||
|
200,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Audio widget pause functionality validation", function() {
|
||||||
|
cy.get(commonlocators.onPause).click();
|
||||||
|
cy.selectShowMsg();
|
||||||
|
cy.addSuccessMessage("Pause success");
|
||||||
|
cy.get(widgetsPage.autoPlay).click();
|
||||||
|
cy.wait("@updateLayout").should(
|
||||||
|
"have.nested.property",
|
||||||
|
"response.body.responseMeta.status",
|
||||||
|
200,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Update audio url and check play and pause functionality validation", function() {
|
||||||
|
cy.testCodeMirror(testdata.audioUrl);
|
||||||
|
cy.get(".CodeMirror textarea")
|
||||||
|
.first()
|
||||||
|
.blur();
|
||||||
|
cy.get(widgetsPage.autoPlay).click({ force: true });
|
||||||
|
cy.wait("@updateLayout").should(
|
||||||
|
"have.nested.property",
|
||||||
|
"response.body.responseMeta.status",
|
||||||
|
200,
|
||||||
|
);
|
||||||
|
cy.get(widgetsPage.autoPlay).click({ force: true });
|
||||||
|
cy.wait("@updateLayout").should(
|
||||||
|
"have.nested.property",
|
||||||
|
"response.body.responseMeta.status",
|
||||||
|
200,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
"SearchTextChangeAction": ".t--property-control-onsearchtextchanged button",
|
"SearchTextChangeAction": ".t--property-control-onsearchtextchanged button",
|
||||||
"tableSearchTextChangeSelected": ".t--property-control-onsearchtextchanged",
|
"tableSearchTextChangeSelected": ".t--property-control-onsearchtextchanged",
|
||||||
"videoWidget": ".t--draggable-videowidget",
|
"videoWidget": ".t--draggable-videowidget",
|
||||||
|
"audioWidget": ".t--draggable-audiowidget",
|
||||||
"autoPlay": ".t--property-control-autoplay > .bp3-control > .bp3-control-indicator",
|
"autoPlay": ".t--property-control-autoplay > .bp3-control > .bp3-control-indicator",
|
||||||
"defaultOption": ".t--property-control-defaultoption .CodeMirror-code",
|
"defaultOption": ".t--property-control-defaultoption .CodeMirror-code",
|
||||||
"dropdownSingleSelect": ".bp3-popover-target > div > .bp3-button",
|
"dropdownSingleSelect": ".bp3-popover-target > div > .bp3-button",
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@
|
||||||
"toastAction": ".t--toast-action",
|
"toastAction": ".t--toast-action",
|
||||||
"toastBody": ".Toastify__toast-body",
|
"toastBody": ".Toastify__toast-body",
|
||||||
"videoInner": ".t--draggable-videowidget span.t--widget-name",
|
"videoInner": ".t--draggable-videowidget span.t--widget-name",
|
||||||
|
"audioInner": ".t--draggable-audiowidget span.t--widget-name",
|
||||||
"onPlay": ".t--property-control-onplay .t--open-dropdown-Select-Action",
|
"onPlay": ".t--property-control-onplay .t--open-dropdown-Select-Action",
|
||||||
"chooseAction": ".single-select",
|
"chooseAction": ".single-select",
|
||||||
"chooseMsgType": ".t--open-dropdown-Select-type",
|
"chooseMsgType": ".t--open-dropdown-Select-type",
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,10 @@ export enum EventType {
|
||||||
ON_VIDEO_END = "ON_VIDEO_END",
|
ON_VIDEO_END = "ON_VIDEO_END",
|
||||||
ON_VIDEO_PLAY = "ON_VIDEO_PLAY",
|
ON_VIDEO_PLAY = "ON_VIDEO_PLAY",
|
||||||
ON_VIDEO_PAUSE = "ON_VIDEO_PAUSE",
|
ON_VIDEO_PAUSE = "ON_VIDEO_PAUSE",
|
||||||
|
ON_AUDIO_START = "ON_AUDIO_START",
|
||||||
|
ON_AUDIO_END = "ON_AUDIO_END",
|
||||||
|
ON_AUDIO_PLAY = "ON_AUDIO_PLAY",
|
||||||
|
ON_AUDIO_PAUSE = "ON_AUDIO_PAUSE",
|
||||||
ON_RATE_CHANGED = "ON_RATE_CHANGED",
|
ON_RATE_CHANGED = "ON_RATE_CHANGED",
|
||||||
ON_IFRAME_URL_CHANGED = "ON_IFRAME_URL_CHANGED",
|
ON_IFRAME_URL_CHANGED = "ON_IFRAME_URL_CHANGED",
|
||||||
ON_IFRAME_MESSAGE_RECEIVED = "ON_IFRAME_MESSAGE_RECEIVED",
|
ON_IFRAME_MESSAGE_RECEIVED = "ON_IFRAME_MESSAGE_RECEIVED",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
export const HelpMap: Record<string, { path: string; searchKey: string }> = {
|
export const HelpMap: Record<string, { path: string; searchKey: string }> = {
|
||||||
|
AUDIO_WIDGET: {
|
||||||
|
path: "/widget-reference/audio",
|
||||||
|
searchKey: "Audio",
|
||||||
|
},
|
||||||
CONTAINER_WIDGET: {
|
CONTAINER_WIDGET: {
|
||||||
path: "/widget-reference/container",
|
path: "/widget-reference/container",
|
||||||
searchKey: "Container",
|
searchKey: "Container",
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ export const FORM_VALIDATION_EMPTY_EMAIL = () => `Please enter an email`;
|
||||||
export const FORM_VALIDATION_INVALID_EMAIL = () =>
|
export const FORM_VALIDATION_INVALID_EMAIL = () =>
|
||||||
`Please provide a valid email address`;
|
`Please provide a valid email address`;
|
||||||
export const ENTER_VIDEO_URL = () => `Please provide a valid url`;
|
export const ENTER_VIDEO_URL = () => `Please provide a valid url`;
|
||||||
|
export const ENTER_AUDIO_URL = () => `Please provide a valid url`;
|
||||||
|
|
||||||
export const FORM_VALIDATION_EMPTY_PASSWORD = () => `Please enter the password`;
|
export const FORM_VALIDATION_EMPTY_PASSWORD = () => `Please enter the password`;
|
||||||
export const FORM_VALIDATION_PASSWORD_RULE = () =>
|
export const FORM_VALIDATION_PASSWORD_RULE = () =>
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,9 @@ import StatboxWidget, {
|
||||||
import FilePickerWidgetV2, {
|
import FilePickerWidgetV2, {
|
||||||
CONFIG as FILEPICKER_WIDGET_V2_CONFIG,
|
CONFIG as FILEPICKER_WIDGET_V2_CONFIG,
|
||||||
} from "widgets/FilePickerWidgetV2";
|
} from "widgets/FilePickerWidgetV2";
|
||||||
|
import AudioWidget, {
|
||||||
|
CONFIG as AUDIO_WIDGET_CONFIG,
|
||||||
|
} from "widgets/AudioWidget";
|
||||||
|
|
||||||
import AudioRecorderWidget, {
|
import AudioRecorderWidget, {
|
||||||
CONFIG as AUDIO_RECORDER_WIDGET_CONFIG,
|
CONFIG as AUDIO_RECORDER_WIDGET_CONFIG,
|
||||||
|
|
@ -143,6 +146,7 @@ export const registerWidgets = () => {
|
||||||
registerWidget(AudioRecorderWidget, AUDIO_RECORDER_WIDGET_CONFIG);
|
registerWidget(AudioRecorderWidget, AUDIO_RECORDER_WIDGET_CONFIG);
|
||||||
registerWidget(MultiSelectTreeWidget, MULTI_SELECT_TREE_WIDGET_CONFIG);
|
registerWidget(MultiSelectTreeWidget, MULTI_SELECT_TREE_WIDGET_CONFIG);
|
||||||
registerWidget(SingleSelectTreeWidget, SINGLE_SELECT_TREE_WIDGET_CONFIG);
|
registerWidget(SingleSelectTreeWidget, SINGLE_SELECT_TREE_WIDGET_CONFIG);
|
||||||
|
registerWidget(AudioWidget, AUDIO_WIDGET_CONFIG);
|
||||||
|
|
||||||
log.debug("Widget registration took: ", performance.now() - start, "ms");
|
log.debug("Widget registration took: ", performance.now() - start, "ms");
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,13 @@ export const entityDefinitions: Record<string, unknown> = {
|
||||||
clear: "fn() -> void",
|
clear: "fn() -> void",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
AUDIO_WIDGET: {
|
||||||
|
"!doc":
|
||||||
|
"Audio widget can be used for playing a variety of audio formats like MP3, AAC etc.",
|
||||||
|
"!url": "https://docs.appsmith.com/widget-reference/audio",
|
||||||
|
playState: "number",
|
||||||
|
autoPlay: "bool",
|
||||||
|
},
|
||||||
CONTAINER_WIDGET: {
|
CONTAINER_WIDGET: {
|
||||||
"!doc":
|
"!doc":
|
||||||
"Containers are used to group widgets together to form logical higher order widgets. Containers let you organize your page better and move all the widgets inside them together.",
|
"Containers are used to group widgets together to form logical higher order widgets. Containers let you organize your page better and move all the widgets inside them together.",
|
||||||
|
|
|
||||||
68
app/client/src/widgets/AudioWidget/component/index.tsx
Normal file
68
app/client/src/widgets/AudioWidget/component/index.tsx
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
import ReactPlayer from "react-player";
|
||||||
|
import React, { Ref } from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import { createMessage, ENTER_AUDIO_URL } from "constants/messages";
|
||||||
|
export interface AudioComponentProps {
|
||||||
|
url?: string;
|
||||||
|
autoplay?: boolean;
|
||||||
|
controls?: boolean;
|
||||||
|
onStart?: () => void;
|
||||||
|
onPlay?: () => void;
|
||||||
|
onPause?: () => void;
|
||||||
|
onEnded?: () => void;
|
||||||
|
onReady?: () => void;
|
||||||
|
onProgress?: () => void;
|
||||||
|
onSeek?: () => void;
|
||||||
|
onError?: () => void;
|
||||||
|
player?: Ref<ReactPlayer>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ErrorContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Error = styled.span``;
|
||||||
|
|
||||||
|
export default function AudioComponent(props: AudioComponentProps) {
|
||||||
|
const {
|
||||||
|
autoplay,
|
||||||
|
controls,
|
||||||
|
onEnded,
|
||||||
|
onError,
|
||||||
|
onPause,
|
||||||
|
onPlay,
|
||||||
|
onProgress,
|
||||||
|
onReady,
|
||||||
|
onSeek,
|
||||||
|
onStart,
|
||||||
|
player,
|
||||||
|
url,
|
||||||
|
} = props;
|
||||||
|
return url ? (
|
||||||
|
<ReactPlayer
|
||||||
|
controls={controls || true}
|
||||||
|
height="100%"
|
||||||
|
onEnded={onEnded}
|
||||||
|
onError={onError}
|
||||||
|
onPause={onPause}
|
||||||
|
onPlay={onPlay}
|
||||||
|
onProgress={onProgress}
|
||||||
|
onReady={onReady}
|
||||||
|
onSeek={onSeek}
|
||||||
|
onStart={onStart}
|
||||||
|
pip={false}
|
||||||
|
playing={autoplay}
|
||||||
|
ref={player}
|
||||||
|
url={url}
|
||||||
|
width="100%"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ErrorContainer>
|
||||||
|
<Error>{createMessage(ENTER_AUDIO_URL)}</Error>
|
||||||
|
</ErrorContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
8
app/client/src/widgets/AudioWidget/icon.svg
Normal file
8
app/client/src/widgets/AudioWidget/icon.svg
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M0.5 11V3H2.5V11H0.5Z" fill="#EAEAEA"/>
|
||||||
|
<path d="M3.5 13V1H5.5V13H3.5Z" fill="#EAEAEA"/>
|
||||||
|
<path d="M6.5 10V5H8.5V10H6.5Z" fill="#EAEAEA"/>
|
||||||
|
<path d="M9.5 14V0H11.5V14H9.5Z" fill="#EAEAEA"/>
|
||||||
|
<path d="M12.5 12V2H14.5V12H12.5Z" fill="#EAEAEA"/>
|
||||||
|
<path d="M15.5 10V4H17.5V10H15.5Z" fill="#EAEAEA"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 404 B |
27
app/client/src/widgets/AudioWidget/index.tsx
Normal file
27
app/client/src/widgets/AudioWidget/index.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import Widget from "./widget";
|
||||||
|
import IconSVG from "./icon.svg";
|
||||||
|
import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants";
|
||||||
|
|
||||||
|
export const CONFIG = {
|
||||||
|
type: Widget.getWidgetType(),
|
||||||
|
name: "Audio",
|
||||||
|
iconSVG: IconSVG,
|
||||||
|
needsMeta: true,
|
||||||
|
defaults: {
|
||||||
|
rows: 1 * GRID_DENSITY_MIGRATION_V1,
|
||||||
|
columns: 7 * GRID_DENSITY_MIGRATION_V1,
|
||||||
|
widgetName: "Audio",
|
||||||
|
url:
|
||||||
|
"https://cdn.simplecast.com/audio/10488ddf-3ca4-4300-9391-c2967d806334/episodes/8c8341f0-0a3a-4f2c-bfe0-0abb6b3c1c87/audio/03e2e3d8-e703-4953-adc0-e72687f31178/default_tc.mp3",
|
||||||
|
autoPlay: false,
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
derived: Widget.getDerivedPropertiesMap(),
|
||||||
|
default: Widget.getDefaultPropertiesMap(),
|
||||||
|
meta: Widget.getMetaPropertiesMap(),
|
||||||
|
config: Widget.getPropertyPaneConfig(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Widget;
|
||||||
40
app/client/src/widgets/AudioWidget/widget/index.test.tsx
Normal file
40
app/client/src/widgets/AudioWidget/widget/index.test.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { PropertyPaneControlConfig } from "constants/PropertyControlConstants";
|
||||||
|
import AudioWidget from ".";
|
||||||
|
|
||||||
|
const urlTests = [
|
||||||
|
{ url: "https://appsmith.com/", isValid: true },
|
||||||
|
{ url: "http://appsmith.com/", isValid: true },
|
||||||
|
{ url: "appsmith.com/", isValid: true },
|
||||||
|
{ url: "appsmith.com", isValid: true },
|
||||||
|
{ url: "release.appsmith.com", isValid: true },
|
||||||
|
{ url: "appsmith.com/audio.mp3", isValid: true },
|
||||||
|
{ url: "appsmith./audio.mp3", isValid: false },
|
||||||
|
{ url: "https://appsmith.com/randompath/somefile.mp3", isValid: true },
|
||||||
|
{ url: "https://appsmith.com/randompath/some file.mp3", isValid: true },
|
||||||
|
{ url: "random string", isValid: false },
|
||||||
|
{
|
||||||
|
url: "blob:https://dev.appsmith.com/9db94f56-5e32-4b18-2758-64c21a7f4610",
|
||||||
|
isValid: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe("urlRegexValidation", () => {
|
||||||
|
const generalSectionProperties: PropertyPaneControlConfig[] = AudioWidget.getPropertyPaneConfig().filter(
|
||||||
|
(x) => x.sectionName === "General",
|
||||||
|
)[0].children;
|
||||||
|
const urlPropertyControl = generalSectionProperties.filter(
|
||||||
|
(x) => x.propertyName === "url",
|
||||||
|
)[0];
|
||||||
|
const regEx = urlPropertyControl.validation?.params?.regex;
|
||||||
|
|
||||||
|
it("validate existence of regEx", () => {
|
||||||
|
expect(regEx).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("test regEx", () => {
|
||||||
|
urlTests.forEach((test) => {
|
||||||
|
if (test.isValid) expect(test.url).toMatch(regEx || "");
|
||||||
|
else expect(test.url).not.toMatch(regEx || "");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
175
app/client/src/widgets/AudioWidget/widget/index.tsx
Normal file
175
app/client/src/widgets/AudioWidget/widget/index.tsx
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
import React, { Suspense, lazy } from "react";
|
||||||
|
import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget";
|
||||||
|
import { WidgetType } from "constants/WidgetConstants";
|
||||||
|
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
|
||||||
|
import { ValidationTypes } from "constants/WidgetValidation";
|
||||||
|
import Skeleton from "components/utils/Skeleton";
|
||||||
|
import { retryPromise } from "utils/AppsmithUtils";
|
||||||
|
import ReactPlayer from "react-player";
|
||||||
|
import { AutocompleteDataType } from "utils/autocomplete/TernServer";
|
||||||
|
|
||||||
|
const AudioComponent = lazy(() => retryPromise(() => import("../component")));
|
||||||
|
|
||||||
|
export enum PlayState {
|
||||||
|
NOT_STARTED = "NOT_STARTED",
|
||||||
|
PAUSED = "PAUSED",
|
||||||
|
ENDED = "ENDED",
|
||||||
|
PLAYING = "PLAYING",
|
||||||
|
}
|
||||||
|
|
||||||
|
class AudioWidget extends BaseWidget<AudioWidgetProps, WidgetState> {
|
||||||
|
static getPropertyPaneConfig() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
sectionName: "General",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
propertyName: "url",
|
||||||
|
label: "URL",
|
||||||
|
controlType: "INPUT_TEXT",
|
||||||
|
placeholderText: "Enter url",
|
||||||
|
inputType: "TEXT",
|
||||||
|
isBindProperty: true,
|
||||||
|
isTriggerProperty: false,
|
||||||
|
validation: {
|
||||||
|
type: ValidationTypes.TEXT,
|
||||||
|
params: {
|
||||||
|
regex: /(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/,
|
||||||
|
expected: {
|
||||||
|
type: "Audio URL",
|
||||||
|
example:
|
||||||
|
"https://cdn.simplecast.com/audio/10488ddf-3ca4-4300-9391-c2967d806334/episodes/8c8341f0-0a3a-4f2c-bfe0-0abb6b3c1c87/audio/03e2e3d8-e703-4953-adc0-e72687f31178/default_tc.mp3",
|
||||||
|
autocompleteDataType: AutocompleteDataType.STRING,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
propertyName: "autoPlay",
|
||||||
|
label: "Auto Play",
|
||||||
|
helpText: "Audio will be automatically played",
|
||||||
|
controlType: "SWITCH",
|
||||||
|
isJSConvertible: true,
|
||||||
|
isBindProperty: true,
|
||||||
|
isTriggerProperty: false,
|
||||||
|
validation: { type: ValidationTypes.BOOLEAN },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
helpText: "Controls the visibility of the widget",
|
||||||
|
propertyName: "isVisible",
|
||||||
|
label: "Visible",
|
||||||
|
controlType: "SWITCH",
|
||||||
|
isJSConvertible: true,
|
||||||
|
isBindProperty: true,
|
||||||
|
isTriggerProperty: false,
|
||||||
|
validation: { type: ValidationTypes.BOOLEAN },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sectionName: "Actions",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
helpText: "Triggers an action when the audio is played",
|
||||||
|
propertyName: "onPlay",
|
||||||
|
label: "onPlay",
|
||||||
|
controlType: "ACTION_SELECTOR",
|
||||||
|
isJSConvertible: true,
|
||||||
|
isBindProperty: true,
|
||||||
|
isTriggerProperty: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
helpText: "Triggers an action when the audio is paused",
|
||||||
|
propertyName: "onPause",
|
||||||
|
label: "onPause",
|
||||||
|
controlType: "ACTION_SELECTOR",
|
||||||
|
isJSConvertible: true,
|
||||||
|
isBindProperty: true,
|
||||||
|
isTriggerProperty: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
helpText: "Triggers an action when the audio ends",
|
||||||
|
propertyName: "onEnd",
|
||||||
|
label: "onEnd",
|
||||||
|
controlType: "ACTION_SELECTOR",
|
||||||
|
isJSConvertible: true,
|
||||||
|
isBindProperty: true,
|
||||||
|
isTriggerProperty: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private _player = React.createRef<ReactPlayer>();
|
||||||
|
|
||||||
|
static getMetaPropertiesMap(): Record<string, any> {
|
||||||
|
return {
|
||||||
|
playState: PlayState.NOT_STARTED,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDefaultPropertiesMap(): Record<string, string> {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
getPageView() {
|
||||||
|
const { autoPlay, onEnd, onPause, onPlay, url } = this.props;
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<Skeleton />}>
|
||||||
|
<AudioComponent
|
||||||
|
autoplay={autoPlay}
|
||||||
|
controls
|
||||||
|
onEnded={() => {
|
||||||
|
this.props.updateWidgetMetaProperty("playState", PlayState.ENDED, {
|
||||||
|
triggerPropertyName: "onEnd",
|
||||||
|
dynamicString: onEnd,
|
||||||
|
event: {
|
||||||
|
type: EventType.ON_AUDIO_END,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onPause={() => {
|
||||||
|
//TODO: We do not want the pause event for onSeek or onEnd.
|
||||||
|
this.props.updateWidgetMetaProperty("playState", PlayState.PAUSED, {
|
||||||
|
triggerPropertyName: "onPause",
|
||||||
|
dynamicString: onPause,
|
||||||
|
event: {
|
||||||
|
type: EventType.ON_AUDIO_PAUSE,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onPlay={() => {
|
||||||
|
this.props.updateWidgetMetaProperty(
|
||||||
|
"playState",
|
||||||
|
PlayState.PLAYING,
|
||||||
|
{
|
||||||
|
triggerPropertyName: "onPlay",
|
||||||
|
dynamicString: onPlay,
|
||||||
|
event: {
|
||||||
|
type: EventType.ON_AUDIO_PLAY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
player={this._player}
|
||||||
|
url={url}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getWidgetType(): WidgetType {
|
||||||
|
return "AUDIO_WIDGET";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AudioWidgetProps extends WidgetProps {
|
||||||
|
url: string;
|
||||||
|
autoPlay: boolean;
|
||||||
|
onPause?: string;
|
||||||
|
onPlay?: string;
|
||||||
|
onEnd?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AudioWidget;
|
||||||
Loading…
Reference in New Issue
Block a user