Skip to content

Commit

Permalink
Create bout show page, allow updating existing bout
Browse files Browse the repository at this point in the history
  • Loading branch information
skanderm committed Jan 27, 2025
1 parent b12319f commit 80ccd71
Show file tree
Hide file tree
Showing 8 changed files with 404 additions and 42 deletions.
28 changes: 26 additions & 2 deletions server/lib/orcasite/radio/bout.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ defmodule Orcasite.Radio.Bout do
relationships do
belongs_to :created_by_user, Orcasite.Accounts.User

belongs_to :feed, Orcasite.Radio.Feed
belongs_to :feed, Orcasite.Radio.Feed do
public? true
end

has_many :bout_feed_streams, Orcasite.Radio.BoutFeedStream

many_to_many :feed_streams, Orcasite.Radio.FeedStream do
through Orcasite.Radio.BoutFeedStream
public? true
end
end

Expand All @@ -63,7 +67,7 @@ defmodule Orcasite.Radio.Bout do
end

actions do
defaults [:read, :update, :destroy]
defaults [:read, :destroy]

read :index do
pagination do
Expand Down Expand Up @@ -113,17 +117,37 @@ defmodule Orcasite.Radio.Bout do
changeset
end
end

update :update do
primary? true
accept [:category, :start_time, :end_time]

change fn changeset, _ ->
end_time = Ash.Changeset.get_argument_or_attribute(changeset, :end_time)
start_time = Ash.Changeset.get_argument_or_attribute(changeset, :start_time)

if start_time && end_time do
changeset
|> Ash.Changeset.change_attribute(:duration, DateTime.diff(end_time, start_time, :millisecond) / 1000)
else
changeset
end
end
end
end

graphql do
type :bout
attribute_types [feed_id: :id, feed_stream_id: :id]

queries do
list :bouts, :index
get :bout, :read
end

mutations do
create :create_bout, :create
update :update_bout, :update
end
end
end
58 changes: 45 additions & 13 deletions ui/src/components/Bouts/BoutPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ import SpectrogramTimeline, {
import { BoutPlayer, PlayerControls } from "@/components/Player/BoutPlayer";
import {
AudioCategory,
BoutQuery,
FeedQuery,
useCreateBoutMutation,
useDetectionsQuery,
useGetCurrentUserQuery,
useListFeedStreamsQuery,
useUpdateBoutMutation,
} from "@/graphql/generated";
import { formatTimestamp } from "@/utils/time";

Expand All @@ -64,10 +66,11 @@ export default function BoutPage({
feed: FeedQuery["feed"];
targetAudioCategory?: AudioCategory;
targetTime?: Date;
// bout?: BoutQuery["bout"];
bout?: BoutQuery["bout"];
}) {
const now = useMemo(() => new Date(), []);
targetTime = targetTime ?? now;
targetTime =
targetTime ?? (bout?.startTime && new Date(bout.startTime)) ?? now;

const { currentUser } = useGetCurrentUserQuery().data ?? {};
const playerTime = useRef<Date>(targetTime);
Expand All @@ -78,15 +81,19 @@ export default function BoutPage({
const [playerControls, setPlayerControls] = useState<PlayerControls>();
const spectrogramControls = useRef<SpectrogramControls>();

const [boutStartTime, setBoutStartTime] = useState<Date>();
const [boutEndTime, setBoutEndTime] = useState<Date>();
const [boutStartTime, setBoutStartTime] = useState<Date | undefined>(
bout?.startTime && new Date(bout.startTime),
);
const [boutEndTime, setBoutEndTime] = useState<Date | undefined>(
(bout?.endTime && new Date(bout.endTime)) ?? undefined,
);
const [currentTab, setCurrentTab] = useState(0);
const audioCategories: AudioCategory[] = useMemo(
() => ["ANTHROPHONY", "BIOPHONY", "GEOPHONY"],
[],
);
const [audioCategory, setAudioCategory] = useState<AudioCategory | undefined>(
targetAudioCategory,
targetAudioCategory ?? bout?.category,
);

const timeBuffer = 5; // minutes
Expand Down Expand Up @@ -159,15 +166,40 @@ export default function BoutPage({
}
},
});
const updateBoutMutation = useUpdateBoutMutation({
onSuccess: ({ updateBout: { errors } }) => {
if (errors && errors.length > 0) {
console.error(errors);
setBoutForm((form) => ({
...form,
errors: {
...form.errors,
...Object.fromEntries(
errors.map(({ code, message }) => [code, message] as const),
),
},
}));
}
},
});
const saveBout = () => {
setBoutForm((form) => ({ ...form, errors: {} }));
if (audioCategory && boutStartTime) {
createBoutMutation.mutate({
feedId: feed.id,
startTime: boutStartTime,
endTime: boutEndTime,
category: audioCategory,
});
if (isNew) {
createBoutMutation.mutate({
feedId: feed.id,
startTime: boutStartTime,
endTime: boutEndTime,
category: audioCategory,
});
} else if (bout) {
updateBoutMutation.mutate({
id: bout.id,
startTime: boutStartTime,
endTime: boutEndTime,
category: audioCategory,
});
}
} else {
const errors: Record<string, string> = {};
if (!audioCategory) {
Expand All @@ -190,14 +222,14 @@ export default function BoutPage({
>
<Box>
<Typography variant="overline" sx={{ fontSize: 18 }}>
New Bout
Bout
</Typography>
<Typography variant="h4">{feed.name}</Typography>
</Box>
<Box ml="auto">
{currentUser?.moderator && (
<Button variant="contained" size="large" onClick={saveBout}>
Create bout
{isNew ? "Create" : "Update"} bout
</Button>
)}
</Box>
Expand Down
15 changes: 15 additions & 0 deletions ui/src/graphql/fragments/FeedParts.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
fragment FeedParts on Feed {
id
name
slug
nodeName
latLng {
lat
lng
}
introHtml
thumbUrl
imageUrl
mapUrl
bucket
}
Loading

0 comments on commit 80ccd71

Please sign in to comment.