Bubble/Threaded/Components/NotificationRow.swift

260 lines
9.8 KiB
Swift

//Made by Lumaa
import SwiftUI
struct NotificationRow: View {
@EnvironmentObject private var navigator: Navigator
@State private var multiPeopleSheet: Bool = false
var notif: GroupedNotification
var showIcon: Bool = true
var body: some View {
VStack {
HStack(spacing: 5) {
if let acc = notif.accounts.first {
if notif.accounts.count == 1 {
ProfilePicture(url: acc.avatar, size: 60)
.padding(.trailing)
.overlay(alignment: .bottomTrailing) {
if showIcon {
notifIcon()
.offset(x: -5, y: 5)
}
}
.padding(.horizontal, 10)
.onTapGesture {
navigator.navigate(to: .account(acc: acc))
}
} else {
accountCount()
.padding(.trailing)
.overlay(alignment: .bottomTrailing) {
if showIcon {
notifIcon()
.offset(x: -5, y: 5)
}
}
.padding(.horizontal, 10)
.onTapGesture {
multiPeopleSheet.toggle()
}
}
}
VStack(alignment: .leading) {
Text(localizedString())
.multilineTextAlignment(.leading)
.font(.subheadline)
.lineLimit(2)
if notif.status != nil {
TextEmoji(notif.status!.content, emojis: notif.status!.emojis)
.multilineTextAlignment(.leading)
.font(.caption)
.foregroundStyle(Color.gray)
.lineLimit(3, reservesSpace: true)
HStack(spacing: 7.5) {
if notif.status!.mediaAttachments.count > 0 {
Label("activity.status.attachments-\(notif.status!.mediaAttachments.count)", systemImage: notif.status!.mediaAttachments.count > 1 ? "photo.on.rectangle.angled" : "photo")
.multilineTextAlignment(.leading)
.font(.caption)
.foregroundStyle(Color.gray)
.lineLimit(1, reservesSpace: false)
}
Spacer()
Text(notif.notifications[0].createdAt.relativeFormatted)
.multilineTextAlignment(.leading)
.font(.caption)
.foregroundStyle(Color.gray)
.lineLimit(1, reservesSpace: false)
}
} else {
if let acc = notif.accounts.first {
TextEmoji(acc.note, emojis: acc.emojis)
.multilineTextAlignment(.leading)
.font(.caption)
.foregroundStyle(Color.gray)
.lineLimit(3, reservesSpace: true)
}
}
}
.contentShape(Rectangle())
.onTapGesture {
navigator.navigate(to: notif.status == nil ? .account(acc: notif.accounts.first!) : .post(status: notif.status!))
}
}
.padding(.horizontal)
.sheet(isPresented: $multiPeopleSheet) {
users
.presentationDetents([.height(200), .medium])
.presentationDragIndicator(.visible)
.presentationCornerRadius(25)
.scrollBounceBehavior(.basedOnSize)
}
}
}
var users: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .listRowSeparatorLeading) {
ForEach(notif.accounts) { acc in
HStack {
ProfilePicture(url: acc.avatar, size: 60)
.padding(.horizontal)
VStack(alignment: .leading) {
Text(acc.displayName ?? acc.username)
.font(.body.bold())
Text("@\(acc.acct)")
.font(.caption)
.foregroundStyle(Color.gray)
.lineLimit(1)
}
Spacer()
}
.padding(.vertical)
.onTapGesture {
multiPeopleSheet.toggle()
navigator.navigate(to: .account(acc: acc))
}
}
}
.padding(.top)
}
}
private func localizedString() -> LocalizedStringKey {
var nameStr: String = ""
if notif.accounts.count > 1, let acc = notif.accounts.first {
nameStr = String(localized: "activity.group.\("@" + acc.username)-\(notif.accounts.count - 1)")
} else if let acc = notif.accounts.first {
nameStr = "@\(acc.username)"
}
switch (notif.type) {
case .favourite:
return "activity.favorite.\(nameStr)"
case .follow:
return "activity.followed.\(nameStr)"
case .mention:
return "activity.mentionned.\(nameStr)"
case .reblog:
return "activity.reblogged.\(nameStr)"
case .status:
return "activity.status.\(nameStr)"
case .update:
return "activity.update.\(nameStr)"
case .poll:
return "activity.poll.\(nameStr)"
default:
return "activity.unknown" // follow requests
}
}
private func notifColor() -> Color {
switch (notif.type) {
case .favourite:
return Color.red
case .follow:
return Color.purple
case .mention:
return Color.blue
case .reblog:
return Color.orange
case .status, .update: // update and post are techn. the same
return Color.yellow
case .poll:
return Color.green
default:
return Color.gray
}
}
@ViewBuilder
private func notifIcon() -> some View {
let size: CGFloat = 60.0 / 4.0
ZStack {
switch (notif.type) {
case .favourite:
Image(systemName: "heart.fill")
.resizable()
.scaledToFit()
.frame(width: size, height: size)
case .follow:
Image(systemName: "person.fill.badge.plus")
.resizable()
.scaledToFit()
.frame(width: size, height: size)
case .mention:
Image(systemName: "tag.fill")
.resizable()
.scaledToFit()
.frame(width: size, height: size)
case .reblog:
Image(systemName: "bolt.horizontal.fill")
.resizable()
.scaledToFit()
.frame(width: size, height: size)
case .status:
Image(systemName: "text.badge.plus")
.resizable()
.scaledToFit()
.frame(width: size, height: size)
case .update:
Image(systemName: "pencil.and.scribble")
.resizable()
.scaledToFit()
.frame(width: size, height: size)
case .poll:
Image(systemName: "checklist")
.resizable()
.scaledToFit()
.frame(width: size, height: size)
default:
Image(systemName: "questionmark")
.resizable()
.scaledToFit()
.frame(width: size, height: size)
}
}
.frame(minWidth: 30)
.padding(7)
.background(notifColor())
.clipShape(.circle)
.overlay {
Circle()
.stroke(Color.appBackground, lineWidth: 3)
}
.fixedSize()
}
@ViewBuilder
private func accountCount() -> some View {
ZStack {
Text(String("+\(notif.accounts.count - 1)"))
.font(.body)
.scaledToFit()
.lineLimit(1)
.minimumScaleFactor(0.5)
.frame(width: 30, height: 30)
}
.frame(width: 40, height: 40)
.padding(7)
.background(Color.gray)
.clipShape(.circle)
.overlay {
Circle()
.stroke(Color.appBackground, lineWidth: 3)
}
.fixedSize()
}
}