Songs offered links to all five services. Settings now has "Your music services": pick the ones you use and songs only show those, with the main one as the big button. Until someone picks, they still see all of them. Onboarding sets it to the app you said you listen with. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
ALTER TABLE "users" ADD COLUMN "services" text[];
|
||||
File diff suppressed because it is too large
Load Diff
@@ -99,6 +99,13 @@
|
||||
"when": 1791308012441,
|
||||
"tag": "0013_reactions",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 14,
|
||||
"version": "7",
|
||||
"when": 1792025011207,
|
||||
"tag": "0014_user_services",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -31,6 +31,8 @@ export const users = pgTable('users', {
|
||||
/** Path of the uploaded picture (`/avatars/<id>?v=<version>`), resolved against API_PUBLIC_URL when sent. */
|
||||
avatarUrl: varchar('avatar_url', { length: 500 }),
|
||||
preferredService: text('preferred_service').$type<StreamingService>(),
|
||||
/** The services they use, so songs only offer those; null until they choose (offer all). */
|
||||
services: text('services').array().$type<StreamingService[]>(),
|
||||
/** Billing plan. Kept in sync by the billing webhook (RevenueCat/Stripe) — see lib/entitlements.ts */
|
||||
/** The first account on a server becomes admin (moderation). */
|
||||
role: text('role').$type<'user' | 'admin'>().notNull().default('user'),
|
||||
|
||||
@@ -24,6 +24,7 @@ export const toMeDTO = (u: User): MeDTO => ({
|
||||
pendingEmail: u.pendingEmail,
|
||||
bio: u.bio,
|
||||
preferredService: u.preferredService,
|
||||
services: u.services,
|
||||
role: u.role,
|
||||
onboardedAt: u.onboardedAt?.toISOString() ?? null,
|
||||
sharingPausedUntil: isSharingPaused(u) ? u.sharingPausedUntil!.toISOString() : null,
|
||||
|
||||
@@ -115,3 +115,16 @@ describe('onboarding flag', () => {
|
||||
expect(again.onboardedAt).toBe(first.onboardedAt);
|
||||
});
|
||||
});
|
||||
|
||||
describe('music services', () => {
|
||||
it('saves the services you use, without duplicates, and clears them', async () => {
|
||||
const { token, user } = await createUser(app(), 'listener');
|
||||
expect(user.services).toBeNull();
|
||||
const patch = (payload: object) => app().inject({ method: 'PATCH', url: '/me', headers: bearer(token), payload });
|
||||
const saved = (await patch({ services: ['spotify', 'tidal', 'spotify'], preferredService: 'tidal' })).json();
|
||||
expect(saved.services).toEqual(['spotify', 'tidal']);
|
||||
expect(saved.preferredService).toBe('tidal');
|
||||
expect((await patch({ services: ['napster'] })).statusCode).toBe(400);
|
||||
expect((await patch({ services: null })).json().services).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user