feat(fluent-player): add FluentPlayer integration (26 actions) - #195
feat(fluent-player): add FluentPlayer integration (26 actions)#195RishadAlam wants to merge 4 commits into
Conversation
Adds the free half of the FluentPlayer integration: 26 write actions that are dispatched to the Pro plugin over bit_integrations_fluent_player_* filters, plus the action UI. Backend: - FluentPlayerController: activation check, authorize, and list endpoints for media, playlists, tags, presets, users and attachments. - RecordApiHelper: switch over the 26 actions, each firing its own filter, then records the outcome through LogHandler. Frontend: - Action picker (all Pro-gated), record pickers, and a Utilities section for the optional status/view-type/ended selects. - Field map carries free-text values only; anything with a fixed option set or a fetchable record list is a select instead. Registers FluentPlayer in AllTriggersName, NewInteg, EditInteg, IntegInfo and SelectAction, and ships the integration logo.
… refresh functionality
…s and improve code clarity
There was a problem hiding this comment.
Code Review
This pull request introduces a new integration for FluentPlayer, adding backend controllers, routes, and API helpers alongside frontend components for authorization, field mapping, and layout configuration. The feedback highlights critical improvements for robustness and code quality: adding .catch blocks to asynchronous API requests to handle failures gracefully, implementing a useEffect hook on mount to rebuild non-persisted configuration fields when editing an integration, and declaring several controller methods as static since they do not access instance state.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const authorizeHandler = () => { | ||
| setIsLoading('auth') | ||
| bitsFetch({}, 'fluent_player_authorize').then(result => { | ||
| if (result?.success) { | ||
| setIsAuthorized(true) | ||
| setSnackbar({ | ||
| show: true, | ||
| msg: __('Connected with FluentPlayer Successfully', 'bit-integrations') | ||
| }) | ||
| } | ||
| setIsLoading(false) | ||
| setShowAuthMsg(true) | ||
| }) | ||
| } |
There was a problem hiding this comment.
When handling asynchronous API requests, always append a .catch block to ensure that loading states are reset and error messages are displayed if the request fails or encounters a network error.
const authorizeHandler = () => {
setIsLoading('auth')
bitsFetch({}, 'fluent_player_authorize')
.then(result => {
if (result?.success) {
setIsAuthorized(true)
setSnackbar({
show: true,
msg: __('Connected with FluentPlayer Successfully', 'bit-integrations')
})
}
setIsLoading(false)
setShowAuthMsg(true)
})
.catch(() => {
setIsLoading(false)
setShowAuthMsg(true)
setSnackbar({
show: true,
msg: __('Connection failed. Please try again.', 'bit-integrations')
})
})
}References
- When handling asynchronous API requests, always append a .catch block to ensure that loading states are reset and error messages are displayed if the request fails or encounters a network error.
There was a problem hiding this comment.
Fixed — the promise now has a .catch that clears the loading state, still shows the auth message, and surfaces the failure:
.catch(() => {
setIsLoading(false)
setShowAuthMsg(true)
setSnackbar({ show: true, msg: __('Connection failed. Please try again.', 'bit-integrations') })
})| setIsLoading(false) | ||
| toast.error(errorMsg) | ||
| }) | ||
| .catch(() => setIsLoading(false)) |
There was a problem hiding this comment.
When handling asynchronous API requests, always append a .catch block to ensure that loading states are reset and error messages are displayed if the request fails or encounters a network error.
.catch(() => {
setIsLoading(false)
toast.error(errorMsg)
})References
- When handling asynchronous API requests, always append a .catch block to ensure that loading states are reset and error messages are displayed if the request fails or encounters a network error.
There was a problem hiding this comment.
Fixed. There was a .catch but it only cleared the spinner, so a network failure looked identical to a no-op. It now reports the error too:
.catch(() => {
setIsLoading(false)
toast.error(errorMsg)
})| @@ -0,0 +1,261 @@ | |||
| import { create } from 'mutative' | |||
There was a problem hiding this comment.
Done — useEffect is imported and used for the mount-time refetch described in your other comment.
| const action = fluentPlayerConf?.mainAction | ||
|
|
There was a problem hiding this comment.
When editing an integration, ensure that non-persisted configuration fields (like static fields) are rebuilt on mount from the saved action without modifying the saved field mappings, to prevent empty options and component crashes.
const action = fluentPlayerConf?.mainAction
useEffect(() => {
if (action) {
if (needsPreset.includes(action)) refreshFluentPlayerPresets(setFluentPlayerConf, setIsLoading)
if (needsTags.includes(action)) refreshFluentPlayerTags(setFluentPlayerConf, setIsLoading)
if (needsAttachment.includes(action)) refreshFluentPlayerAttachments(setFluentPlayerConf, setIsLoading)
if (needsOptionalUser.includes(action)) refreshFluentPlayerUsers(setFluentPlayerConf, setIsLoading)
if (needsOptionalMedia.includes(action) || needsOptionalMediaIds.includes(action)) {
refreshFluentPlayerMedia(setFluentPlayerConf, setIsLoading)
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])References
- When editing an integration, ensure that non-persisted configuration fields (like static fields) are rebuilt on mount from the saved action without modifying the saved field mappings, to prevent empty options and component crashes.
There was a problem hiding this comment.
Fixed. Extracted the per-action fan-out into fetchListsFor(action) and call it once on mount, so an edit rebuilds the option lists without touching the saved field_map:
useEffect(() => {
fetchListsFor(action)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])handleMainAction now calls the same helper, so the two paths can't drift.
| wp_send_json_success(true); | ||
| } | ||
|
|
||
| public function refreshMedia() |
There was a problem hiding this comment.
The refreshMedia method does not access any instance properties ($this) and is registered as a static route callback. It should be explicitly declared as static to maintain consistency and adhere to coding standards.
public static function refreshMedia()References
- In PHP, controller methods that do not access instance state ($this) and are registered as static route callbacks should be explicitly declared as static to maintain consistency and adhere to coding standards.
There was a problem hiding this comment.
Fixed — declared static. All five refresh* methods are registered as [FluentPlayerController::class, 'refreshX'] route callbacks and none touch $this.
| wp_send_json_success($response, 200); | ||
| } | ||
|
|
||
| public function refreshTags() |
There was a problem hiding this comment.
The refreshTags method does not access any instance properties ($this) and is registered as a static route callback. It should be explicitly declared as static to maintain consistency and adhere to coding standards.
public static function refreshTags()References
- In PHP, controller methods that do not access instance state ($this) and are registered as static route callbacks should be explicitly declared as static to maintain consistency and adhere to coding standards.
There was a problem hiding this comment.
Fixed — declared static. All five refresh* methods are registered as [FluentPlayerController::class, 'refreshX'] route callbacks and none touch $this.
| wp_send_json_success($response, 200); | ||
| } | ||
|
|
||
| public function refreshPresets() |
There was a problem hiding this comment.
The refreshPresets method does not access any instance properties ($this) and is registered as a static route callback. It should be explicitly declared as static to maintain consistency and adhere to coding standards.
public static function refreshPresets()References
- In PHP, controller methods that do not access instance state ($this) and are registered as static route callbacks should be explicitly declared as static to maintain consistency and adhere to coding standards.
There was a problem hiding this comment.
Fixed — declared static. All five refresh* methods are registered as [FluentPlayerController::class, 'refreshX'] route callbacks and none touch $this.
| wp_send_json_success($response, 200); | ||
| } | ||
|
|
||
| public function refreshUsers() |
There was a problem hiding this comment.
The refreshUsers method does not access any instance properties ($this) and is registered as a static route callback. It should be explicitly declared as static to maintain consistency and adhere to coding standards.
public static function refreshUsers()References
- In PHP, controller methods that do not access instance state ($this) and are registered as static route callbacks should be explicitly declared as static to maintain consistency and adhere to coding standards.
There was a problem hiding this comment.
Fixed — declared static. All five refresh* methods are registered as [FluentPlayerController::class, 'refreshX'] route callbacks and none touch $this.
| wp_send_json_success($response, 200); | ||
| } | ||
|
|
||
| public function refreshAttachments() |
There was a problem hiding this comment.
The refreshAttachments method does not access any instance properties ($this) and is registered as a static route callback. It should be explicitly declared as static to maintain consistency and adhere to coding standards.
public static function refreshAttachments()References
- In PHP, controller methods that do not access instance state ($this) and are registered as static route callbacks should be explicitly declared as static to maintain consistency and adhere to coding standards.
There was a problem hiding this comment.
Fixed — declared static. All five refresh* methods are registered as [FluentPlayerController::class, 'refreshX'] route callbacks and none touch $this.
✅ WordPress Plugin Check Report
📊 ReportAll checks passed! No errors or warnings found. 🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check |
- Authorization: a rejected authorize request left the spinner running with no feedback. Clear the loading state, show the auth message and surface a snackbar. - CommonFunc: the refresh `.catch` only cleared the spinner, so a network failure was indistinguishable from an empty list. Report it with a toast. - IntegLayout: option lists were only built when the action select changed, so opening a saved integration rendered empty dropdowns. Extract `fetchListsFor(action)` and call it on mount as well as on change. - Controller: the five `refresh*` route callbacks are registered statically and never touch `$this`; declare them `static`.
Description
Adds the free half of the FluentPlayer integration: 26 write actions dispatched to the Pro plugin over
bit_integrations_fluent_player_*filters, plus the action UI. Pairs withbit-integrations-pro— the free plugin only fires the hooks and logs the result; all plugin work happens in Pro.Motivation & Context
FluentPlayer had no Bit Integrations support, so its media, playlists, tags, leads and watch analytics could not be automated from a flow. This is the free-side half of that.
Type of Change
Key Changes
Backend (
backend/Actions/FluentPlayer/)FluentPlayerController— activation check, authorize, and the list endpoints backing the dropdowns (media, tags, presets, users, attachments).RecordApiHelper— aswitchwith one case per action, each firing its ownConfig::withPrefix('fluent_player_*')filter and passing only the arguments that action's Pro handler consumes, then recording the outcome throughLogHandler.Routes.php; registeredFluentPlayerinAllTriggersName.Frontend (
frontend/src/components/AllIntegrations/FluentPlayer/)media_id,playlist_id,media_ids, tag name,user_id) so those can be mapped from trigger data. Fixed option sets (provider, status) and fetchable config values (preset, tags, attachment, optional media/user) are selects instead.NewInteg,EditInteg,IntegInfoandSelectAction, and added the app logo.Checklist
Changelog
Reviewer notes
Requires bit-integrations-pro
feat/fluentplayer— without it every action returns the "Bit Integrations Pro is not installed" default.A review pass raised a set of defects that are not addressed in this branch and are worth folding in before merge:
checkMappedFieldsonly checks that an action is selected — it does not reject incomplete field-map rows or missing required selects (provider / status / preset / tags), so a flow can be saved that fails on every run.get_users()with nonumber, twoget_posts()withposts_per_page => -1) and the fetched lists are persisted intoflow_details.