initial commit

This commit is contained in:
Filip
2026-03-11 16:06:00 +01:00
parent b3c69053f6
commit 5fd80e6dd6
127 changed files with 39684 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
import SwiftUI
// Group creation is handled within NewConversationSheet via the isGroup toggle.
// This file exists for potential future separation.

View File

@@ -0,0 +1,123 @@
import SwiftUI
struct GroupInfoView: View {
let conversation: Conversation
var appState: AppState
@State private var showRenameSheet = false
@State private var showLeaveConfirm = false
@State private var newName = ""
@Environment(\.dismiss) private var dismiss
private var isCreator: Bool {
conversation.createdBy == appState.currentUser?.id
}
var body: some View {
NavigationStack {
List {
// Avatar section
Section {
HStack {
Spacer()
VStack(spacing: 8) {
CircularAvatarView(
name: conversation.name ?? "Group",
size: 64,
isGroup: true
)
Text(conversation.name ?? "Group")
.font(.title2.bold())
Text("\(conversation.members.count) members")
.font(.subheadline)
.foregroundStyle(.secondary)
}
Spacer()
}
.listRowBackground(Color.clear)
}
// Actions
if isCreator {
Section {
Button("Rename Group") {
newName = conversation.name ?? ""
showRenameSheet = true
}
Button("Change Avatar") {
// Photo picker would go here
}
}
}
// Members
Section("Members") {
ForEach(conversation.members) { member in
HStack {
CircularAvatarView(name: member.username, size: 32, isGroup: false)
VStack(alignment: .leading) {
Text(member.username)
.font(.body)
Text(member.email)
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer()
if member.userId == conversation.createdBy {
Text("Admin")
.font(.caption)
.foregroundStyle(.blue)
}
}
}
}
// Leave / Delete
Section {
Button("Leave Group", role: .destructive) {
showLeaveConfirm = true
}
if isCreator {
Button("Delete Group", role: .destructive) {
Task {
await appState.chatClient.deleteConversation(convId: conversation.id)
dismiss()
}
}
}
}
}
.navigationTitle("Group Info")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("Done") { dismiss() }
}
}
.alert("Leave Group?", isPresented: $showLeaveConfirm) {
Button("Cancel", role: .cancel) {}
Button("Leave", role: .destructive) {
Task {
await appState.chatClient.leaveGroup(convId: conversation.id)
dismiss()
}
}
}
.alert("Rename Group", isPresented: $showRenameSheet) {
TextField("Group Name", text: $newName)
Button("Cancel", role: .cancel) {}
Button("Rename") {
Task {
await appState.chatClient.renameConversation(convId: conversation.id, name: newName)
}
}
}
}
}
}

View File

@@ -0,0 +1,41 @@
import SwiftUI
struct InvitationBanner: View {
let invitation: Invitation
let onAccept: () -> Void
let onDecline: () -> Void
var body: some View {
VStack(alignment: .leading, spacing: 8) {
HStack {
Image(systemName: "envelope.badge")
.foregroundStyle(.orange)
VStack(alignment: .leading) {
Text(invitation.conversationName)
.font(.body.bold())
Text("Invited by \(invitation.invitedByUsername)")
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer()
}
HStack(spacing: 12) {
Button("Accept") {
onAccept()
}
.buttonStyle(.borderedProminent)
.controlSize(.small)
Button("Decline") {
onDecline()
}
.buttonStyle(.bordered)
.controlSize(.small)
}
}
.padding(.vertical, 4)
}
}