diff --git a/IceCubesApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/IceCubesApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8afd403c..5b4efd2c 100644 --- a/IceCubesApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/IceCubesApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -3,10 +3,10 @@ { "identity" : "bodega", "kind" : "remoteSourceControl", - "location" : "https://github.com/Dimillian/Bodega", + "location" : "https://github.com/mergesort/Bodega", "state" : { - "branch" : "main", - "revision" : "a144ed8afdd760b65b6b9a136ba8bb75cd19387e" + "revision" : "bfd8871e9c2590d31b200e54c75428a71483afdf", + "version" : "2.1.3" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/divadretlaw/EmojiText", "state" : { - "revision" : "415112e5f14619be0fdddd9dc6594bd76702927c", - "version" : "4.0.3" + "revision" : "174a7bc7bd75650ad1acb5679dbb754296093de0", + "version" : "4.0.0" } }, { @@ -68,8 +68,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/kean/Nuke", "state" : { - "revision" : "2efd206503e99dd3a88a4dc433a76f98ce8cc198", - "version" : "12.7.1" + "revision" : "311016d972aa751ae8ab0cd5897422ebe7db0501", + "version" : "12.7.3" } }, { @@ -129,10 +129,10 @@ { "identity" : "swiftui-introspect", "kind" : "remoteSourceControl", - "location" : "https://github.com/Dimillian/swiftui-introspect", + "location" : "https://github.com/siteline/swiftui-introspect", "state" : { - "branch" : "main", - "revision" : "e5d36b00e6e437b552aa76ed6d1eca71d6fd8f8b" + "revision" : "668a65735751432b640260c56dfa621cec568368", + "version" : "1.2.0" } } ], diff --git a/IceCubesApp/Resources/Localization/Localizable.xcstrings b/IceCubesApp/Resources/Localization/Localizable.xcstrings index cb5209bd..5331b93e 100644 --- a/IceCubesApp/Resources/Localization/Localizable.xcstrings +++ b/IceCubesApp/Resources/Localization/Localizable.xcstrings @@ -332,6 +332,12 @@ "state" : "translated", "value" : "%@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } } } }, @@ -714,6 +720,12 @@ "state" : "translated", "value" : "%@ Mastodon'da yayınlandı" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ 已在 Mastodon 发布" + } } } }, @@ -8935,6 +8947,12 @@ "state" : "translated", "value" : "Hesap" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Account" + } } } }, @@ -20503,6 +20521,12 @@ "state" : "translated", "value" : "Mastodon'a gönderim yapılırken bir hata oluştu, lütfen tekrar deneyin." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "在向 Mastodon 发表嘟文时出错,请重试。" + } } } }, @@ -20651,6 +20675,12 @@ "state" : "translated", "value" : "Uygulama Hesabı" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "AppAccount" + } } } }, @@ -20713,6 +20743,12 @@ "state" : "translated", "value" : "Gönderi oluştur" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "撰写一篇嘟文" + } } } }, @@ -20741,6 +20777,12 @@ "state" : "translated", "value" : "Mastodon'a bir gönderi yazın" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "向 Mastodon 撰写一篇嘟文" + } } } }, @@ -20769,6 +20811,12 @@ "state" : "translated", "value" : "Mastodon'a gönderilecek gönderinin içeriği" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "发布到 Mastodon 的嘟文内容" + } } } }, @@ -22338,6 +22386,12 @@ "state" : "translated", "value" : "DeepL'e ulaşılamadı!\nAPI Anahtarı doğru mu?" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无法连接 DeepL!\n你的 API 密钥是正确的吗?" + } } } }, @@ -31163,6 +31217,12 @@ "state" : "translated", "value" : "Yalnızca Takipçiler" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "仅关注者" + } } } }, @@ -31294,6 +31354,12 @@ "state" : "translated", "value" : "Örnek" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "服务器" + } } } }, @@ -32858,6 +32924,12 @@ "state" : "translated", "value" : "Ancak yine de örneğinizin çeviri hizmeti için bir yedek olarak kullanılabilir." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "它仍可用作为你所处服务器的翻译服务的备用。" + } } } }, @@ -32881,6 +32953,12 @@ "state" : "translated", "value" : "Anahtar" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Key" + } } } }, @@ -35469,7 +35547,7 @@ }, "zh-Hans" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "设置 ..." } }, @@ -41022,6 +41100,12 @@ "state" : "translated", "value" : "Resim ile bir durum gönder" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "发布带图片的状态" + } } } }, @@ -41176,6 +41260,12 @@ "state" : "translated", "value" : "Gönderi görünürlüğü" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "嘟文可见度" + } } } }, @@ -41204,6 +41294,12 @@ "state" : "translated", "value" : "Özel" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "具体的人" + } } } }, @@ -41300,6 +41396,12 @@ "state" : "translated", "value" : "Herkese Açık" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "公开" + } } } }, @@ -41328,6 +41430,12 @@ "state" : "translated", "value" : "Oldukça Açık" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "悄悄公开" + } } } }, @@ -41864,6 +41972,12 @@ "state" : "translated", "value" : "Post gönder" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "发送嘟文" + } } } }, @@ -41892,6 +42006,12 @@ "state" : "translated", "value" : "Ice cubes ile Mastodon'a kısa mesaj gönder" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "使用 Ice Cubes 向 Mastodon 发布文本嘟文" + } } } }, @@ -41920,6 +42040,12 @@ "state" : "translated", "value" : "Gönderiyi Mastodon'a gönder" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "发送嘟文至 Mastodon" + } } } }, @@ -61537,7 +61663,7 @@ }, "zh-Hans" : { "stringUnit" : { - "state" : "needs_review", + "state" : "translated", "value" : "该功能需要 DeepL API 密钥" } }, @@ -69630,7 +69756,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Your settings require alt text on all media before posting" + "value" : "你的设置要求在发布前为所有媒体添加描述文本" } }, "zh-Hant" : { @@ -76981,8 +77107,8 @@ }, "zh-Hans" : { "stringUnit" : { - "state" : "needs_review", - "value" : "Profile (%@)" + "state" : "translated", + "value" : "个人主页 (%@)" } }, "zh-Hant" : { @@ -77845,6 +77971,12 @@ "state" : "translated", "value" : "DeepL API Anahtarı hala saklanıyor!" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "DeepL API 密钥已存储!" + } } } }, @@ -77874,6 +78006,12 @@ "state" : "translated", "value" : "Örneğinizin Çeviri Hizmetine ulaşılamadı!" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无法连接你的服务器的翻译服务!" + } } } }, @@ -81771,6 +81909,12 @@ "state" : "translated", "value" : "Zaman Çizelgesi Filtresi" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "TimelineFilter" + } } } }, @@ -81800,6 +81944,12 @@ "state" : "translated", "value" : "Çeviri servisi" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "翻译服务" + } } } }, @@ -82255,6 +82405,12 @@ "state" : "translated", "value" : "Mastodon için bir gönderi oluşturmak için Ice Cubes kullanın" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "使用 Ice Cubes 向 Mastodon 发布嘟文" + } } } }, @@ -82283,6 +82439,12 @@ "state" : "translated", "value" : "Mastodon'da görsel içeren bir gönderi oluşturmak için Ice Cubes kullanın" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "使用 Ice Cubes 向 Mastodon 发布带有图片的嘟文" + } } } }, @@ -82369,6 +82531,12 @@ "state" : "translated", "value" : "Görünürlük" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "可见度" + } } } }, @@ -82397,9 +82565,15 @@ "state" : "translated", "value" : "Gönderinizin görünürlüğü" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "嘟文可见度" + } } } } }, "version" : "1.0" -} +} \ No newline at end of file diff --git a/Packages/Account/Sources/Account/AccountDetailView.swift b/Packages/Account/Sources/Account/AccountDetailView.swift index 0ed1bc69..9bf02fbd 100644 --- a/Packages/Account/Sources/Account/AccountDetailView.swift +++ b/Packages/Account/Sources/Account/AccountDetailView.swift @@ -83,7 +83,7 @@ public struct AccountDetailView: View { client: client, routerPath: routerPath) } - .environment(\.defaultMinListRowHeight, 1) + .environment(\.defaultMinListRowHeight, 0) .listStyle(.plain) #if !os(visionOS) .scrollContentBackground(.hidden) diff --git a/Packages/Account/Sources/Account/Edit/EditAccountViewModel.swift b/Packages/Account/Sources/Account/Edit/EditAccountViewModel.swift index 221b0b31..50f10491 100644 --- a/Packages/Account/Sources/Account/Edit/EditAccountViewModel.swift +++ b/Packages/Account/Sources/Account/Edit/EditAccountViewModel.swift @@ -51,17 +51,19 @@ import SwiftUI didSet { if let item = mediaPickers.first { Task { - if let data = await getItemImageData(item: item) { if isChangingAvatar { - _ = await uploadAvatar(data: data) + if let data = await getItemImageData(item: item, for: .avatar) { + _ = await uploadAvatar(data: data) + } + isChangingAvatar = false } else if isChangingHeader { - _ = await uploadHeader(data: data) + if let data = await getItemImageData(item: item, for: .header) { + _ = await uploadHeader(data: data) + } + isChangingHeader = false } await fetchAccount() - isChangingAvatar = false - isChangingHeader = false mediaPickers = [] - } } } } @@ -140,16 +142,48 @@ import SwiftUI } } - private func getItemImageData(item: PhotosPickerItem) async -> Data? { + private func getItemImageData(item: PhotosPickerItem, for type: ItemType) async -> Data? { guard let imageFile = try? await item.loadTransferable(type: StatusEditor.ImageFileTranseferable.self) else { return nil } let compressor = StatusEditor.Compressor() guard let compressedData = await compressor.compressImageFrom(url: imageFile.url), let image = UIImage(data: compressedData), - let uploadData = try? await compressor.compressImageForUpload(image) - else { return nil } + let uploadData = try? await compressor.compressImageForUpload( + image, + maxSize: 2 * 1024 * 1024, // 2MB + maxHeight: type.maxHeight, + maxWidth: type.maxWidth + ) + else { + return nil + } return uploadData } } + +extension EditAccountViewModel { + private enum ItemType { + case avatar + case header + + var maxHeight: CGFloat { + switch self { + case .avatar: + 400 + case .header: + 500 + } + } + + var maxWidth: CGFloat { + switch self { + case .avatar: + 400 + case .header: + 1500 + } + } + } +} diff --git a/Packages/DesignSystem/Package.swift b/Packages/DesignSystem/Package.swift index 4a44b8e6..ae4574cd 100644 --- a/Packages/DesignSystem/Package.swift +++ b/Packages/DesignSystem/Package.swift @@ -19,8 +19,8 @@ let package = Package( dependencies: [ .package(name: "Models", path: "../Models"), .package(name: "Env", path: "../Env"), - .package(url: "https://github.com/kean/Nuke", from: "12.4.0"), - .package(url: "https://github.com/divadretlaw/EmojiText", from: "4.0.0"), + .package(url: "https://github.com/kean/Nuke", exact: "12.7.3"), + .package(url: "https://github.com/divadretlaw/EmojiText", exact: "4.0.0"), ], targets: [ .target( diff --git a/Packages/StatusKit/Sources/StatusKit/Editor/Components/Compressor.swift b/Packages/StatusKit/Sources/StatusKit/Editor/Components/Compressor.swift index 460804a8..d4029a22 100644 --- a/Packages/StatusKit/Sources/StatusKit/Editor/Components/Compressor.swift +++ b/Packages/StatusKit/Sources/StatusKit/Editor/Components/Compressor.swift @@ -57,30 +57,45 @@ public extension StatusEditor { } } - public func compressImageForUpload(_ image: UIImage) async throws -> Data { + public func compressImageForUpload( + _ image: UIImage, + maxSize: Int = 10 * 1024 * 1024, + maxHeight: Double = 5000, + maxWidth: Double = 5000 + ) async throws -> Data { var image = image - if image.size.height > 5000 || image.size.width > 5000 { - image = image.resized(to: .init(width: image.size.width / 4, - height: image.size.height / 4)) + + if image.size.height > maxHeight || image.size.width > maxWidth { + let heightFactor = image.size.height / maxHeight + let widthFactor = image.size.width / maxWidth + let maxFactor = max(heightFactor, widthFactor) + + image = image.resized(to: .init(width: image.size.width / maxFactor, + height: image.size.height / maxFactor)) } guard var imageData = image.jpegData(compressionQuality: 0.8) else { throw CompressorError.noData } - let maxSize = 10 * 1024 * 1024 - + var compressionQualityFactor: CGFloat = 0.8 if imageData.count > maxSize { - while imageData.count > maxSize { + while imageData.count > maxSize && compressionQualityFactor >= 0 { guard let compressedImage = UIImage(data: imageData), - let compressedData = compressedImage.jpegData(compressionQuality: 0.8) + let compressedData = compressedImage.jpegData(compressionQuality: compressionQualityFactor) else { throw CompressorError.noData } + imageData = compressedData + compressionQualityFactor -= 0.1 } } + if imageData.count > maxSize && compressionQualityFactor <= 0 { + throw CompressorError.noData + } + return imageData } diff --git a/Packages/Timeline/Package.swift b/Packages/Timeline/Package.swift index dbac639c..b0c48db0 100644 --- a/Packages/Timeline/Package.swift +++ b/Packages/Timeline/Package.swift @@ -22,8 +22,8 @@ let package = Package( .package(name: "Env", path: "../Env"), .package(name: "StatusKit", path: "../StatusKit"), .package(name: "DesignSystem", path: "../DesignSystem"), - .package(url: "https://github.com/Dimillian/swiftui-introspect", branch: "main"), - .package(url: "https://github.com/Dimillian/Bodega", branch: "main"), + .package(url: "https://github.com/siteline/swiftui-introspect", exact: "1.2.0"), + .package(url: "https://github.com/mergesort/Bodega", exact: "2.1.3"), ], targets: [ .target(