diff --git a/packages/app/src/components/MusicStudio/ReleaseEditor/index.jsx b/packages/app/src/components/MusicStudio/ReleaseEditor/index.jsx
index 2e5d604c..1aa38e05 100644
--- a/packages/app/src/components/MusicStudio/ReleaseEditor/index.jsx
+++ b/packages/app/src/components/MusicStudio/ReleaseEditor/index.jsx
@@ -1,280 +1,332 @@
import React from "react"
import * as antd from "antd"
-
import { Icons, createIconRender } from "@components/Icons"
import MusicModel from "@models/music"
-
+import compareObjectsByProperties from "@utils/compareObjectsByProperties"
import useUrlQueryActiveKey from "@hooks/useUrlQueryActiveKey"
import TrackManifest from "@cores/player/classes/TrackManifest"
-import { DefaultReleaseEditorState, ReleaseEditorStateContext } from "@contexts/MusicReleaseEditor"
+import {
+ DefaultReleaseEditorState,
+ ReleaseEditorStateContext,
+} from "@contexts/MusicReleaseEditor"
import Tabs from "./tabs"
import "./index.less"
const ReleaseEditor = (props) => {
- const { release_id } = props
+ const { release_id } = props
- const basicInfoRef = React.useRef()
+ const basicInfoRef = React.useRef()
- const [submitting, setSubmitting] = React.useState(false)
- const [loading, setLoading] = React.useState(true)
- const [submitError, setSubmitError] = React.useState(null)
+ const [submitting, setSubmitting] = React.useState(false)
+ const [loading, setLoading] = React.useState(true)
+ const [submitError, setSubmitError] = React.useState(null)
- const [loadError, setLoadError] = React.useState(null)
- const [globalState, setGlobalState] = React.useState(DefaultReleaseEditorState)
+ const [loadError, setLoadError] = React.useState(null)
+ const [globalState, setGlobalState] = React.useState(
+ DefaultReleaseEditorState,
+ )
+ const [initialValues, setInitialValues] = React.useState({})
- const [customPage, setCustomPage] = React.useState(null)
- const [customPageActions, setCustomPageActions] = React.useState([])
+ const [customPage, setCustomPage] = React.useState(null)
+ const [customPageActions, setCustomPageActions] = React.useState([])
- const [selectedTab, setSelectedTab] = useUrlQueryActiveKey({
- defaultKey: "info",
- queryKey: "tab"
- })
+ const [selectedTab, setSelectedTab] = useUrlQueryActiveKey({
+ defaultKey: "info",
+ queryKey: "tab",
+ })
- async function initialize() {
- setLoading(true)
- setLoadError(null)
+ async function initialize() {
+ setLoading(true)
+ setLoadError(null)
- if (release_id !== "new") {
- try {
- let releaseData = await MusicModel.getReleaseData(release_id)
+ if (release_id !== "new") {
+ try {
+ let releaseData = await MusicModel.getReleaseData(release_id)
- if (Array.isArray(releaseData.list)) {
- releaseData.list = releaseData.list.map((item) => {
- return new TrackManifest(item)
- })
- }
+ if (Array.isArray(releaseData.items)) {
+ releaseData.items = releaseData.items.map((item) => {
+ return new TrackManifest(item)
+ })
+ }
- setGlobalState({
- ...globalState,
- ...releaseData,
- })
- } catch (error) {
- setLoadError(error)
- }
- }
+ setGlobalState({
+ ...globalState,
+ ...releaseData,
+ })
- setLoading(false)
- }
+ setInitialValues(releaseData)
+ } catch (error) {
+ setLoadError(error)
+ }
+ }
- async function renderCustomPage(page, actions) {
- setCustomPage(page ?? null)
- setCustomPageActions(actions ?? [])
- }
+ setLoading(false)
+ }
- async function handleSubmit() {
- setSubmitting(true)
- setSubmitError(null)
+ function hasChanges() {
+ const stagedChanges = {
+ title: globalState.title,
+ type: globalState.type,
+ public: globalState.public,
+ cover: globalState.cover,
+ items: globalState.items,
+ }
- try {
- // first sumbit tracks
- const tracks = await MusicModel.putTrack({
- list: globalState.list,
- })
+ return !compareObjectsByProperties(
+ stagedChanges,
+ initialValues,
+ Object.keys(stagedChanges),
+ )
+ }
- // then submit release
- const result = await MusicModel.putRelease({
- _id: globalState._id,
- title: globalState.title,
- description: globalState.description,
- public: globalState.public,
- cover: globalState.cover,
- explicit: globalState.explicit,
- type: globalState.type,
- list: tracks.list.map((item) => item._id),
- })
+ async function renderCustomPage(page, actions) {
+ setCustomPage(page ?? null)
+ setCustomPageActions(actions ?? [])
+ }
- app.location.push(`/studio/music/${result._id}`)
- } catch (error) {
- console.error(error)
- app.message.error(error.message)
+ async function handleSubmit() {
+ setSubmitting(true)
+ setSubmitError(null)
- setSubmitError(error)
- setSubmitting(false)
+ try {
+ console.log("Submitting Tracks")
- return false
- }
+ // first sumbit tracks
+ const tracks = await MusicModel.putTrack({
+ items: globalState.items,
+ })
- setSubmitting(false)
- app.message.success("Release saved")
- }
+ console.log("Submitting release")
- async function handleDelete() {
- app.layout.modal.confirm({
- headerText: "Are you sure you want to delete this release?",
- descriptionText: "This action cannot be undone.",
- onConfirm: async () => {
- await MusicModel.deleteRelease(globalState._id)
- app.location.push(window.location.pathname.split("/").slice(0, -1).join("/"))
- },
- })
- }
+ // then submit release
+ const result = await MusicModel.putRelease({
+ _id: globalState._id,
+ title: globalState.title,
+ description: globalState.description,
+ public: globalState.public,
+ cover: globalState.cover,
+ explicit: globalState.explicit,
+ type: globalState.type,
+ items: tracks.items.map((item) => item._id),
+ })
- async function canFinish() {
- return true
- }
+ app.location.push(`/studio/music/${result._id}`)
+ } catch (error) {
+ console.error(error)
+ app.message.error(error.message)
- React.useEffect(() => {
- initialize()
- }, [])
+ setSubmitError(error)
+ setSubmitting(false)
- if (loadError) {
- return
- }
+ return false
+ }
- if (loading) {
- return
- }
+ setSubmitting(false)
+ app.message.success("Release saved")
+ }
- const Tab = Tabs.find(({ key }) => key === selectedTab)
+ async function handleDelete() {
+ app.layout.modal.confirm({
+ headerText: "Are you sure you want to delete this release?",
+ descriptionText: "This action cannot be undone.",
+ onConfirm: async () => {
+ await MusicModel.deleteRelease(globalState._id)
+ app.location.push(
+ window.location.pathname.split("/").slice(0, -1).join("/"),
+ )
+ },
+ })
+ }
- const CustomPageProps = {
- close: () => {
- renderCustomPage(null, null)
- }
- }
+ function canFinish() {
+ return hasChanges()
+ }
- return
-
- {
- customPage &&
- {
- customPage.header &&
-
-
}
- onClick={() => renderCustomPage(null, null)}
- />
+ React.useEffect(() => {
+ initialize()
+ }, [])
-
{customPage.header}
-
+ if (loadError) {
+ return (
+
+ )
+ }
- {
- Array.isArray(customPageActions) && customPageActions.map((action, index) => {
- return
{
- if (typeof action.onClick === "function") {
- await action.onClick()
- }
+ if (loading) {
+ return
+ }
- if (action.fireEvent) {
- app.eventBus.emit(action.fireEvent)
- }
- }}
- disabled={action.disabled}
- >
- {action.label}
-
- })
- }
-
- }
+ const Tab = Tabs.find(({ key }) => key === selectedTab)
- {
- customPage.content && (React.isValidElement(customPage.content) ?
- React.cloneElement(customPage.content, {
- ...CustomPageProps,
- ...customPage.props
- }) :
- React.createElement(customPage.content, {
- ...CustomPageProps,
- ...customPage.props
- })
- )
- }
-
- }
- {
- !customPage && <>
-
-
setSelectedTab(e.key)}
- selectedKeys={[selectedTab]}
- items={Tabs}
- mode="vertical"
- />
+ const CustomPageProps = {
+ close: () => {
+ renderCustomPage(null, null)
+ },
+ }
-
-
:
}
- disabled={submitting || loading || !canFinish()}
- loading={submitting}
- >
- {release_id !== "new" ? "Save" : "Release"}
-
+ return (
+
+
+ {customPage && (
+
+ {customPage.header && (
+
+
+
}
+ onClick={() =>
+ renderCustomPage(null, null)
+ }
+ />
- {
- release_id !== "new" ?
}
- disabled={loading}
- onClick={handleDelete}
- >
- Delete
- : null
- }
+
{customPage.header}
+
- {
- release_id !== "new" ?
}
- onClick={() => app.location.push(`/music/release/${globalState._id}`)}
- >
- Go to release
- : null
- }
-
-
+ {Array.isArray(customPageActions) &&
+ customPageActions.map((action, index) => {
+ return (
+
{
+ if (
+ typeof action.onClick ===
+ "function"
+ ) {
+ await action.onClick()
+ }
-
- {
- submitError &&
- }
- {
- !Tab &&
- }
- {
- Tab && React.createElement(Tab.render, {
- release: globalState,
+ if (action.fireEvent) {
+ app.eventBus.emit(
+ action.fireEvent,
+ )
+ }
+ }}
+ disabled={action.disabled}
+ >
+ {action.label}
+
+ )
+ })}
+
+ )}
- state: globalState,
- setState: setGlobalState,
+ {customPage.content &&
+ (React.isValidElement(customPage.content)
+ ? React.cloneElement(customPage.content, {
+ ...CustomPageProps,
+ ...customPage.props,
+ })
+ : React.createElement(customPage.content, {
+ ...CustomPageProps,
+ ...customPage.props,
+ }))}
+
+ )}
+ {!customPage && (
+ <>
+
+
setSelectedTab(e.key)}
+ selectedKeys={[selectedTab]}
+ items={Tabs}
+ mode="vertical"
+ />
- references: {
- basic: basicInfoRef
- }
- })
- }
-
- >
- }
-
-
+
+
+ ) : (
+
+ )
+ }
+ disabled={
+ submitting || loading || !canFinish()
+ }
+ loading={submitting}
+ >
+ {release_id !== "new" ? "Save" : "Release"}
+
+
+ {release_id !== "new" ? (
+
}
+ disabled={loading}
+ onClick={handleDelete}
+ >
+ Delete
+
+ ) : null}
+
+ {release_id !== "new" ? (
+
}
+ onClick={() =>
+ app.location.push(
+ `/music/release/${globalState._id}`,
+ )
+ }
+ >
+ Go to release
+
+ ) : null}
+
+
+
+
+ {submitError && (
+
+ )}
+ {!Tab && (
+
+ )}
+ {Tab &&
+ React.createElement(Tab.render, {
+ release: globalState,
+
+ state: globalState,
+ setState: setGlobalState,
+
+ references: {
+ basic: basicInfoRef,
+ },
+ })}
+
+ >
+ )}
+
+
+ )
}
-export default ReleaseEditor
\ No newline at end of file
+export default ReleaseEditor
diff --git a/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/TrackListItem/index.jsx b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/TrackListItem/index.jsx
index 38a6c0ef..23f4fcec 100644
--- a/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/TrackListItem/index.jsx
+++ b/packages/app/src/components/MusicStudio/ReleaseEditor/tabs/Tracks/components/TrackListItem/index.jsx
@@ -11,13 +11,27 @@ import { ReleaseEditorStateContext } from "@contexts/MusicReleaseEditor"
import "./index.less"
+const stateToString = {
+ uploading: "Uploading",
+ transmuxing: "Processing...",
+ uploading_s3: "Archiving...",
+}
+
+const getTitleString = ({ track, progress }) => {
+ if (progress) {
+ return stateToString[progress.state] || progress.state
+ }
+
+ return track.title
+}
+
const TrackListItem = (props) => {
const context = React.useContext(ReleaseEditorStateContext)
const [loading, setLoading] = React.useState(false)
const [error, setError] = React.useState(null)
- const { track } = props
+ const { track, progress } = props
async function onClickEditTrack() {
context.renderCustomPage({
@@ -33,8 +47,6 @@ const TrackListItem = (props) => {
props.onDelete(track.uid)
}
- console.log("render")
-
return (
{
@@ -58,7 +70,7 @@ const TrackListItem = (props) => {
{props.index + 1}
- {props.uploading.working && }
+ {progress !== null && }
{
}}
/>
- {track.title}
+ {getTitleString({ track, progress })}
{
- if (prevState.list !== this.state.list) {
+ if (prevState.items !== this.state.items) {
if (typeof this.props.onChangeState === "function") {
this.props.onChangeState(this.state)
}
@@ -55,7 +55,7 @@ class TracksManager extends React.Component {
return false
}
- return this.state.list.find((item) => item.uid === uid)
+ return this.state.items.find((item) => item.uid === uid)
}
addTrackToList = (track) => {
@@ -64,7 +64,7 @@ class TracksManager extends React.Component {
}
this.setState({
- list: [...this.state.list, track],
+ items: [...this.state.items, track],
})
}
@@ -76,18 +76,17 @@ class TracksManager extends React.Component {
this.removeTrackUIDFromPendingUploads(uid)
this.setState({
- list: this.state.list.filter((item) => item.uid !== uid),
+ items: this.state.items.filter((item) => item.uid !== uid),
})
}
modifyTrackByUid = (uid, track) => {
- console.log("modifyTrackByUid", uid, track)
if (!uid || !track) {
return false
}
this.setState({
- list: this.state.list.map((item) => {
+ items: this.state.items.map((item) => {
if (item.uid === uid) {
return {
...item,
@@ -140,7 +139,7 @@ class TracksManager extends React.Component {
)
if (uploadProgressIndex === -1) {
- return 0
+ return null
}
return this.state.pendingUploads[uploadProgressIndex].progress
@@ -159,7 +158,7 @@ class TracksManager extends React.Component {
newData[uploadProgressIndex].progress = progress
- console.log(`Updating progress for [${uid}] to [${progress}]`)
+ console.log(`Updating progress for [${uid}] to >`, progress)
this.setState({
pendingUploads: newData,
@@ -189,7 +188,7 @@ class TracksManager extends React.Component {
// remove pending file
this.removeTrackUIDFromPendingUploads(uid)
- let trackManifest = this.state.list.find(
+ let trackManifest = this.state.items.find(
(item) => item.uid === uid,
)
@@ -231,9 +230,8 @@ class TracksManager extends React.Component {
const response = await app.cores.remoteStorage
.uploadFile(req.file, {
onProgress: this.handleTrackFileUploadProgress,
- service: "b2",
headers: {
- transmux: "a-dash",
+ transformations: "a-dash",
},
})
.catch((error) => {
@@ -258,17 +256,17 @@ class TracksManager extends React.Component {
this.setState((prev) => {
// move all list items by id
const orderedIds = orderedIdsArray.map((id) =>
- this.state.list.find((item) => item._id === id),
+ this.state.items.find((item) => item._id === id),
)
console.log("orderedIds", orderedIds)
return {
- list: orderedIds,
+ items: orderedIds,
}
})
}
render() {
- console.log(`Tracks List >`, this.state.list)
+ console.log(`Tracks List >`, this.state.items)
return (
@@ -280,7 +278,7 @@ class TracksManager extends React.Component {
accept="audio/*"
multiple
>
- {this.state.list.length === 0 ? (
+ {this.state.items.length === 0 ? (
) : (
- {this.state.list.length === 0 && (
+ {this.state.items.length === 0 && (
)}
- {this.state.list.map((track, index) => {
+ {this.state.items.map((track, index) => {
const progress = this.getUploadProgress(track.uid)
return (
@@ -310,12 +308,7 @@ class TracksManager extends React.Component {
track={track}
onEdit={this.modifyTrackByUid}
onDelete={this.removeTrackByUid}
- uploading={{
- progress: progress,
- working: this.state.pendingUploads.find(
- (item) => item.uid === track.uid,
- ),
- }}
+ progress={progress}
disabled={progress > 0}
/>
@@ -336,7 +329,7 @@ const ReleaseTracks = (props) => {
{
setState({
...state,
diff --git a/packages/app/src/components/MusicStudio/TrackEditor/index.jsx b/packages/app/src/components/MusicStudio/TrackEditor/index.jsx
index d73f48ba..9c5fab25 100644
--- a/packages/app/src/components/MusicStudio/TrackEditor/index.jsx
+++ b/packages/app/src/components/MusicStudio/TrackEditor/index.jsx
@@ -10,158 +10,163 @@ import { ReleaseEditorStateContext } from "@contexts/MusicReleaseEditor"
import "./index.less"
const TrackEditor = (props) => {
- const context = React.useContext(ReleaseEditorStateContext)
- const [track, setTrack] = React.useState(props.track ?? {})
+ const context = React.useContext(ReleaseEditorStateContext)
+ const [track, setTrack] = React.useState(props.track ?? {})
- async function handleChange(key, value) {
- setTrack((prev) => {
- return {
- ...prev,
- [key]: value
- }
- })
- }
+ async function handleChange(key, value) {
+ setTrack((prev) => {
+ return {
+ ...prev,
+ [key]: value,
+ }
+ })
+ }
- async function openEnhancedLyricsEditor() {
- context.renderCustomPage({
- header: "Enhanced Lyrics",
- content: EnhancedLyricsEditor,
- props: {
- track: track,
- }
- })
- }
+ async function openEnhancedLyricsEditor() {
+ context.renderCustomPage({
+ header: "Enhanced Lyrics",
+ content: EnhancedLyricsEditor,
+ props: {
+ track: track,
+ },
+ })
+ }
- async function handleOnSave() {
- setTrack((prev) => {
- const listData = [...context.list]
+ async function handleOnSave() {
+ setTrack((prev) => {
+ const listData = [...context.items]
- const trackIndex = listData.findIndex((item) => item.uid === prev.uid)
+ const trackIndex = listData.findIndex(
+ (item) => item.uid === prev.uid,
+ )
- if (trackIndex === -1) {
- return prev
- }
+ if (trackIndex === -1) {
+ return prev
+ }
- listData[trackIndex] = prev
+ listData[trackIndex] = prev
- context.setGlobalState({
- ...context,
- list: listData
- })
+ context.setGlobalState({
+ ...context,
+ items: listData,
+ })
- return prev
- })
- }
+ props.close()
- React.useEffect(() => {
- context.setCustomPageActions([
- {
- label: "Save",
- icon: "FiSave",
- type: "primary",
- onClick: handleOnSave,
- disabled: props.track === track,
- },
- ])
- }, [track])
+ return prev
+ })
+ }
- return
-
-
-
- Cover
-
+ function setParentCover() {
+ handleChange("cover", context.cover)
+ }
-
handleChange("cover", url)}
- extraActions={[
-
- Use Parent
-
- ]}
- />
-
+ React.useEffect(() => {
+ context.setCustomPageActions([
+ {
+ label: "Save",
+ icon: "FiSave",
+ type: "primary",
+ onClick: handleOnSave,
+ disabled: props.track === track,
+ },
+ ])
+ }, [track])
-
-
-
- Title
-
+ return (
+
+
+
+
+ Cover
+
-
handleChange("title", e.target.value)}
- />
-
+
handleChange("cover", url)}
+ extraActions={[
+
+ Use Parent
+ ,
+ ]}
+ />
+
-
-
-
- Artist
-
+
+
+
+ Title
+
-
handleChange("artist", e.target.value)}
- />
-
+
handleChange("title", e.target.value)}
+ />
+
-
-
-
- Album
-
+
+
+
+ Artist
+
-
handleChange("album", e.target.value)}
- />
-
+
handleChange("artist", e.target.value)}
+ />
+
-
-
-
- Explicit
-
+
+
+
+ Album
+
-
handleChange("explicit", value)}
- />
-
+
handleChange("album", e.target.value)}
+ />
+
-
-
-
-
Enhanced Lyrics
+
+
+
+ Explicit
+
-
handleChange("lyrics_enabled", value)}
- disabled={!track.params._id}
- />
-
+
handleChange("explicit", value)}
+ />
+
-
-
- Edit
-
+
+
+
+ Enhanced Lyrics
+
- {
- !track.params._id &&
- You cannot edit Video and Lyrics without release first
-
- }
-
-
-
+
+
+ Edit
+
+
+ {!track.params._id && (
+
+ You cannot edit Video and Lyrics without release
+ first
+
+ )}
+
+
+
+ )
}
-export default TrackEditor
\ No newline at end of file
+export default TrackEditor