mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-29 11:41:01 +00:00
Merge branch 'main' of https://github.com/Dimillian/IceCubesApp
This commit is contained in:
commit
d8a5e39606
10 changed files with 254 additions and 116 deletions
|
@ -18,6 +18,7 @@
|
||||||
<string>%lld nieuwe posts</string>
|
<string>%lld nieuwe posts</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
||||||
<key>notifications-others-count %lld</key>
|
<key>notifications-others-count %lld</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
@ -34,5 +35,73 @@
|
||||||
<string> en %lld anderen </string>
|
<string> en %lld anderen </string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>notifications.label.mention %lld</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@count@</string>
|
||||||
|
<key>count</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>lld</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>heeft jou vermeld</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>hebben jou vermeld</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
|
||||||
|
<key>notifications.label.follow %lld</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@count@</string>
|
||||||
|
<key>count</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>lld</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>volgt jou</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>volgen jou</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
|
||||||
|
<key>notifications.label.reblog %lld</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@count@</string>
|
||||||
|
<key>count</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>lld</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>boostte</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>boostten</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
|
||||||
|
<key>notifications.label.favorite %lld</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@count@</string>
|
||||||
|
<key>count</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>lld</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>markeerde jouw bericht als favoriet</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>markeerden jouw bericht als favoriet</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -13,9 +13,9 @@
|
||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>lld</string>
|
<string>lld</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string>%lld 个新嘟文</string>
|
<string>%lld 条新嘟文</string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string>%lld 个新嘟文</string>
|
<string>%lld 条新嘟文</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>notifications-others-count %lld</key>
|
<key>notifications-others-count %lld</key>
|
||||||
|
@ -29,9 +29,9 @@
|
||||||
<key>NSStringFormatValueTypeKey</key>
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
<string>lld</string>
|
<string>lld</string>
|
||||||
<key>one</key>
|
<key>one</key>
|
||||||
<string> 和其它 %lld 个用户 </string>
|
<string> 和其他 %lld 个用户 </string>
|
||||||
<key>other</key>
|
<key>other</key>
|
||||||
<string> 和其它 %lld 个用户 </string>
|
<string> 和其他 %lld 个用户 </string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// MARK: Common strings
|
// MARK: Common strings
|
||||||
"action.cancel" = "Annuleer";
|
"action.cancel" = "Annuleer";
|
||||||
"action.delete" = "Verwijder";
|
"action.delete" = "Verwijder";
|
||||||
"action.save" = "Opslaan";
|
"action.save" = "Bewaar";
|
||||||
"action.done" = "Gereed";
|
"action.done" = "Gereed";
|
||||||
"action.retry" = "Opnieuw";
|
"action.retry" = "Opnieuw";
|
||||||
"action.view.error" = "Bekijk fout";
|
"action.view.error" = "Bekijk fout";
|
||||||
|
@ -125,10 +125,10 @@
|
||||||
"settings.push.duplicate.title" = "Dubbele meldingen";
|
"settings.push.duplicate.title" = "Dubbele meldingen";
|
||||||
"settings.push.duplicate.footer" = "Ontvang je dubbele meldingen? Gebruik deze magische knop om dit probleem te verhelpen";
|
"settings.push.duplicate.footer" = "Ontvang je dubbele meldingen? Gebruik deze magische knop om dit probleem te verhelpen";
|
||||||
"settings.push.duplicate.button.fix" = "🪄 Los op";
|
"settings.push.duplicate.button.fix" = "🪄 Los op";
|
||||||
"settings.other.autoplay-video" = "Auto Play Videos";
|
"settings.other.autoplay-video" = "Speel video’s automatisch af";
|
||||||
"settings.display.font" = "Timeline Font";
|
"settings.display.font" = "Tijdlijnlettertype";
|
||||||
"settings.display.font.system" = "System";
|
"settings.display.font.system" = "Systeem";
|
||||||
"settings.display.font.custom" = "Custom";
|
"settings.display.font.custom" = "Aangepast";
|
||||||
|
|
||||||
// MARK: Tabs
|
// MARK: Tabs
|
||||||
"tab.explore" = "Ontdekken";
|
"tab.explore" = "Ontdekken";
|
||||||
|
@ -177,8 +177,8 @@
|
||||||
"account.edit.account-settings.private" = "Privé";
|
"account.edit.account-settings.private" = "Privé";
|
||||||
"account.edit.account-settings.section-title" = "Accountinstellingen";
|
"account.edit.account-settings.section-title" = "Accountinstellingen";
|
||||||
"account.edit.display-name" = "Weergavenaam";
|
"account.edit.display-name" = "Weergavenaam";
|
||||||
"account.edit.error.save.message" = "Er heeft zich een fout voorgedaan tijdens het opslaan van je profiel. Probeer het nogmaals";
|
"account.edit.error.save.message" = "Er heeft zich een fout voorgedaan tijdens het bewaren van je profiel. Probeer het nogmaals";
|
||||||
"account.edit.error.save.title" = "Fout tijdens het opslaan van je profiel";
|
"account.edit.error.save.title" = "Fout tijdens het bewaren van je profiel";
|
||||||
"account.edit.navigation-title" = "Profiel bewerken";
|
"account.edit.navigation-title" = "Profiel bewerken";
|
||||||
"account.edit.post-settings.privacy" = "Standaardprivacy";
|
"account.edit.post-settings.privacy" = "Standaardprivacy";
|
||||||
"account.edit.post-settings.section-title" = "Postinstellingen";
|
"account.edit.post-settings.section-title" = "Postinstellingen";
|
||||||
|
@ -260,12 +260,8 @@
|
||||||
"notifications.empty.title" = "Geen notificaties";
|
"notifications.empty.title" = "Geen notificaties";
|
||||||
"notifications.error.message" = "Er heeft zich een fout voorgedaan tijdens het laden van je notificaties. Probeer het nogmaals.";
|
"notifications.error.message" = "Er heeft zich een fout voorgedaan tijdens het laden van je notificaties. Probeer het nogmaals.";
|
||||||
"notifications.error.title" = "Er heeft zich een fout voorgedaan";
|
"notifications.error.title" = "Er heeft zich een fout voorgedaan";
|
||||||
"notifications.label.favorite %lld" = "heeft jouw bericht als favoriet gemarkeerd";
|
|
||||||
"notifications.label.follow %lld" = "volgt jou";
|
|
||||||
"notifications.label.follow-request" = "wil je volgen";
|
"notifications.label.follow-request" = "wil je volgen";
|
||||||
"notifications.label.mention %lld" = "heeft jou vermeld";
|
|
||||||
"notifications.label.poll" = "poll beëindigd";
|
"notifications.label.poll" = "poll beëindigd";
|
||||||
"notifications.label.reblog %lld" = "boostte";
|
|
||||||
"notifications.label.status" = "nieuwe status";
|
"notifications.label.status" = "nieuwe status";
|
||||||
"notifications.label.update" = "wijzigde een post";
|
"notifications.label.update" = "wijzigde een post";
|
||||||
"notifications.menu-title.favorite" = "Favoriet";
|
"notifications.menu-title.favorite" = "Favoriet";
|
||||||
|
@ -289,7 +285,7 @@
|
||||||
|
|
||||||
// MARK: Package: Status
|
// MARK: Package: Status
|
||||||
"status.action.translate" = "Vertaal";
|
"status.action.translate" = "Vertaal";
|
||||||
"status.action.translate-from-%@" = "Vertaal uit %@";
|
"status.action.translate-from-%@" = "Vertaal uit het %@";
|
||||||
"status.action.translated-label" = "Vertaald met behulp van DeepL.com";
|
"status.action.translated-label" = "Vertaald met behulp van DeepL.com";
|
||||||
"status.action.bookmark" = "Voeg bladwijzer toe";
|
"status.action.bookmark" = "Voeg bladwijzer toe";
|
||||||
"status.action.boost" = "Boosten";
|
"status.action.boost" = "Boosten";
|
||||||
|
@ -310,8 +306,8 @@
|
||||||
"status.action.unfavorite" = "Verwijder favoriet";
|
"status.action.unfavorite" = "Verwijder favoriet";
|
||||||
"status.action.unpin" = "Maak los";
|
"status.action.unpin" = "Maak los";
|
||||||
"status.action.view-in-browser" = "Open in browser";
|
"status.action.view-in-browser" = "Open in browser";
|
||||||
"status.draft.delete" = "Concept verwijderen";
|
"status.draft.delete" = "Verwijder concept";
|
||||||
"status.draft.save" = "Concept opslaan";
|
"status.draft.save" = "Bewaar concept";
|
||||||
"status.editor.ai-prompt.correct" = "Corrigeer tekst";
|
"status.editor.ai-prompt.correct" = "Corrigeer tekst";
|
||||||
"status.editor.ai-prompt.emphasize" = "Benadruk tekst";
|
"status.editor.ai-prompt.emphasize" = "Benadruk tekst";
|
||||||
"status.editor.ai-prompt.fit" = "Kort tekst in";
|
"status.editor.ai-prompt.fit" = "Kort tekst in";
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
"settings.timeline.add" = "添加远程时间线";
|
"settings.timeline.add" = "添加远程时间线";
|
||||||
"settings.title" = "设置";
|
"settings.title" = "设置";
|
||||||
"settings.rate" = "给 Ice Cubes 评分";
|
"settings.rate" = "给 Ice Cubes 评分";
|
||||||
"settings.section.other" = "其它";
|
"settings.section.other" = "其他";
|
||||||
"settings.other.hide-openai" = "启用写作助手 🤖";
|
"settings.other.hide-openai" = "启用写作助手 🤖";
|
||||||
"settings.other.social-keyboard" = "启用社交键盘";
|
"settings.other.social-keyboard" = "启用社交键盘";
|
||||||
|
|
||||||
|
@ -129,9 +129,9 @@
|
||||||
"settings.push.duplicate.button.fix" = "🪄 修复";
|
"settings.push.duplicate.button.fix" = "🪄 修复";
|
||||||
"settings.other.autoplay-video" = "自动播放视频";
|
"settings.other.autoplay-video" = "自动播放视频";
|
||||||
|
|
||||||
"settings.display.font" = "Timeline Font";
|
"settings.display.font" = "时间线字体";
|
||||||
"settings.display.font.system" = "System";
|
"settings.display.font.system" = "系统";
|
||||||
"settings.display.font.custom" = "Custom";
|
"settings.display.font.custom" = "自定义";
|
||||||
|
|
||||||
// MARK: Tabs
|
// MARK: Tabs
|
||||||
"tab.explore" = "探索";
|
"tab.explore" = "探索";
|
||||||
|
@ -229,7 +229,7 @@
|
||||||
"explore.search.message-%@" = "在此界面上,你可以搜索 %@ 上的任何信息";
|
"explore.search.message-%@" = "在此界面上,你可以搜索 %@ 上的任何信息";
|
||||||
"explore.search.prompt" = "搜索用户、嘟文或标签";
|
"explore.search.prompt" = "搜索用户、嘟文或标签";
|
||||||
"explore.search.title" = "搜索你的服务器";
|
"explore.search.title" = "搜索你的服务器";
|
||||||
"explore.search.empty.message" = "搜索无结果,请尝试其它查询";
|
"explore.search.empty.message" = "搜索无结果,请尝试其他查询";
|
||||||
"explore.search.empty.title" = "无结果";
|
"explore.search.empty.title" = "无结果";
|
||||||
"explore.section.posts" = "嘟文";
|
"explore.section.posts" = "嘟文";
|
||||||
"explore.section.suggested-users" = "推荐的用户";
|
"explore.section.suggested-users" = "推荐的用户";
|
||||||
|
@ -270,7 +270,7 @@
|
||||||
"notifications.label.poll" = "投票结束";
|
"notifications.label.poll" = "投票结束";
|
||||||
"notifications.label.reblog %lld" = "已转发";
|
"notifications.label.reblog %lld" = "已转发";
|
||||||
"notifications.label.status" = "嘟嘟了一个状态";
|
"notifications.label.status" = "嘟嘟了一个状态";
|
||||||
"notifications.label.update" = "编辑了一个嘟文";
|
"notifications.label.update" = "编辑了一条嘟文";
|
||||||
"notifications.menu-title.favorite" = "收藏";
|
"notifications.menu-title.favorite" = "收藏";
|
||||||
"notifications.menu-title.follow" = "关注";
|
"notifications.menu-title.follow" = "关注";
|
||||||
"notifications.menu-title.follow-request" = "关注申请";
|
"notifications.menu-title.follow-request" = "关注申请";
|
||||||
|
@ -293,7 +293,7 @@
|
||||||
// MARK: Package: Status
|
// MARK: Package: Status
|
||||||
"status.action.translate" = "翻译";
|
"status.action.translate" = "翻译";
|
||||||
"status.action.translate-from-%@" = "翻译 %@";
|
"status.action.translate-from-%@" = "翻译 %@";
|
||||||
"status.action.translated-label" = "使用 DeepL.com 翻译";
|
"status.action.translated-label" = "由 DeepL.com 翻译";
|
||||||
"status.action.bookmark" = "书签";
|
"status.action.bookmark" = "书签";
|
||||||
"status.action.boost" = "转发";
|
"status.action.boost" = "转发";
|
||||||
"status.action.copy-text" = "拷贝文本";
|
"status.action.copy-text" = "拷贝文本";
|
||||||
|
@ -329,10 +329,10 @@
|
||||||
"status.editor.mode.edit" = "正在编辑你的嘟文";
|
"status.editor.mode.edit" = "正在编辑你的嘟文";
|
||||||
"status.editor.mode.new" = "新嘟文";
|
"status.editor.mode.new" = "新嘟文";
|
||||||
"status.editor.mode.quote-%@" = "%@ 的引用";
|
"status.editor.mode.quote-%@" = "%@ 的引用";
|
||||||
"status.editor.mode.reply-%@" = "正在回复 %@";
|
"status.editor.mode.reply-%@" = "回复 %@";
|
||||||
"status.editor.restore-previous" = "撤销更改";
|
"status.editor.restore-previous" = "撤销更改";
|
||||||
"status.editor.spoiler" = "剧透警告";
|
"status.editor.spoiler" = "剧透警告";
|
||||||
"status.editor.text.placeholder" = "在想些什么?";
|
"status.editor.text.placeholder" = "在想些什么呢?";
|
||||||
"status.editor.visibility" = "嘟文可见性";
|
"status.editor.visibility" = "嘟文可见性";
|
||||||
"status.error.loading.message" = "加载嘟文时发生错误,请重试。";
|
"status.error.loading.message" = "加载嘟文时发生错误,请重试。";
|
||||||
"status.error.message" = "嘟文的上下文出现了错误,请重试。";
|
"status.error.message" = "嘟文的上下文出现了错误,请重试。";
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
//
|
||||||
|
// ConsolidatedNotification.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Jérôme Danthinne on 31/01/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct ConsolidatedNotification: Identifiable {
|
||||||
|
public let notifications: [Notification]
|
||||||
|
public let type: Notification.NotificationType
|
||||||
|
public let createdAt: ServerDate
|
||||||
|
public let accounts: [Account]
|
||||||
|
public let status: Status?
|
||||||
|
|
||||||
|
public var id: String? { notifications.first?.id }
|
||||||
|
|
||||||
|
public init(notifications: [Notification],
|
||||||
|
type: Notification.NotificationType,
|
||||||
|
createdAt: ServerDate,
|
||||||
|
accounts: [Account],
|
||||||
|
status: Status?)
|
||||||
|
{
|
||||||
|
self.notifications = notifications
|
||||||
|
self.type = type
|
||||||
|
self.createdAt = createdAt
|
||||||
|
self.accounts = accounts
|
||||||
|
self.status = status
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func placeholder() -> ConsolidatedNotification {
|
||||||
|
.init(notifications: [Notification.placeholder()],
|
||||||
|
type: .favourite,
|
||||||
|
createdAt: "2022-12-16T10:20:54.000Z",
|
||||||
|
accounts: [.placeholder()],
|
||||||
|
status: .placeholder())
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func placeholders() -> [ConsolidatedNotification] {
|
||||||
|
[.placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder()]
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,4 +14,12 @@ public struct Notification: Decodable, Identifiable {
|
||||||
public var supportedType: NotificationType? {
|
public var supportedType: NotificationType? {
|
||||||
.init(rawValue: type)
|
.init(rawValue: type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func placeholder() -> Notification {
|
||||||
|
.init(id: UUID().uuidString,
|
||||||
|
type: NotificationType.favourite.rawValue,
|
||||||
|
createdAt: "2022-12-16T10:20:54.000Z",
|
||||||
|
account: .placeholder(),
|
||||||
|
status: .placeholder())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
//
|
||||||
|
// ConsolidatedNotificationExt.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Jérôme Danthinne on 31/01/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Models
|
||||||
|
|
||||||
|
extension ConsolidatedNotification {
|
||||||
|
var notificationIds: [String] { notifications.map(\.id) }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Array where Element == ConsolidatedNotification {
|
||||||
|
var notificationCount: Int {
|
||||||
|
reduce(0) { $0 + ($1.accounts.isEmpty ? 1 : $1.accounts.count) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
//
|
||||||
|
// Notification+Consolidated.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Jérôme Danthinne on 31/01/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Models
|
||||||
|
|
||||||
|
extension Array where Element == Notification {
|
||||||
|
func consolidated(selectedType: Notification.NotificationType?) -> [ConsolidatedNotification] {
|
||||||
|
Dictionary(grouping: self) { $0.consolidationId(selectedType: selectedType) }
|
||||||
|
.values
|
||||||
|
.compactMap { notifications in
|
||||||
|
guard let notification = notifications.first,
|
||||||
|
let supportedType = notification.supportedType
|
||||||
|
else { return nil }
|
||||||
|
|
||||||
|
return ConsolidatedNotification(notifications: notifications,
|
||||||
|
type: supportedType,
|
||||||
|
createdAt: notification.createdAt,
|
||||||
|
accounts: notifications.map(\.account),
|
||||||
|
status: notification.status)
|
||||||
|
}
|
||||||
|
.sorted {
|
||||||
|
$0.createdAt > $1.createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
//
|
||||||
|
// NotificationExt.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Jérôme Danthinne on 31/01/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Models
|
||||||
|
|
||||||
|
extension Notification {
|
||||||
|
func consolidationId(selectedType: Models.Notification.NotificationType?) -> String? {
|
||||||
|
guard let supportedType else { return nil }
|
||||||
|
|
||||||
|
switch supportedType {
|
||||||
|
case .follow where selectedType != .follow:
|
||||||
|
// Always group followers, so use the type to group
|
||||||
|
return supportedType.rawValue
|
||||||
|
case .reblog, .favourite:
|
||||||
|
// Group boosts and favourites by status, so use the type + the related status id
|
||||||
|
return "\(supportedType.rawValue)-\(status?.id ?? "")"
|
||||||
|
default:
|
||||||
|
// Never group remaining ones, so use the notification id itself
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isConsolidable(selectedType: Models.Notification.NotificationType?) -> Bool {
|
||||||
|
// Notification is consolidable onlt if the consolidation id is not the notication id (unique) itself
|
||||||
|
consolidationId(selectedType: selectedType) != id
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,6 @@ class NotificationsViewModel: ObservableObject {
|
||||||
var client: Client? {
|
var client: Client? {
|
||||||
didSet {
|
didSet {
|
||||||
if oldValue != client {
|
if oldValue != client {
|
||||||
notifications = []
|
|
||||||
consolidatedNotifications = []
|
consolidatedNotifications = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +32,6 @@ class NotificationsViewModel: ObservableObject {
|
||||||
@Published var selectedType: Models.Notification.NotificationType? {
|
@Published var selectedType: Models.Notification.NotificationType? {
|
||||||
didSet {
|
didSet {
|
||||||
if oldValue != selectedType {
|
if oldValue != selectedType {
|
||||||
notifications = []
|
|
||||||
consolidatedNotifications = []
|
consolidatedNotifications = []
|
||||||
Task {
|
Task {
|
||||||
await fetchNotifications()
|
await fetchNotifications()
|
||||||
|
@ -51,7 +49,6 @@ class NotificationsViewModel: ObservableObject {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private var notifications: [Models.Notification] = []
|
|
||||||
private var consolidatedNotifications: [ConsolidatedNotification] = []
|
private var consolidatedNotifications: [ConsolidatedNotification] = []
|
||||||
|
|
||||||
func fetchNotifications() async {
|
func fetchNotifications() async {
|
||||||
|
@ -64,7 +61,6 @@ class NotificationsViewModel: ObservableObject {
|
||||||
try await client.get(endpoint: Notifications.notifications(sinceId: nil,
|
try await client.get(endpoint: Notifications.notifications(sinceId: nil,
|
||||||
maxId: nil,
|
maxId: nil,
|
||||||
types: queryTypes))
|
types: queryTypes))
|
||||||
self.notifications = notifications
|
|
||||||
consolidatedNotifications = notifications.consolidated(selectedType: selectedType)
|
consolidatedNotifications = notifications.consolidated(selectedType: selectedType)
|
||||||
nextPageState = notifications.count < 15 ? .none : .hasNextPage
|
nextPageState = notifications.count < 15 ? .none : .hasNextPage
|
||||||
} else if let first = consolidatedNotifications.first {
|
} else if let first = consolidatedNotifications.first {
|
||||||
|
@ -76,7 +72,6 @@ class NotificationsViewModel: ObservableObject {
|
||||||
newNotifications = newNotifications.filter { notification in
|
newNotifications = newNotifications.filter { notification in
|
||||||
!consolidatedNotifications.contains(where: { $0.id == notification.id })
|
!consolidatedNotifications.contains(where: { $0.id == notification.id })
|
||||||
}
|
}
|
||||||
notifications.append(contentsOf: newNotifications)
|
|
||||||
consolidatedNotifications.insert(
|
consolidatedNotifications.insert(
|
||||||
contentsOf: newNotifications.consolidated(selectedType: selectedType),
|
contentsOf: newNotifications.consolidated(selectedType: selectedType),
|
||||||
at: 0
|
at: 0
|
||||||
|
@ -101,7 +96,6 @@ class NotificationsViewModel: ObservableObject {
|
||||||
maxId: lastId,
|
maxId: lastId,
|
||||||
types: queryTypes))
|
types: queryTypes))
|
||||||
consolidatedNotifications.append(contentsOf: newNotifications.consolidated(selectedType: selectedType))
|
consolidatedNotifications.append(contentsOf: newNotifications.consolidated(selectedType: selectedType))
|
||||||
notifications.append(contentsOf: newNotifications)
|
|
||||||
state = .display(notifications: consolidatedNotifications, nextPageState: newNotifications.count < 15 ? .none : .hasNextPage)
|
state = .display(notifications: consolidatedNotifications, nextPageState: newNotifications.count < 15 ? .none : .hasNextPage)
|
||||||
} catch {
|
} catch {
|
||||||
state = .error(error: error)
|
state = .error(error: error)
|
||||||
|
@ -117,16 +111,29 @@ class NotificationsViewModel: ObservableObject {
|
||||||
|
|
||||||
func handleEvent(event: any StreamEvent) {
|
func handleEvent(event: any StreamEvent) {
|
||||||
Task {
|
Task {
|
||||||
|
// Check if the event is a notification,
|
||||||
|
// if it is not already in the list,
|
||||||
|
// and if it can be shown (no selected type or the same as the received notification type)
|
||||||
if let event = event as? StreamEventNotification,
|
if let event = event as? StreamEventNotification,
|
||||||
!consolidatedNotifications.contains(where: { $0.id == event.notification.id })
|
!consolidatedNotifications.flatMap(\.notificationIds).contains(event.notification.id),
|
||||||
|
selectedType == nil || selectedType?.rawValue == event.notification.type
|
||||||
{
|
{
|
||||||
if let selectedType, event.notification.type == selectedType.rawValue {
|
if event.notification.isConsolidable(selectedType: selectedType) {
|
||||||
notifications.insert(event.notification, at: 0)
|
// If the notification type can be consolidated, try to consolidate with the latest row
|
||||||
consolidatedNotifications = notifications.consolidated(selectedType: selectedType)
|
let latestConsolidatedNotification = consolidatedNotifications.removeFirst()
|
||||||
} else if selectedType == nil {
|
consolidatedNotifications.insert(
|
||||||
notifications.insert(event.notification, at: 0)
|
contentsOf: ([event.notification] + latestConsolidatedNotification.notifications)
|
||||||
consolidatedNotifications = notifications.consolidated(selectedType: selectedType)
|
.consolidated(selectedType: selectedType),
|
||||||
|
at: 0
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Otherwise, just insert the new notification
|
||||||
|
consolidatedNotifications.insert(
|
||||||
|
contentsOf: [event.notification].consolidated(selectedType: selectedType),
|
||||||
|
at: 0
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
withAnimation {
|
withAnimation {
|
||||||
state = .display(notifications: consolidatedNotifications, nextPageState: .hasNextPage)
|
state = .display(notifications: consolidatedNotifications, nextPageState: .hasNextPage)
|
||||||
}
|
}
|
||||||
|
@ -134,66 +141,3 @@ class NotificationsViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConsolidatedNotification: Identifiable {
|
|
||||||
let notificationIds: [String]
|
|
||||||
let type: Models.Notification.NotificationType
|
|
||||||
let createdAt: ServerDate
|
|
||||||
let accounts: [Account]
|
|
||||||
let status: Status?
|
|
||||||
|
|
||||||
var id: String? { notificationIds.first }
|
|
||||||
|
|
||||||
static func placeholder() -> ConsolidatedNotification {
|
|
||||||
.init(notificationIds: [UUID().uuidString],
|
|
||||||
type: .favourite,
|
|
||||||
createdAt: "2022-12-16T10:20:54.000Z",
|
|
||||||
accounts: [.placeholder()],
|
|
||||||
status: .placeholder())
|
|
||||||
}
|
|
||||||
|
|
||||||
static func placeholders() -> [ConsolidatedNotification] {
|
|
||||||
[.placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Array where Element == Models.Notification {
|
|
||||||
func consolidated(selectedType: Models.Notification.NotificationType?) -> [ConsolidatedNotification] {
|
|
||||||
Dictionary(grouping: self) { notification -> String? in
|
|
||||||
guard let supportedType = notification.supportedType else { return nil }
|
|
||||||
|
|
||||||
switch supportedType {
|
|
||||||
case .follow where selectedType != .follow:
|
|
||||||
// Always group followers
|
|
||||||
return supportedType.rawValue
|
|
||||||
case .reblog, .favourite:
|
|
||||||
// Group boosts and favourites by status
|
|
||||||
return "\(supportedType.rawValue)-\(notification.status?.id ?? "")"
|
|
||||||
default:
|
|
||||||
// Never group remaining ones
|
|
||||||
return notification.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.values
|
|
||||||
.compactMap { notifications in
|
|
||||||
guard let notification = notifications.first,
|
|
||||||
let supportedType = notification.supportedType
|
|
||||||
else { return nil }
|
|
||||||
|
|
||||||
return ConsolidatedNotification(notificationIds: notifications.map(\.id),
|
|
||||||
type: supportedType,
|
|
||||||
createdAt: notification.createdAt,
|
|
||||||
accounts: notifications.map(\.account),
|
|
||||||
status: notification.status)
|
|
||||||
}
|
|
||||||
.sorted {
|
|
||||||
$0.createdAt > $1.createdAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Array where Element == ConsolidatedNotification {
|
|
||||||
var notificationCount: Int {
|
|
||||||
reduce(0) { $0 + ($1.accounts.isEmpty ? 1 : $1.accounts.count) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue