Revert "Merge remote-tracking branch 'upstream/main' into Improve-notification-localization"

This reverts commit f435d1e7ed, reversing
changes made to dae2aee676.
This commit is contained in:
Minseo Lee
2024-09-25 10:32:03 +09:00
parent f435d1e7ed
commit c93ac19048
372 changed files with 4172 additions and 4839 deletions
+12 -61
View File
@@ -2,80 +2,46 @@ import UserNotifications
import UIKit
let APP_GROUP = "group.app.bsky"
typealias ContentHandler = (UNNotificationContent) -> Void
// This extension allows us to do some processing of the received notification
// data before displaying the notification to the user. In our use case, there
// are a few particular things that we want to do:
//
// - Determine whether we should play a sound for the notification
// - Download and display any images for the notification
// - Update the badge count accordingly
//
// The extension may or may not create a new process to handle a notification.
// It is also possible that multiple notifications will be processed by the
// same instance of `NotificationService`, though these will happen in
// parallel.
//
// Because multiple instances of `NotificationService` may exist, we should
// be careful in accessing preferences that will be mutated _by the
// extension itself_. For example, we should not worry about `playChatSound`
// changing, since we never mutate that value within the extension itself.
// However, since we mutate `badgeCount` frequently, we should ensure that
// these updates always run sync with each other and that the have access
// to the most recent values.
class NotificationService: UNNotificationServiceExtension {
private var contentHandler: ContentHandler?
private var bestAttempt: UNMutableNotificationContent?
var prefs = UserDefaults(suiteName: APP_GROUP)
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
guard let bestAttempt = NSEUtil.createCopy(request.content),
guard let bestAttempt = createCopy(request.content),
let reason = request.content.userInfo["reason"] as? String
else {
contentHandler(request.content)
return
}
self.bestAttempt = bestAttempt
if reason == "chat-message" {
mutateWithChatMessage(bestAttempt)
} else {
mutateWithBadge(bestAttempt)
}
// Any image downloading (or other network tasks) should be handled at the end
// of this block. Otherwise, if there is a timeout and serviceExtensionTimeWillExpire
// gets called, we might not have all the needed mutations completed in time.
contentHandler(bestAttempt)
}
override func serviceExtensionTimeWillExpire() {
guard let contentHandler = self.contentHandler,
let bestAttempt = self.bestAttempt else {
return
}
contentHandler(bestAttempt)
// If for some reason the alloted time expires, we don't actually want to display a notification
}
// MARK: Mutations
func createCopy(_ content: UNNotificationContent) -> UNMutableNotificationContent? {
return content.mutableCopy() as? UNMutableNotificationContent
}
func mutateWithBadge(_ content: UNMutableNotificationContent) {
NSEUtil.shared.prefsQueue.sync {
var count = NSEUtil.shared.prefs?.integer(forKey: "badgeCount") ?? 0
count += 1
var count = prefs?.integer(forKey: "badgeCount") ?? 0
count += 1
// Set the new badge number for the notification, then store that value for using later
content.badge = NSNumber(value: count)
NSEUtil.shared.prefs?.setValue(count, forKey: "badgeCount")
}
// Set the new badge number for the notification, then store that value for using later
content.badge = NSNumber(value: count)
prefs?.setValue(count, forKey: "badgeCount")
}
func mutateWithChatMessage(_ content: UNMutableNotificationContent) {
if NSEUtil.shared.prefs?.bool(forKey: "playSoundChat") == true {
if self.prefs?.bool(forKey: "playSoundChat") == true {
mutateWithDmSound(content)
}
}
@@ -88,18 +54,3 @@ class NotificationService: UNNotificationServiceExtension {
content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "dm.aiff"))
}
}
// NSEUtil's purpose is to create a shared instance of `UserDefaults` across
// `NotificationService` instances. It also includes a queue so that we can process
// updates to `UserDefaults` in parallel.
private class NSEUtil {
static let shared = NSEUtil()
var prefs = UserDefaults(suiteName: APP_GROUP)
var prefsQueue = DispatchQueue(label: "NSEPrefsQueue")
static func createCopy(_ content: UNNotificationContent) -> UNMutableNotificationContent? {
return content.mutableCopy() as? UNMutableNotificationContent
}
}
+1 -3
View File
@@ -16,8 +16,6 @@
<integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>10</integer>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
<key>NSExtensionPointIdentifier</key>
@@ -40,4 +38,4 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
</dict>
</plist>
</plist>
@@ -5,6 +5,7 @@ class ShareViewController: UIViewController {
// scheme.
let appScheme = Bundle.main.object(forInfoDictionaryKey: "MainAppScheme") as? String ?? "bluesky"
//
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
@@ -23,8 +24,6 @@ class ShareViewController: UIViewController {
await self.handleUrl(item: firstAttachment)
} else if firstAttachment.hasItemConformingToTypeIdentifier("public.image") {
await self.handleImages(items: attachments)
} else if firstAttachment.hasItemConformingToTypeIdentifier("public.video") {
await self.handleVideos(items: attachments)
} else {
self.completeRequest()
}
@@ -32,23 +31,31 @@ class ShareViewController: UIViewController {
}
private func handleText(item: NSItemProvider) async {
if let data = try? await item.loadItem(forTypeIdentifier: "public.text") as? String {
if let encoded = data.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed),
let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)") {
_ = self.openURL(url)
do {
if let data = try await item.loadItem(forTypeIdentifier: "public.text") as? String {
if let encoded = data.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed),
let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)") {
_ = self.openURL(url)
}
}
self.completeRequest()
} catch {
self.completeRequest()
}
self.completeRequest()
}
private func handleUrl(item: NSItemProvider) async {
if let data = try? await item.loadItem(forTypeIdentifier: "public.url") as? URL {
if let encoded = data.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed),
let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)") {
_ = self.openURL(url)
do {
if let data = try await item.loadItem(forTypeIdentifier: "public.url") as? URL {
if let encoded = data.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed),
let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)") {
_ = self.openURL(url)
}
}
self.completeRequest()
} catch {
self.completeRequest()
}
self.completeRequest()
}
private func handleImages(items: [NSItemProvider]) async {
@@ -98,25 +105,6 @@ class ShareViewController: UIViewController {
self.completeRequest()
}
private func handleVideos(items: [NSItemProvider]) async {
let firstItem = items.first
if let dataUri = try? await firstItem?.loadItem(forTypeIdentifier: "public.video") as? URL {
let ext = String(dataUri.lastPathComponent.split(separator: ".").last ?? "mp4")
if let tempUrl = getTempUrl(ext: ext) {
let data = try? Data(contentsOf: dataUri)
try? data?.write(to: tempUrl)
if let encoded = dataUri.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed),
let url = URL(string: "\(self.appScheme)://intent/compose?videoUri=\(encoded)") {
_ = self.openURL(url)
}
}
}
self.completeRequest()
}
private func saveImageWithInfo(_ image: UIImage?) -> String? {
guard let image = image else {
return nil
@@ -126,26 +114,27 @@ class ShareViewController: UIViewController {
// Saving this file to the bundle group's directory lets us access it from
// inside of the app. Otherwise, we wouldn't have access even though the
// extension does.
if let tempUrl = getTempUrl(ext: "jpeg"),
let jpegData = image.jpegData(compressionQuality: 1) {
try jpegData.write(to: tempUrl)
return "\(tempUrl.absoluteString)|\(image.size.width)|\(image.size.height)"
if let dir = FileManager()
.containerURL(
forSecurityApplicationGroupIdentifier: "group.app.bsky") {
let filePath = "\(dir.absoluteString)\(ProcessInfo.processInfo.globallyUniqueString).jpeg"
if let newUri = URL(string: filePath),
let jpegData = image.jpegData(compressionQuality: 1) {
try jpegData.write(to: newUri)
return "\(newUri.absoluteString)|\(image.size.width)|\(image.size.height)"
}
}
} catch {}
return nil
return nil
} catch {
return nil
}
}
private func completeRequest() {
self.extensionContext?.completeRequest(returningItems: nil)
}
private func getTempUrl(ext: String) -> URL? {
if let dir = FileManager().containerURL(forSecurityApplicationGroupIdentifier: "group.app.bsky") {
return URL(string: "\(dir.absoluteString)\(ProcessInfo.processInfo.globallyUniqueString).\(ext)")!
}
return nil
}
@objc func openURL(_ url: URL) -> Bool {
var responder: UIResponder? = self
while responder != nil {