2024-01-31 16:47:29 +01:00
|
|
|
//Made by Lumaa
|
|
|
|
|
|
|
|
import SwiftUI
|
2024-02-08 08:34:17 +01:00
|
|
|
import TipKit
|
2024-01-31 16:47:29 +01:00
|
|
|
|
|
|
|
struct NotificationsView: View {
|
|
|
|
@Environment(AccountManager.self) private var accountManager
|
|
|
|
|
|
|
|
@State private var navigator: Navigator = Navigator()
|
|
|
|
@State private var notifications: [Notification] = []
|
2024-02-08 08:34:17 +01:00
|
|
|
@State private var loadingNotifs: Bool = true
|
2024-01-31 16:47:29 +01:00
|
|
|
@State private var lastId: Int? = nil
|
|
|
|
private let notifLimit = 50
|
|
|
|
|
2024-02-10 02:22:39 +01:00
|
|
|
@State private var messages: [MessageContact] = []
|
2024-02-08 08:34:17 +01:00
|
|
|
private var msgBadge: Int {
|
2024-02-10 02:22:39 +01:00
|
|
|
messages.filter({ $0.unread == true }).count
|
2024-02-08 08:34:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private let msgTip: MsgTip = .init()
|
2024-01-31 16:47:29 +01:00
|
|
|
|
|
|
|
var body: some View {
|
|
|
|
NavigationStack(path: $navigator.path) {
|
|
|
|
if !notifications.isEmpty {
|
|
|
|
ScrollView(.vertical, showsIndicators: false) {
|
2024-02-04 08:43:56 +01:00
|
|
|
LazyVStack(alignment: .leading, spacing: 15) {
|
2024-01-31 16:47:29 +01:00
|
|
|
ForEach(notifications) { notif in
|
|
|
|
NotificationRow(notif: notif)
|
|
|
|
.onDisappear() {
|
|
|
|
guard !notifications.isEmpty else { return }
|
|
|
|
lastId = notifications.firstIndex(where: { $0.id == notif.id })
|
|
|
|
}
|
|
|
|
}
|
2024-02-04 08:43:56 +01:00
|
|
|
|
|
|
|
if loadingNotifs {
|
|
|
|
ProgressView()
|
|
|
|
.progressViewStyle(.circular)
|
|
|
|
.padding(.vertical)
|
|
|
|
}
|
2024-01-31 16:47:29 +01:00
|
|
|
}
|
|
|
|
.onChange(of: lastId ?? 0) { _, new in
|
|
|
|
guard !loadingNotifs else { return }
|
|
|
|
Task {
|
|
|
|
loadingNotifs = true
|
|
|
|
await fetchNotifications(lastId: new)
|
|
|
|
loadingNotifs = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-02-04 10:44:51 +01:00
|
|
|
.environmentObject(navigator)
|
2024-02-04 08:43:56 +01:00
|
|
|
.withAppRouter(navigator)
|
|
|
|
.background(Color.appBackground)
|
|
|
|
.refreshable {
|
|
|
|
await fetchNotifications(lastId: nil)
|
|
|
|
}
|
|
|
|
.navigationTitle(String(localized: "activity"))
|
2024-02-08 08:34:17 +01:00
|
|
|
.toolbar {
|
|
|
|
ToolbarItem(placement: .primaryAction) {
|
|
|
|
Button {
|
|
|
|
msgTip.invalidate(reason: .actionPerformed)
|
2024-02-10 02:22:39 +01:00
|
|
|
navigator.navigate(to: .contacts)
|
2024-02-08 08:34:17 +01:00
|
|
|
} label: {
|
|
|
|
Image(systemName: "paperplane")
|
|
|
|
.foregroundStyle(Color(uiColor: UIColor.label))
|
|
|
|
.overlay(alignment: .topTrailing) {
|
|
|
|
if msgBadge > 0 {
|
|
|
|
Text("\(msgBadge)")
|
|
|
|
.foregroundStyle(Color.white)
|
|
|
|
.font(.caption)
|
|
|
|
.padding(5)
|
|
|
|
.background(Color.red)
|
|
|
|
.clipShape(Circle())
|
|
|
|
.offset(x: 5, y: -7)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.popoverTip(msgTip, arrowEdge: .top)
|
|
|
|
.tipViewStyle(HeadlineTipViewStyle(headlineType: .meta))
|
|
|
|
}
|
|
|
|
}
|
2024-01-31 16:47:29 +01:00
|
|
|
} else if loadingNotifs == false && notifications.isEmpty {
|
|
|
|
ZStack {
|
|
|
|
Color.appBackground
|
|
|
|
.ignoresSafeArea()
|
|
|
|
|
|
|
|
ContentUnavailableView("activity.no-notifications", systemImage: "bolt.heart")
|
|
|
|
}
|
|
|
|
} else if loadingNotifs == true && notifications.isEmpty {
|
|
|
|
ZStack {
|
|
|
|
Color.appBackground
|
|
|
|
.ignoresSafeArea()
|
|
|
|
|
|
|
|
ProgressView()
|
|
|
|
.progressViewStyle(.circular)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.task {
|
|
|
|
loadingNotifs = true
|
|
|
|
await fetchNotifications(lastId: nil)
|
|
|
|
loadingNotifs = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchNotifications(lastId: Int? = nil) async {
|
|
|
|
guard let client = accountManager.getClient() else { return }
|
|
|
|
|
|
|
|
if lastId != nil {
|
|
|
|
guard lastId! >= notifications.count - 6 else { return }
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
2024-02-10 02:22:39 +01:00
|
|
|
var notifs: [Notification] = try await client.get(endpoint: Notifications.notifications(minId: nil, maxId: nil, types: nil, limit: lastId != nil ? notifLimit : 30))
|
2024-01-31 16:47:29 +01:00
|
|
|
guard !notifs.isEmpty else { return }
|
|
|
|
|
2024-02-10 02:22:39 +01:00
|
|
|
notifs = notifs.filter({ $0.supportedType != .mention && $0.status?.visibility != .direct })
|
|
|
|
|
2024-01-31 16:47:29 +01:00
|
|
|
if notifications.isEmpty {
|
|
|
|
notifications = notifs
|
|
|
|
} else {
|
|
|
|
notifications.append(contentsOf: notifs)
|
|
|
|
}
|
2024-02-08 08:34:17 +01:00
|
|
|
|
2024-02-10 02:22:39 +01:00
|
|
|
await getBadge()
|
2024-01-31 16:47:29 +01:00
|
|
|
} catch {
|
|
|
|
print(error)
|
|
|
|
}
|
|
|
|
}
|
2024-02-08 08:34:17 +01:00
|
|
|
|
2024-02-10 02:22:39 +01:00
|
|
|
func getBadge() async {
|
|
|
|
guard let client = accountManager.getClient() else { return }
|
|
|
|
|
|
|
|
do {
|
|
|
|
let msgs: [MessageContact] = try await client.get(endpoint: Conversations.conversations(maxId: nil))
|
|
|
|
guard !msgs.isEmpty else { return }
|
|
|
|
|
|
|
|
messages = msgs
|
|
|
|
} catch {
|
|
|
|
print(error)
|
|
|
|
}
|
2024-02-08 08:34:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
struct MsgTip: Tip {
|
|
|
|
var title: Text = Text("activity.tip.messages.title")
|
|
|
|
var message: Text? = Text("activity.tip.messages.desc")
|
|
|
|
var id: String = "fr.lumaa.Threaded.MsgTip"
|
|
|
|
var image: Image? = Image(systemName: "paperplane")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public extension Array where Element: Hashable {
|
|
|
|
func uniqued() -> [Element] {
|
|
|
|
var seen = Set<Element>()
|
|
|
|
return filter { seen.insert($0).inserted }
|
|
|
|
}
|
2024-01-31 16:47:29 +01:00
|
|
|
}
|