6 Commits
Author SHA1 Message Date
chrisandClaude Opus 5.5 3a462ee146 Remove the seriouscritter notifications section
Reverts 6537c34 and its syntax fix 4bdfae1. The section only regrouped a
user's own notifications from that account (duplicating them in the list),
and the actorId filter it added to listNotifications was never used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FQFD3mtUWSZpeLbKzkCohh
2026-10-26 11:42:54 -04:00
chrisandClaude Opus 5.5 4bdfae155a Fix JSX syntax error on notifications page
The body span was missing its closing brace, which broke typecheck and
the web Docker build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 11:29:54 -04:00
chrisandClaude Opus 5.5 5778035656 Version 1.5.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 11:29:54 -04:00
chrisandClaude Opus 5.5 0a28f05d53 Don't share phone videos and screen recordings as now-playing
General-purpose players (VLC, MX Player, Photos, galleries) report a local
file's name as the title. Skip sessions whose title looks like a video file
or camera/recorder name, or that come from such a player without a real
artist tag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 11:29:54 -04:00
chrisandaider 6537c34a38 feat: Display dedicated notifications section for seriouscritter
Co-authored-by: aider (ollama_chat/hf.co/TrevorJS/gemma-4-E4B-it-uncensored-GGUF:Q4_K_M) <aider@aider.chat>
2026-10-26 11:29:54 -04:00
chrisandClaude Opus 5.5 094f116b10 CI: test builds' publish step works under plain sh
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Y2iUsKEWDuRGjK5FPmcUP
2026-10-22 12:01:20 -04:00
3 changed files with 11 additions and 60 deletions
+1 -1
View File
@@ -117,7 +117,7 @@ jobs:
echo "Release: $SERVER/$REPO/releases/tag/$REF_NAME"
else
owner=${REPO%%/*}
version="$(echo "$REF_NAME" | tr -c 'A-Za-z0-9._-\n' '-')-${SHA::8}"
version="$(printf '%s' "$REF_NAME" | tr -c 'A-Za-z0-9._-' '-')-$(printf '%s' "$SHA" | cut -c1-8)"
for f in *.apk SHA256SUMS; do
curl -fsS -X PUT -H "$auth" --upload-file "$f" "$SERVER/api/packages/$owner/generic/musicshare-android/$version/$f"
echo "Uploaded $f"
+5 -17
View File
@@ -103,27 +103,15 @@ export async function toNotificationDTOs(rows: Row[]): Promise<NotificationDTO[]
}
/** Newest first, 30 at a time; the cursor is the last one's time. */
export async function listNotifications(
userId: string,
cursor: string | null,
actorId?: string // Added optional actorId filter
): Promise<{
notifications: NotificationDTO[];
unread: number;
nextCursor: string | null;
}> {
export async function listNotifications(userId: string, cursor: string | null) {
const n = schema.notifications;
const before = cursor ? new Date(cursor) : null;
const query = db
const rows = await db
.select()
.from(n)
.where(and(
eq(n.userId, userId),
before && !isNaN(before.getTime()) ? lt(n.createdAt, before) : undefined,
actorId ? eq(n.actorId, actorId) : undefined // Apply actor filter if provided
));
const rows = await query.orderBy(desc(n.createdAt)).limit(31);
.where(and(eq(n.userId, userId), before && !isNaN(before.getTime()) ? lt(n.createdAt, before) : undefined))
.orderBy(desc(n.createdAt))
.limit(31);
const page = rows.slice(0, 30);
return {
notifications: await toNotificationDTOs(page),
+5 -42
View File
@@ -8,68 +8,32 @@ import { notificationPath, notificationText, timeAgo, type NotificationListDTO }
import { Avatar } from '@/components/Avatar';
import { client, useMe } from '@/lib/api';
const ADMIN_ACTOR_USERNAME = 'seriouscritter';
/** Reactions, comments, new followers and songs sent to you, newest first. Opening it marks them read. */
export default function NotificationsPage() {
const { me, loggedOut } = useMe();
const { mutate } = useSWRConfig();
// Fetch all notifications for the user
const { data, size, setSize, isLoading } = useSWRInfinite<NotificationListDTO>(
(i, prev: NotificationListDTO | null) => (!me || (i > 0 && !prev?.nextCursor) ? null : ['notifications', prev?.nextCursor ?? null]),
([, cursor]: [string, string | null]) => client.notifications(cursor),
{ revalidateOnFocus: false },
);
const allItems = data?.flatMap((p) => p.notifications) ?? [];
const items = data?.flatMap((p) => p.notifications) ?? [];
const unread = data?.[0]?.unread ?? 0;
// Filter messages specifically from seriouscritter
const seriousCritterMessages = allItems.filter(n => n.actor?.username === ADMIN_ACTOR_USERNAME);
// Seen once you've looked: clear the bell (rows stay highlighted until you come back).
useEffect(() => {
if (unread) client.markNotificationsRead().then(() => mutate('/notifications'), () => {});
}, [unread, mutate]);
if (loggedOut) return <p className="muted"><Link href="/login">Log in</Link> to see your notifications.</p>;
return (
<div className="stack">
<h1>Notifications</h1>
{/* Dedicated section for seriouscritter's messages */}
{seriousCritterMessages.length > 0 && (
<div className="admin-section">
<h2 className="muted">From {ADMIN_ACTOR_USERNAME}</h2>
<ul className="notifications">
{seriousCritterMessages.map((n) => {
const { title, body } = notificationText(n);
return (
<li key={n.id} className={n.read ? '' : 'unread'}>
<Link href={notificationPath(n)} className="notification">
<Avatar user={n.actor} size={40} />
<span className="notification-text">
<strong>{title}</strong>
<span>{body</span >
<span className="muted">{timeAgo(n.createdAt)}</span>
</span>
{n.track?.artworkUrl && <img className="artwork-sm" src={n.track.artworkUrl} alt="" />}
</Link>
</li>
);
})}
</ul>
</div>
)}
{/* General notifications list */}
{allItems.length === 0 && !isLoading && (
{items.length === 0 && !isLoading && (
<p className="muted">Nothing yet. Reactions, new followers and songs sent to you show up here.</p>
)}
<ul className="notifications">
{allItems.map((n) => {
{items.map((n) => {
const { title, body } = notificationText(n);
return (
<li key={n.id} className={n.read ? '' : 'unread'}>
@@ -77,7 +41,7 @@ export default function NotificationsPage() {
<Avatar user={n.actor} size={40} />
<span className="notification-text">
<strong>{title}</strong>
<span>{body</span >
<span>{body}</span>
<span className="muted">{timeAgo(n.createdAt)}</span>
</span>
{n.track?.artworkUrl && <img className="artwork-sm" src={n.track.artworkUrl} alt="" />}
@@ -86,12 +50,11 @@ export default function NotificationsPage() {
);
})}
</ul>
{data?.at(-1)?.nextCursor && (
<button className="btn" onClick={() => setSize(size + 1)}>
Show more
</button>
)}
</div >
</div>
);
}