Something went wrong. Try again.
A slopfork of Sam Henri Gold's Twitter client, reskinned like Tweetie 2 / Twitter for iPhone circa 2012. Personal use.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127import BackgroundTasksimport ComposableArchitectureimport Foundationimport Modelsimport SwiftUIimport TwitterClientimport TwitterUIimport UserNotifications
enum NotificationCheck { static let taskID = "gold.samhenri.Twitter.notifications" private static let lastUnreadCountKey = "lastNotifiedUnreadCount" private static let individualLimit = 3
static var isOn: Bool { UserDefaults.standard.bool(forKey: RetroSettings.notifiesKey) }
static func schedule() { guard isOn else { BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: taskID) return } let request = BGAppRefreshTaskRequest(identifier: taskID) request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) Task { try? await BGTaskScheduler.shared.submitTaskRequest(request) } }
static func run() async { schedule() guard isOn else { return } @Dependency(TwitterClient.self) var client guard (try? await client.restoreSession()) != nil, let unread = try? await client.unreadNotificationCount() else { return }
let defaults = UserDefaults.standard let lastUnread = defaults.integer(forKey: lastUnreadCountKey) defaults.set(unread, forKey: lastUnreadCountKey) try? await UNUserNotificationCenter.current().setBadgeCount(unread) guard unread > lastUnread else { return }
let arrived = unread - lastUnread let page = try? await client.feed(.notifications(.all), nil) let newest = (page?.items ?? []).compactMap { item -> TwitterNotification? in guard case .notification(let notification) = item, notification.isUnread else { return nil } return notification } if arrived <= individualLimit, !newest.isEmpty { for notification in newest.prefix(arrived) { await post(id: notification.id, body: notification.message) } } else { await post( id: UUID().uuidString, body: String(localized: "You have \(arrived) new notifications.") ) } }
static func appBecameActive() { Task { try? await UNUserNotificationCenter.current().setBadgeCount(0) } }
private static func post(id: String, body: String) async { let content = UNMutableNotificationContent() content.body = body content.sound = .default try? await UNUserNotificationCenter.current().add( UNNotificationRequest(identifier: id, content: content, trigger: nil)) }}
@MainActorfinal class NotificationTapDelegate: NSObject, @preconcurrency UNUserNotificationCenterDelegate { static let shared = NotificationTapDelegate()
static func install() { UNUserNotificationCenter.current().delegate = shared }
func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { NotificationTap.record() completionHandler() }
func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void ) { completionHandler([.banner, .list, .sound]) }}
extension View { func schedulesNotificationChecks() -> some View { modifier(SchedulesNotificationChecks()) }}
private struct SchedulesNotificationChecks: ViewModifier { @Environment(\.scenePhase) private var scenePhase
func body(content: Content) -> some View { content .onChange(of: scenePhase) { _, phase in switch phase { case .background: NotificationCheck.schedule() case .active: NotificationCheck.appBecameActive() default: break } } .task { #if DEBUG let arguments = ProcessInfo.processInfo.arguments if let index = arguments.firstIndex(of: "--check-notifications-in"), index + 1 < arguments.count, let seconds = Double(arguments[index + 1]) { try? await Task.sleep(for: .seconds(seconds)) await NotificationCheck.run() } #endif } }}