Skip to content

Commit e6fa16e

Browse files
authored
Merge branch 'main' into contributing
2 parents 7e03fba + 4ce27d8 commit e6fa16e

File tree

9 files changed

+74
-26
lines changed

9 files changed

+74
-26
lines changed

server/lib/orcasite/notifications/resources/notification_instance.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ defmodule Orcasite.Notifications.NotificationInstance do
4343
end
4444

4545
actions do
46-
defaults [:create, :read, :update, :destroy]
46+
defaults [:read, :destroy, update: :*, create: :*]
4747

4848
create :create_with_relationships do
4949
argument :subscription, :map

server/lib/orcasite/radio/audio_image.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ defmodule Orcasite.Radio.AudioImage do
167167
end
168168

169169
update :generate_spectrogram do
170+
require_atomic? false
170171
change set_attribute(:status, :processing)
171172

172173
change after_action(

server/lib/orcasite_web/json_api_router.ex

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,5 @@ defmodule OrcasiteWeb.JsonApiRouter do
22
use AshJsonApi.Router,
33
domains: [Orcasite.Notifications, Orcasite.Radio],
44
json_schema: "/json_schema",
5-
open_api: "/open_api",
6-
modify_open_api: {__MODULE__, :modify_open_api, []}
7-
8-
def modify_open_api(spec, _, _) do
9-
%{
10-
spec
11-
| servers: Enum.map(spec.servers, &%{&1 | url: &1.url <> "/api/json"})
12-
}
13-
end
5+
open_api: "/open_api"
146
end

ui/src/components/DetectionsTable.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,13 @@ export default function DetectionsTable({
7575
<DetectionsPlayer
7676
feed={feed}
7777
marks={detections
78-
.sort((a, b) => a.id.localeCompare(b.id))
78+
.sort(({ timestamp: a }, { timestamp: b }) => {
79+
const date_a = new Date(a);
80+
const date_b = new Date(b);
81+
82+
// Sort by timestamp, low to high
83+
return +date_a - +date_b;
84+
})
7985
.map((d, index) => ({
8086
label: (index + 1).toString(),
8187
value: Number((+d.playerOffset - +startOffset).toFixed(1)),

ui/src/components/Player/Player.tsx

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,26 @@ export default function Player({
119119
}
120120
}, [playerRef, updateMediaSession]);
121121

122-
const handleReady = useCallback((player: VideoJSPlayer) => {
123-
playerRef.current = player;
122+
const handleReady = useCallback(
123+
(player: VideoJSPlayer) => {
124+
playerRef.current = player;
124125

125-
player.on("playing", () => {
126-
setPlayerStatus("playing");
127-
});
128-
player.on("pause", () => setPlayerStatus("paused"));
129-
player.on("waiting", () => setPlayerStatus("loading"));
130-
player.on("error", () => setPlayerStatus("error"));
131-
}, []);
126+
player.on("playing", () => {
127+
setPlayerStatus("playing");
128+
currentFeed?.slug && analytics.stream.started(currentFeed.slug);
129+
});
130+
player.on("pause", () => {
131+
setPlayerStatus("paused");
132+
currentFeed?.slug && analytics.stream.paused(currentFeed.slug);
133+
});
134+
player.on("waiting", () => setPlayerStatus("loading"));
135+
player.on("error", () => {
136+
setPlayerStatus("error");
137+
currentFeed?.slug && analytics.stream.error(currentFeed.slug);
138+
});
139+
},
140+
[currentFeed?.slug],
141+
);
132142

133143
const handlePlayPauseClick = async () => {
134144
const player = playerRef.current;
@@ -146,10 +156,10 @@ export default function Player({
146156
try {
147157
if (playerStatus === "loading" || playerStatus === "playing") {
148158
await player.pause();
149-
currentFeed?.slug && analytics.stream.paused(currentFeed.slug);
159+
currentFeed?.slug && analytics.stream.userPaused(currentFeed.slug);
150160
} else {
151161
await player.play();
152-
currentFeed?.slug && analytics.stream.started(currentFeed.slug);
162+
currentFeed?.slug && analytics.stream.userStarted(currentFeed.slug);
153163
}
154164
} catch (e) {
155165
console.error(e);

ui/src/components/layouts/MapLayout.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ function MapLayout({ children }: { children: ReactNode }) {
4444
const [currentFeed, setCurrentFeed] = useState(feed);
4545
const [map, setMap] = useState<LeafletMap>();
4646
const feeds = useFeedsQuery().data?.feeds ?? [];
47+
const firstOnlineFeed = feeds.filter(({ online }) => online)[0];
4748

4849
// update the currentFeed only if there's a new feed
4950
useEffect(() => {
@@ -52,7 +53,10 @@ function MapLayout({ children }: { children: ReactNode }) {
5253
map?.setZoom(9);
5354
map?.panTo(feed.latLng);
5455
}
55-
}, [feed, map, currentFeed]);
56+
if (!feed && !currentFeed && firstOnlineFeed) {
57+
setCurrentFeed(firstOnlineFeed);
58+
}
59+
}, [feed, map, currentFeed, firstOnlineFeed]);
5660

5761
const invalidateSize = () => {
5862
if (map) {

ui/src/graphql/generated/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2338,6 +2338,8 @@ export type FeedsQuery = {
23382338
imageUrl?: string | null;
23392339
thumbUrl?: string | null;
23402340
mapUrl?: string | null;
2341+
bucket: string;
2342+
online?: boolean | null;
23412343
latLng: { __typename?: "LatLng"; lat: number; lng: number };
23422344
}>;
23432345
};
@@ -3207,6 +3209,8 @@ export const FeedsDocument = `
32073209
imageUrl
32083210
thumbUrl
32093211
mapUrl
3212+
bucket
3213+
online
32103214
}
32113215
}
32123216
`;

ui/src/graphql/queries/listFeeds.graphql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,7 @@ query feeds {
1111
imageUrl
1212
thumbUrl
1313
mapUrl
14+
bucket
15+
online
1416
}
1517
}

ui/src/utils/analytics.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ReactGA from "react-ga4";
22

33
export const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_ID;
4+
let analyticsInitialized = false;
45

56
const about = {
67
sampleAudioPlayed: (exampleTitle: string) =>
@@ -49,6 +50,24 @@ const stream = {
4950
action: "Player paused",
5051
label: feedSlug,
5152
}),
53+
userStarted: (feedSlug: string) =>
54+
sendEvent({
55+
category: "Stream",
56+
action: "User started player",
57+
label: feedSlug,
58+
}),
59+
userPaused: (feedSlug: string) =>
60+
sendEvent({
61+
category: "Stream",
62+
action: "User paused player",
63+
label: feedSlug,
64+
}),
65+
error: (feedSlug: string) =>
66+
sendEvent({
67+
category: "Stream",
68+
action: "Player errored",
69+
label: feedSlug,
70+
}),
5271
playerTextClicked: (playerText: string) => {
5372
sendEvent({
5473
category: "Stream",
@@ -59,9 +78,19 @@ const stream = {
5978
};
6079

6180
function sendEvent(...eventParams: Parameters<typeof ReactGA.event>) {
62-
if (GA_TRACKING_ID) {
63-
ReactGA.initialize(GA_TRACKING_ID);
64-
ReactGA.event(...eventParams);
81+
try {
82+
if (GA_TRACKING_ID) {
83+
if (!analyticsInitialized) {
84+
ReactGA.initialize(GA_TRACKING_ID);
85+
analyticsInitialized = true;
86+
}
87+
ReactGA.event(...eventParams);
88+
}
89+
} catch (e) {
90+
console.error("Failed to send analytics event:", {
91+
error: e,
92+
eventParams,
93+
});
6594
}
6695
}
6796

0 commit comments

Comments
 (0)