feat: explore page space banners, icon-derived gradients, and space descriptions
- Redesign explore cards with banner images, overlapping icons, and frosted fade - Extract dominant colors from space icons for dynamic banner gradients - Add space description/banner fields to schema with migration - Move origin label from banner overlay to content metadata row - Support space descriptions in settings overview panel
This commit is contained in:
@@ -145,6 +145,7 @@ function buildFullSpace(spaceId: string, forUserId: string): SpaceWithChannelsAn
|
||||
id: space.id,
|
||||
name: space.name,
|
||||
icon: space.icon,
|
||||
banner: space.banner ?? null,
|
||||
ownerId: space.ownerId,
|
||||
inviteCode: space.inviteCode,
|
||||
visibility: (space.visibility ?? 'private') as SpaceWithChannelsAndMembers['visibility'],
|
||||
@@ -202,7 +203,7 @@ export async function exploreRoutes(app: FastifyInstance): Promise<void> {
|
||||
|
||||
let countSql = `SELECT COUNT(DISTINCT s.id) as total FROM spaces s WHERE s.visibility IN ('public', 'request')`;
|
||||
let querySql = `
|
||||
SELECT s.id, s.name, s.icon, s.description, s.visibility, s.created_at,
|
||||
SELECT s.id, s.name, s.icon, s.banner, s.description, s.visibility, s.created_at,
|
||||
COUNT(sm.user_id) as member_count
|
||||
FROM spaces s
|
||||
LEFT JOIN space_members sm ON sm.space_id = s.id
|
||||
@@ -225,6 +226,7 @@ export async function exploreRoutes(app: FastifyInstance): Promise<void> {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string | null;
|
||||
banner: string | null;
|
||||
description: string | null;
|
||||
visibility: string;
|
||||
created_at: number;
|
||||
@@ -235,6 +237,7 @@ export async function exploreRoutes(app: FastifyInstance): Promise<void> {
|
||||
id: r.id,
|
||||
name: r.name,
|
||||
icon: r.icon,
|
||||
banner: r.banner,
|
||||
description: r.description,
|
||||
visibility: r.visibility as ExploreSpace['visibility'],
|
||||
memberCount: r.member_count,
|
||||
|
||||
@@ -25,6 +25,7 @@ function rowToSpace(row: typeof schema.spaces.$inferSelect): Space {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
icon: row.icon,
|
||||
banner: row.banner ?? null,
|
||||
ownerId: row.ownerId,
|
||||
inviteCode: row.inviteCode,
|
||||
visibility: (row.visibility ?? 'private') as Space['visibility'],
|
||||
@@ -54,7 +55,7 @@ export async function spaceRoutes(app: FastifyInstance): Promise<void> {
|
||||
app.post<{ Body: CreateSpaceRequest }>('/api/spaces', {
|
||||
preHandler: authenticate,
|
||||
}, async (request, reply) => {
|
||||
const { name, icon, visibility, description } = request.body;
|
||||
const { name, icon, banner, visibility, description } = request.body;
|
||||
|
||||
if (!name || typeof name !== 'string') {
|
||||
return reply.code(400).send({ error: 'Space name is required', statusCode: 400 });
|
||||
@@ -84,6 +85,7 @@ export async function spaceRoutes(app: FastifyInstance): Promise<void> {
|
||||
id: spaceId,
|
||||
name: trimmedName,
|
||||
icon: icon ?? null,
|
||||
banner: banner ?? null,
|
||||
ownerId: request.userId,
|
||||
inviteCode,
|
||||
visibility: safeVisibility,
|
||||
@@ -269,7 +271,7 @@ export async function spaceRoutes(app: FastifyInstance): Promise<void> {
|
||||
preHandler: authenticate,
|
||||
}, async (request, reply) => {
|
||||
const { id } = request.params;
|
||||
const { name, icon, visibility, description } = request.body;
|
||||
const { name, icon, banner, visibility, description } = request.body;
|
||||
const db = getDb();
|
||||
|
||||
const server = db.select().from(schema.spaces).where(eq(schema.spaces.id, id)).get();
|
||||
@@ -295,6 +297,10 @@ export async function spaceRoutes(app: FastifyInstance): Promise<void> {
|
||||
updates.icon = icon || null;
|
||||
}
|
||||
|
||||
if (banner !== undefined) {
|
||||
updates.banner = banner || null;
|
||||
}
|
||||
|
||||
if (visibility !== undefined) {
|
||||
const validVisibilities = ['public', 'request', 'private'];
|
||||
if (!validVisibilities.includes(visibility)) {
|
||||
|
||||
Reference in New Issue
Block a user