feat: discover people tab, privacy settings, and friend request button fix

- Add "Discover People" section to Add Friend tab with user cards, mutual counts, and inline actions
- Add discoverStore for fetching/searching discoverable users across local and federated instances
- Add PrivacyPanel to user settings with discoverability toggle
- Add is_discoverable column to users table with migration
- Fix "Send Friend Request" button vertical alignment using transform centering
This commit is contained in:
Jannis Braun
2026-03-13 22:54:19 +01:00
parent 9382477e33
commit b194cc1915
9 changed files with 584 additions and 31 deletions
+6
View File
@@ -132,6 +132,12 @@ export function runMigrations(db: Database.Database): void {
columns: [
{ name: 'profile_updated_at', type: 'INTEGER' },
]
},
{
name: 'users',
columns: [
{ name: 'discoverable', type: 'INTEGER DEFAULT 1' },
]
}
];
+1
View File
@@ -17,6 +17,7 @@ export const users = sqliteTable('users', {
avatarColor: text('avatar_color'),
bio: text('bio'),
isDeleted: integer('is_deleted').default(0),
discoverable: integer('discoverable').default(1),
profileUpdatedAt: integer('profile_updated_at'),
createdAt: integer('created_at').notNull(),
});
+8 -1
View File
@@ -255,7 +255,7 @@ export async function userRoutes(app: FastifyInstance): Promise<void> {
});
app.patch<{ Body: UpdateUserRequest }>('/api/users/@me', { preHandler: authenticate }, async (request, reply) => {
const { displayName, avatar, banner, accentColor, avatarColor, bio, customStatus, status, replicatedInstances, homeUserId, profileUpdatedAt } = request.body;
const { displayName, avatar, banner, accentColor, avatarColor, bio, customStatus, status, replicatedInstances, homeUserId, profileUpdatedAt, discoverable } = request.body;
const db = getDb();
const updateData: Record<string, string | null | undefined> = {};
@@ -366,6 +366,13 @@ export async function userRoutes(app: FastifyInstance): Promise<void> {
}
}
if (discoverable !== undefined) {
if (typeof discoverable !== 'boolean') {
return reply.code(400).send({ error: 'discoverable must be a boolean', statusCode: 400 });
}
(updateData as Record<string, unknown>).discoverable = discoverable ? 1 : 0;
}
if (Object.keys(updateData).length === 0) {
return reply.code(400).send({ error: 'No fields to update', statusCode: 400 });
}
+2
View File
@@ -17,6 +17,7 @@ export function sanitizeUser(row: typeof schema.users.$inferSelect): User {
customStatus: null,
isAdmin: false,
isDeleted: true,
discoverable: false,
profileUpdatedAt: 0,
createdAt: row.createdAt,
homeInstance: null,
@@ -46,6 +47,7 @@ export function sanitizeUser(row: typeof schema.users.$inferSelect): User {
status: (row.status ?? 'offline') as User['status'],
customStatus: row.customStatus,
isAdmin: row.isAdmin === 1,
discoverable: row.discoverable !== 0,
profileUpdatedAt: row.profileUpdatedAt ?? row.createdAt,
createdAt: row.createdAt,
homeInstance: row.homeInstance ?? null,