mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-22 16:31:00 +00:00
Swiftformat
This commit is contained in:
parent
584a0d0432
commit
8a3c971402
120 changed files with 573 additions and 579 deletions
|
@ -155,8 +155,8 @@ struct ActivityView: UIViewControllerRepresentable {
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeUIViewController(context _: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
|
func makeUIViewController(context _: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
|
||||||
return UIActivityViewController(activityItems: [image, LinkDelegate(image: image, status: status)],
|
UIActivityViewController(activityItems: [image, LinkDelegate(image: image, status: status)],
|
||||||
applicationActivities: nil)
|
applicationActivities: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIViewController(_: UIActivityViewController, context _: UIViewControllerRepresentableContext<ActivityView>) {}
|
func updateUIViewController(_: UIActivityViewController, context _: UIViewControllerRepresentableContext<ActivityView>) {}
|
||||||
|
|
|
@ -100,7 +100,7 @@ struct IceCubesApp: App {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func badgeFor(tab: Tab) -> Int {
|
private func badgeFor(tab: Tab) -> Int {
|
||||||
if tab == .notifications && selectedTab != tab,
|
if tab == .notifications, selectedTab != tab,
|
||||||
let token = appAccountsManager.currentAccount.oauthToken
|
let token = appAccountsManager.currentAccount.oauthToken
|
||||||
{
|
{
|
||||||
return watcher.unreadNotificationsCount + userPreferences.getNotificationsCount(for: token)
|
return watcher.unreadNotificationsCount + userPreferences.getNotificationsCount(for: token)
|
||||||
|
|
|
@ -13,7 +13,7 @@ struct QuickLookPreview: UIViewControllerRepresentable {
|
||||||
let urls: [URL]
|
let urls: [URL]
|
||||||
|
|
||||||
func makeUIViewController(context _: Context) -> UIViewController {
|
func makeUIViewController(context _: Context) -> UIViewController {
|
||||||
return AppQLPreviewController(selectedURL: selectedURL, urls: urls)
|
AppQLPreviewController(selectedURL: selectedURL, urls: urls)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIViewController(
|
func updateUIViewController(
|
||||||
|
@ -53,11 +53,11 @@ class AppQLPreviewController: UIViewController {
|
||||||
|
|
||||||
extension AppQLPreviewController: QLPreviewControllerDataSource {
|
extension AppQLPreviewController: QLPreviewControllerDataSource {
|
||||||
nonisolated func numberOfPreviewItems(in _: QLPreviewController) -> Int {
|
nonisolated func numberOfPreviewItems(in _: QLPreviewController) -> Int {
|
||||||
return urls.count
|
urls.count
|
||||||
}
|
}
|
||||||
|
|
||||||
nonisolated func previewController(_: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
|
nonisolated func previewController(_: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
|
||||||
return urls[index] as QLPreviewItem
|
urls[index] as QLPreviewItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ extension AppQLPreviewController: QLPreviewControllerDelegate {
|
||||||
|
|
||||||
struct TransparentBackground: UIViewControllerRepresentable {
|
struct TransparentBackground: UIViewControllerRepresentable {
|
||||||
public func makeUIViewController(context _: Context) -> UIViewController {
|
public func makeUIViewController(context _: Context) -> UIViewController {
|
||||||
return TransparentController()
|
TransparentController()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateUIViewController(_: UIViewController, context _: Context) {}
|
public func updateUIViewController(_: UIViewController, context _: Context) {}
|
||||||
|
|
|
@ -51,7 +51,7 @@ private struct SafariRouter: ViewModifier {
|
||||||
}
|
}
|
||||||
.background {
|
.background {
|
||||||
WindowReader { window in
|
WindowReader { window in
|
||||||
self.safariManager.windowScene = window.windowScene
|
safariManager.windowScene = window.windowScene
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ private class InAppSafariManager: NSObject, ObservableObject, SFSafariViewContro
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
func open(_ url: URL) -> OpenURLAction.Result {
|
func open(_ url: URL) -> OpenURLAction.Result {
|
||||||
guard let windowScene = windowScene else { return .systemAction }
|
guard let windowScene else { return .systemAction }
|
||||||
|
|
||||||
window = setupWindow(windowScene: windowScene)
|
window = setupWindow(windowScene: windowScene)
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ private class InAppSafariManager: NSObject, ObservableObject, SFSafariViewContro
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupWindow(windowScene: UIWindowScene) -> UIWindow {
|
func setupWindow(windowScene: UIWindowScene) -> UIWindow {
|
||||||
let window = self.window ?? UIWindow(windowScene: windowScene)
|
let window = window ?? UIWindow(windowScene: windowScene)
|
||||||
|
|
||||||
window.rootViewController = viewController
|
window.rootViewController = viewController
|
||||||
window.makeKeyAndVisible()
|
window.makeKeyAndVisible()
|
||||||
|
|
|
@ -19,7 +19,7 @@ struct SideBarView<Content: View>: View {
|
||||||
@ViewBuilder var content: () -> Content
|
@ViewBuilder var content: () -> Content
|
||||||
|
|
||||||
private func badgeFor(tab: Tab) -> Int {
|
private func badgeFor(tab: Tab) -> Int {
|
||||||
if tab == .notifications && selectedTab != tab,
|
if tab == .notifications, selectedTab != tab,
|
||||||
let token = appAccounts.currentAccount.oauthToken
|
let token = appAccounts.currentAccount.oauthToken
|
||||||
{
|
{
|
||||||
return watcher.unreadNotificationsCount + userPreferences.getNotificationsCount(for: token)
|
return watcher.unreadNotificationsCount + userPreferences.getNotificationsCount(for: token)
|
||||||
|
|
|
@ -29,7 +29,7 @@ struct ExploreTab: View {
|
||||||
AppAccountsSelectorView(routerPath: routerPath)
|
AppAccountsSelectorView(routerPath: routerPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if UIDevice.current.userInterfaceIdiom == .pad && !preferences.showiPadSecondaryColumn {
|
if UIDevice.current.userInterfaceIdiom == .pad, !preferences.showiPadSecondaryColumn {
|
||||||
SecondaryColumnToolbarItem()
|
SecondaryColumnToolbarItem()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,11 +100,11 @@ struct AddAccountView: View {
|
||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
// bare bones preflight for domain validity
|
// bare bones preflight for domain validity
|
||||||
if client.server.contains(".") && client.server.last != "." {
|
if client.server.contains("."), client.server.last != "." {
|
||||||
let instance: Instance = try await client.get(endpoint: Instances.instance)
|
let instance: Instance = try await client.get(endpoint: Instances.instance)
|
||||||
withAnimation {
|
withAnimation {
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
self.instanceName = sanitizedName // clean up the text box, principally to chop off the username if present so it's clear that you might not wind up siging in as the thing in the box
|
instanceName = sanitizedName // clean up the text box, principally to chop off the username if present so it's clear that you might not wind up siging in as the thing in the box
|
||||||
}
|
}
|
||||||
instanceFetchError = nil
|
instanceFetchError = nil
|
||||||
} else {
|
} else {
|
||||||
|
@ -178,7 +178,7 @@ struct AddAccountView: View {
|
||||||
} else {
|
} else {
|
||||||
ForEach(sanitizedName.isEmpty ? instances : instances.filter { $0.name.contains(sanitizedName.lowercased()) }) { instance in
|
ForEach(sanitizedName.isEmpty ? instances : instances.filter { $0.name.contains(sanitizedName.lowercased()) }) { instance in
|
||||||
Button {
|
Button {
|
||||||
self.instanceName = instance.name
|
instanceName = instance.name
|
||||||
} label: {
|
} label: {
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
Text(instance.name)
|
Text(instance.name)
|
||||||
|
|
|
@ -87,12 +87,13 @@ struct ContentSettingsView: View {
|
||||||
Picker("settings.content.default-reply-visibility", selection: $userPreferences.appDefaultReplyVisibility) {
|
Picker("settings.content.default-reply-visibility", selection: $userPreferences.appDefaultReplyVisibility) {
|
||||||
ForEach(Visibility.allCases, id: \.rawValue) { vis in
|
ForEach(Visibility.allCases, id: \.rawValue) { vis in
|
||||||
if UserPreferences.getIntOfVisibility(vis) <=
|
if UserPreferences.getIntOfVisibility(vis) <=
|
||||||
UserPreferences.getIntOfVisibility(userPreferences.postVisibility) {
|
UserPreferences.getIntOfVisibility(userPreferences.postVisibility)
|
||||||
|
{
|
||||||
Text(vis.title).tag(vis)
|
Text(vis.title).tag(vis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: userPreferences.postVisibility) { newValue in
|
.onChange(of: userPreferences.postVisibility) { _ in
|
||||||
userPreferences.conformReplyVisibilityConstraints()
|
userPreferences.conformReplyVisibilityConstraints()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,9 @@ struct IconSelectorView: View {
|
||||||
var appIconName: String {
|
var appIconName: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .primary:
|
case .primary:
|
||||||
return "AppIcon"
|
"AppIcon"
|
||||||
default:
|
default:
|
||||||
return "AppIconAlternate\(rawValue)"
|
"AppIconAlternate\(rawValue)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +120,6 @@ struct IconSelectorView: View {
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
var localized: String {
|
var localized: String {
|
||||||
return NSLocalizedString(self, comment: "")
|
NSLocalizedString(self, comment: "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ struct SettingsTabs: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if UIDevice.current.userInterfaceIdiom == .pad && !preferences.showiPadSecondaryColumn {
|
if UIDevice.current.userInterfaceIdiom == .pad, !preferences.showiPadSecondaryColumn {
|
||||||
SecondaryColumnToolbarItem()
|
SecondaryColumnToolbarItem()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,7 +326,6 @@ struct SettingsTabs: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private func moveTimelineItems(from source: IndexSet, to destination: Int) {
|
private func moveTimelineItems(from source: IndexSet, to destination: Int) {
|
||||||
preferences.remoteLocalTimelines.move(fromOffsets: source, toOffset: destination)
|
preferences.remoteLocalTimelines.move(fromOffsets: source, toOffset: destination)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,30 +19,30 @@ struct SupportAppView: View {
|
||||||
var title: LocalizedStringKey {
|
var title: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .one:
|
case .one:
|
||||||
return "settings.support.one.title"
|
"settings.support.one.title"
|
||||||
case .two:
|
case .two:
|
||||||
return "settings.support.two.title"
|
"settings.support.two.title"
|
||||||
case .three:
|
case .three:
|
||||||
return "settings.support.three.title"
|
"settings.support.three.title"
|
||||||
case .four:
|
case .four:
|
||||||
return "settings.support.four.title"
|
"settings.support.four.title"
|
||||||
case .supporter:
|
case .supporter:
|
||||||
return "settings.support.supporter.title"
|
"settings.support.supporter.title"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var subtitle: LocalizedStringKey {
|
var subtitle: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .one:
|
case .one:
|
||||||
return "settings.support.one.subtitle"
|
"settings.support.one.subtitle"
|
||||||
case .two:
|
case .two:
|
||||||
return "settings.support.two.subtitle"
|
"settings.support.two.subtitle"
|
||||||
case .three:
|
case .three:
|
||||||
return "settings.support.three.subtitle"
|
"settings.support.three.subtitle"
|
||||||
case .four:
|
case .four:
|
||||||
return "settings.support.four.subtitle"
|
"settings.support.four.subtitle"
|
||||||
case .supporter:
|
case .supporter:
|
||||||
return "settings.support.supporter.subtitle"
|
"settings.support.supporter.subtitle"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,8 +103,8 @@ struct SupportAppView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fetchStoreProducts() {
|
private func fetchStoreProducts() {
|
||||||
Purchases.shared.getProducts(Tip.allCases.map { $0.productId }) { products in
|
Purchases.shared.getProducts(Tip.allCases.map(\.productId)) { products in
|
||||||
self.subscription = products.first(where: { $0.productIdentifier == Tip.supporter.productId })
|
subscription = products.first(where: { $0.productIdentifier == Tip.supporter.productId })
|
||||||
self.products = products.filter { $0.productIdentifier != Tip.supporter.productId }.sorted(by: { $0.price < $1.price })
|
self.products = products.filter { $0.productIdentifier != Tip.supporter.productId }.sorted(by: { $0.price < $1.price })
|
||||||
withAnimation {
|
withAnimation {
|
||||||
loadingProducts = false
|
loadingProducts = false
|
||||||
|
@ -114,7 +114,7 @@ struct SupportAppView: View {
|
||||||
|
|
||||||
private func refreshUserInfo() {
|
private func refreshUserInfo() {
|
||||||
Purchases.shared.getCustomerInfo { info, _ in
|
Purchases.shared.getCustomerInfo { info, _ in
|
||||||
self.customerInfo = info
|
customerInfo = info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@ struct SupportAppView: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
Button {
|
Button {
|
||||||
Purchases.shared.restorePurchases { info, _ in
|
Purchases.shared.restorePurchases { info, _ in
|
||||||
self.customerInfo = info
|
customerInfo = info
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Text("settings.support.restore-purchase.button")
|
Text("settings.support.restore-purchase.button")
|
||||||
|
|
|
@ -68,7 +68,7 @@ struct SwipeActionsSettingsView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createStatusActionPicker(selection: Binding<StatusAction>, label: LocalizedStringKey) -> some View {
|
private func createStatusActionPicker(selection: Binding<StatusAction>, label: LocalizedStringKey) -> some View {
|
||||||
return Picker(selection: selection, label: Text(label)) {
|
Picker(selection: selection, label: Text(label)) {
|
||||||
Section {
|
Section {
|
||||||
Text(StatusAction.none.displayName()).tag(StatusAction.none)
|
Text(StatusAction.none.displayName()).tag(StatusAction.none)
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,27 +86,27 @@ enum Tab: Int, Identifiable, Hashable {
|
||||||
var iconName: String {
|
var iconName: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .timeline:
|
case .timeline:
|
||||||
return "rectangle.stack"
|
"rectangle.stack"
|
||||||
case .trending:
|
case .trending:
|
||||||
return "chart.line.uptrend.xyaxis"
|
"chart.line.uptrend.xyaxis"
|
||||||
case .local:
|
case .local:
|
||||||
return "person.2"
|
"person.2"
|
||||||
case .federated:
|
case .federated:
|
||||||
return "globe.americas"
|
"globe.americas"
|
||||||
case .notifications:
|
case .notifications:
|
||||||
return "bell"
|
"bell"
|
||||||
case .mentions:
|
case .mentions:
|
||||||
return "at"
|
"at"
|
||||||
case .explore:
|
case .explore:
|
||||||
return "magnifyingglass"
|
"magnifyingglass"
|
||||||
case .messages:
|
case .messages:
|
||||||
return "tray"
|
"tray"
|
||||||
case .settings:
|
case .settings:
|
||||||
return "gear"
|
"gear"
|
||||||
case .profile:
|
case .profile:
|
||||||
return "person.crop.circle"
|
"person.crop.circle"
|
||||||
case .other:
|
case .other:
|
||||||
return ""
|
""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ struct AddRemoteTimelineView: View {
|
||||||
isInstanceURLFieldFocused = true
|
isInstanceURLFieldFocused = true
|
||||||
let client = InstanceSocialClient()
|
let client = InstanceSocialClient()
|
||||||
Task {
|
Task {
|
||||||
self.instances = await client.fetchInstances()
|
instances = await client.fetchInstances()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ struct AddRemoteTimelineView: View {
|
||||||
} else {
|
} else {
|
||||||
ForEach(instanceName.isEmpty ? instances : instances.filter { $0.name.contains(instanceName.lowercased()) }) { instance in
|
ForEach(instanceName.isEmpty ? instances : instances.filter { $0.name.contains(instanceName.lowercased()) }) { instance in
|
||||||
Button {
|
Button {
|
||||||
self.instanceName = instance.name
|
instanceName = instance.name
|
||||||
} label: {
|
} label: {
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
Text(instance.name)
|
Text(instance.name)
|
||||||
|
|
|
@ -171,7 +171,8 @@ struct EditTagGroupView: View {
|
||||||
additional: toSave
|
additional: toSave
|
||||||
)
|
)
|
||||||
if let editingTagGroup,
|
if let editingTagGroup,
|
||||||
let index = preferences.tagGroups.firstIndex(of: editingTagGroup) {
|
let index = preferences.tagGroups.firstIndex(of: editingTagGroup)
|
||||||
|
{
|
||||||
preferences.tagGroups[index] = tagGroup
|
preferences.tagGroups[index] = tagGroup
|
||||||
} else {
|
} else {
|
||||||
preferences.tagGroups.append(tagGroup)
|
preferences.tagGroups.append(tagGroup)
|
||||||
|
@ -183,7 +184,7 @@ struct EditTagGroupView: View {
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var symbolsSuggestionView: some View {
|
private var symbolsSuggestionView: some View {
|
||||||
if focusedField == .symbol && !sfSymbolName.isEmpty {
|
if focusedField == .symbol, !sfSymbolName.isEmpty {
|
||||||
let filteredMatches = allSymbols
|
let filteredMatches = allSymbols
|
||||||
.filter { $0.contains(sfSymbolName) }
|
.filter { $0.contains(sfSymbolName) }
|
||||||
if !filteredMatches.isEmpty {
|
if !filteredMatches.isEmpty {
|
||||||
|
|
|
@ -9,5 +9,5 @@ import Foundation
|
||||||
import SFSafeSymbols
|
import SFSafeSymbols
|
||||||
|
|
||||||
let allSymbols: [String] = SFSymbol.allSymbols.map { symbol in
|
let allSymbols: [String] = SFSymbol.allSymbols.map { symbol in
|
||||||
symbol.rawValue
|
symbol.rawValue
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ struct TimelineTab: View {
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
routerPath.client = client
|
routerPath.client = client
|
||||||
if !didAppear && canFilterTimeline {
|
if !didAppear, canFilterTimeline {
|
||||||
didAppear = true
|
didAppear = true
|
||||||
if client.isAuth {
|
if client.isAuth {
|
||||||
timeline = lastTimelineFilter
|
timeline = lastTimelineFilter
|
||||||
|
@ -97,7 +97,7 @@ struct TimelineTab: View {
|
||||||
private var timelineFilterButton: some View {
|
private var timelineFilterButton: some View {
|
||||||
if timeline.supportNewestPagination {
|
if timeline.supportNewestPagination {
|
||||||
Button {
|
Button {
|
||||||
self.timeline = .latest
|
timeline = .latest
|
||||||
} label: {
|
} label: {
|
||||||
Label(TimelineFilter.latest.localizedTitle(), systemImage: TimelineFilter.latest.iconName() ?? "")
|
Label(TimelineFilter.latest.localizedTitle(), systemImage: TimelineFilter.latest.iconName() ?? "")
|
||||||
}
|
}
|
||||||
|
@ -197,7 +197,7 @@ struct TimelineTab: View {
|
||||||
}
|
}
|
||||||
statusEditorToolbarItem(routerPath: routerPath,
|
statusEditorToolbarItem(routerPath: routerPath,
|
||||||
visibility: preferences.postVisibility)
|
visibility: preferences.postVisibility)
|
||||||
if UIDevice.current.userInterfaceIdiom == .pad && !preferences.showiPadSecondaryColumn {
|
if UIDevice.current.userInterfaceIdiom == .pad, !preferences.showiPadSecondaryColumn {
|
||||||
SecondaryColumnToolbarItem()
|
SecondaryColumnToolbarItem()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -70,7 +70,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||||
preferences.setNotification(count: currentCount, token: token)
|
preferences.setNotification(count: currentCount, token: token)
|
||||||
}
|
}
|
||||||
|
|
||||||
let tokens = AppAccountsManager.shared.pushAccounts.map { $0.token }
|
let tokens = AppAccountsManager.shared.pushAccounts.map(\.token)
|
||||||
bestAttemptContent.badge = .init(integerLiteral: preferences.getNotificationsTotalCount(for: tokens))
|
bestAttemptContent.badge = .init(integerLiteral: preferences.getNotificationsTotalCount(for: tokens))
|
||||||
|
|
||||||
if let urlString = notification.icon,
|
if let urlString = notification.icon,
|
||||||
|
|
|
@ -29,7 +29,7 @@ let package = Package(
|
||||||
.product(name: "Status", package: "Status"),
|
.product(name: "Status", package: "Status"),
|
||||||
],
|
],
|
||||||
swiftSettings: [
|
swiftSettings: [
|
||||||
.enableExperimentalFeature("StrictConcurrency")
|
.enableExperimentalFeature("StrictConcurrency"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
|
|
|
@ -89,7 +89,7 @@ public struct AccountDetailView: View {
|
||||||
viewModel.client = client
|
viewModel.client = client
|
||||||
|
|
||||||
// Avoid capturing non-Sendable `self` just to access the view model.
|
// Avoid capturing non-Sendable `self` just to access the view model.
|
||||||
let viewModel = self.viewModel
|
let viewModel = viewModel
|
||||||
Task {
|
Task {
|
||||||
await withTaskGroup(of: Void.self) { group in
|
await withTaskGroup(of: Void.self) { group in
|
||||||
group.addTask { await viewModel.fetchAccount() }
|
group.addTask { await viewModel.fetchAccount() }
|
||||||
|
|
|
@ -27,25 +27,25 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
|
|
||||||
var iconName: String {
|
var iconName: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .statuses: return "bubble.right"
|
case .statuses: "bubble.right"
|
||||||
case .favorites: return "star"
|
case .favorites: "star"
|
||||||
case .bookmarks: return "bookmark"
|
case .bookmarks: "bookmark"
|
||||||
case .followedTags: return "tag"
|
case .followedTags: "tag"
|
||||||
case .postsAndReplies: return "bubble.left.and.bubble.right"
|
case .postsAndReplies: "bubble.left.and.bubble.right"
|
||||||
case .media: return "photo.on.rectangle.angled"
|
case .media: "photo.on.rectangle.angled"
|
||||||
case .lists: return "list.bullet"
|
case .lists: "list.bullet"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var accessibilityLabel: LocalizedStringKey {
|
var accessibilityLabel: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .statuses: return "accessibility.tabs.profile.picker.statuses"
|
case .statuses: "accessibility.tabs.profile.picker.statuses"
|
||||||
case .favorites: return "accessibility.tabs.profile.picker.favorites"
|
case .favorites: "accessibility.tabs.profile.picker.favorites"
|
||||||
case .bookmarks: return "accessibility.tabs.profile.picker.bookmarks"
|
case .bookmarks: "accessibility.tabs.profile.picker.bookmarks"
|
||||||
case .followedTags: return "accessibility.tabs.profile.picker.followed-tags"
|
case .followedTags: "accessibility.tabs.profile.picker.followed-tags"
|
||||||
case .postsAndReplies: return "accessibility.tabs.profile.picker.posts-and-replies"
|
case .postsAndReplies: "accessibility.tabs.profile.picker.posts-and-replies"
|
||||||
case .media: return "accessibility.tabs.profile.picker.media"
|
case .media: "accessibility.tabs.profile.picker.media"
|
||||||
case .lists: return "accessibility.tabs.profile.picker.lists"
|
case .lists: "accessibility.tabs.profile.picker.lists"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
private func fetchAccountData(accountId: String, client: Client) async throws -> AccountData {
|
private func fetchAccountData(accountId: String, client: Client) async throws -> AccountData {
|
||||||
async let account: Account = client.get(endpoint: Accounts.accounts(id: accountId))
|
async let account: Account = client.get(endpoint: Accounts.accounts(id: accountId))
|
||||||
async let featuredTags: [FeaturedTag] = client.get(endpoint: Accounts.featuredTags(id: accountId))
|
async let featuredTags: [FeaturedTag] = client.get(endpoint: Accounts.featuredTags(id: accountId))
|
||||||
if client.isAuth && !isCurrentUser {
|
if client.isAuth, !isCurrentUser {
|
||||||
async let relationships: [Relationship] = client.get(endpoint: Accounts.relationships(ids: [accountId]))
|
async let relationships: [Relationship] = client.get(endpoint: Accounts.relationships(ids: [accountId]))
|
||||||
do {
|
do {
|
||||||
return try await .init(account: account,
|
return try await .init(account: account,
|
||||||
|
|
|
@ -10,15 +10,15 @@ public enum AccountsListMode {
|
||||||
var title: LocalizedStringKey {
|
var title: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .following:
|
case .following:
|
||||||
return "account.following"
|
"account.following"
|
||||||
case .followers:
|
case .followers:
|
||||||
return "account.followers"
|
"account.followers"
|
||||||
case .favoritedBy:
|
case .favoritedBy:
|
||||||
return "account.favorited-by"
|
"account.favorited-by"
|
||||||
case .rebloggedBy:
|
case .rebloggedBy:
|
||||||
return "account.boosted-by"
|
"account.boosted-by"
|
||||||
case .accountsList:
|
case .accountsList:
|
||||||
return ""
|
""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ class AccountsListViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
nextPageId = link?.maxId
|
nextPageId = link?.maxId
|
||||||
relationships = try await client.get(endpoint:
|
relationships = try await client.get(endpoint:
|
||||||
Accounts.relationships(ids: accounts.map { $0.id }))
|
Accounts.relationships(ids: accounts.map(\.id)))
|
||||||
state = .display(accounts: accounts,
|
state = .display(accounts: accounts,
|
||||||
relationships: relationships,
|
relationships: relationships,
|
||||||
nextPageState: link?.maxId != nil ? .hasNextPage : .none)
|
nextPageState: link?.maxId != nil ? .hasNextPage : .none)
|
||||||
|
@ -108,7 +108,7 @@ class AccountsListViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
accounts.append(contentsOf: newAccounts)
|
accounts.append(contentsOf: newAccounts)
|
||||||
let newRelationships: [Relationship] =
|
let newRelationships: [Relationship] =
|
||||||
try await client.get(endpoint: Accounts.relationships(ids: newAccounts.map { $0.id }))
|
try await client.get(endpoint: Accounts.relationships(ids: newAccounts.map(\.id)))
|
||||||
|
|
||||||
relationships.append(contentsOf: newRelationships)
|
relationships.append(contentsOf: newRelationships)
|
||||||
self.nextPageId = link?.maxId
|
self.nextPageId = link?.maxId
|
||||||
|
|
|
@ -28,15 +28,13 @@ struct EditFilterView: View {
|
||||||
@FocusState private var focusedField: Fields?
|
@FocusState private var focusedField: Fields?
|
||||||
|
|
||||||
private var data: ServerFilterData {
|
private var data: ServerFilterData {
|
||||||
var expiresIn: String?
|
let expiresIn: String? = switch expirySelection {
|
||||||
// we add 50 seconds, otherwise we immediately show 6d for a 7d filter (6d, 23h, 59s)
|
|
||||||
switch expirySelection {
|
|
||||||
case .infinite:
|
case .infinite:
|
||||||
expiresIn = "" // need to send an empty value in order for the server to clear this field in the filter
|
"" // need to send an empty value in order for the server to clear this field in the filter
|
||||||
case .custom:
|
case .custom:
|
||||||
expiresIn = String(Int(expiresAt?.timeIntervalSince(Date()) ?? 0) + 50)
|
String(Int(expiresAt?.timeIntervalSince(Date()) ?? 0) + 50)
|
||||||
default:
|
default:
|
||||||
expiresIn = String(expirySelection.rawValue + 50)
|
String(expirySelection.rawValue + 50)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ServerFilterData(title: title,
|
return ServerFilterData(title: title,
|
||||||
|
@ -100,7 +98,7 @@ struct EditFilterView: View {
|
||||||
}
|
}
|
||||||
if expirySelection != .infinite {
|
if expirySelection != .infinite {
|
||||||
DatePicker("filter.edit.expiry.date-time",
|
DatePicker("filter.edit.expiry.date-time",
|
||||||
selection: Binding<Date>(get: { self.expiresAt ?? Date() }, set: { self.expiresAt = $0 }),
|
selection: Binding<Date>(get: { expiresAt ?? Date() }, set: { expiresAt = $0 }),
|
||||||
displayedComponents: [.date, .hourAndMinute])
|
displayedComponents: [.date, .hourAndMinute])
|
||||||
.disabled(expirySelection != .custom)
|
.disabled(expirySelection != .custom)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,11 @@ public struct FiltersListView: View {
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
Form {
|
Form {
|
||||||
if !isLoading && filters.isEmpty {
|
if !isLoading, filters.isEmpty {
|
||||||
EmptyView()
|
EmptyView()
|
||||||
} else {
|
} else {
|
||||||
Section {
|
Section {
|
||||||
if isLoading && filters.isEmpty {
|
if isLoading, filters.isEmpty {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
} else {
|
} else {
|
||||||
ForEach(filters) { filter in
|
ForEach(filters) { filter in
|
||||||
|
@ -31,7 +31,7 @@ public struct FiltersListView: View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text(filter.title)
|
Text(filter.title)
|
||||||
.font(.scaledSubheadline)
|
.font(.scaledSubheadline)
|
||||||
Text("\(filter.context.map { $0.name }.joined(separator: ", "))")
|
Text("\(filter.context.map(\.name).joined(separator: ", "))")
|
||||||
.font(.scaledBody)
|
.font(.scaledBody)
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
if filter.hasExpiry() {
|
if filter.hasExpiry() {
|
||||||
|
|
|
@ -31,7 +31,7 @@ let package = Package(
|
||||||
.product(name: "DesignSystem", package: "DesignSystem"),
|
.product(name: "DesignSystem", package: "DesignSystem"),
|
||||||
],
|
],
|
||||||
swiftSettings: [
|
swiftSettings: [
|
||||||
.enableExperimentalFeature("StrictConcurrency")
|
.enableExperimentalFeature("StrictConcurrency"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -25,9 +25,9 @@ public class AppAccountViewModel: ObservableObject {
|
||||||
|
|
||||||
var acct: String {
|
var acct: String {
|
||||||
if let acct = appAccount.accountName {
|
if let acct = appAccount.accountName {
|
||||||
return acct
|
acct
|
||||||
} else {
|
} else {
|
||||||
return "@\(account?.acct ?? "...")@\(appAccount.server)"
|
"@\(account?.acct ?? "...")@\(appAccount.server)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ public class AppAccountsManager: ObservableObject {
|
||||||
|
|
||||||
public static var shared = AppAccountsManager()
|
public static var shared = AppAccountsManager()
|
||||||
|
|
||||||
internal init() {
|
init() {
|
||||||
var defaultAccount = AppAccount(server: AppInfo.defaultServer, accountName: nil, oauthToken: nil)
|
var defaultAccount = AppAccount(server: AppInfo.defaultServer, accountName: nil, oauthToken: nil)
|
||||||
let keychainAccounts = AppAccount.retrieveAll()
|
let keychainAccounts = AppAccount.retrieveAll()
|
||||||
availableAccounts = keychainAccounts
|
availableAccounts = keychainAccounts
|
||||||
|
|
|
@ -19,7 +19,7 @@ public struct AppAccountsSelectorView: View {
|
||||||
private var showNotificationBadge: Bool {
|
private var showNotificationBadge: Bool {
|
||||||
accountsViewModel
|
accountsViewModel
|
||||||
.filter { $0.account?.id != currentAccount.account?.id }
|
.filter { $0.account?.id != currentAccount.account?.id }
|
||||||
.compactMap { $0.appAccount.oauthToken }
|
.compactMap(\.appAccount.oauthToken)
|
||||||
.map { preferences.getNotificationsCount(for: $0) }
|
.map { preferences.getNotificationsCount(for: $0) }
|
||||||
.reduce(0, +) > 0
|
.reduce(0, +) > 0
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ public struct AppAccountsSelectorView: View {
|
||||||
.redacted(reason: .placeholder)
|
.redacted(reason: .placeholder)
|
||||||
}
|
}
|
||||||
}.overlay(alignment: .topTrailing) {
|
}.overlay(alignment: .topTrailing) {
|
||||||
if (!currentAccount.followRequests.isEmpty || showNotificationBadge) && accountCreationEnabled {
|
if !currentAccount.followRequests.isEmpty || showNotificationBadge, accountCreationEnabled {
|
||||||
Circle()
|
Circle()
|
||||||
.fill(Color.red)
|
.fill(Color.red)
|
||||||
.frame(width: 9, height: 9)
|
.frame(width: 9, height: 9)
|
||||||
|
|
|
@ -31,7 +31,7 @@ let package = Package(
|
||||||
.product(name: "DesignSystem", package: "DesignSystem"),
|
.product(name: "DesignSystem", package: "DesignSystem"),
|
||||||
],
|
],
|
||||||
swiftSettings: [
|
swiftSettings: [
|
||||||
.enableExperimentalFeature("StrictConcurrency")
|
.enableExperimentalFeature("StrictConcurrency"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -27,8 +27,8 @@ struct ConversationsListRow: View {
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
HStack {
|
HStack {
|
||||||
EmojiTextApp(.init(stringValue: conversation.accounts.map { $0.safeDisplayName }.joined(separator: ", ")),
|
EmojiTextApp(.init(stringValue: conversation.accounts.map(\.safeDisplayName).joined(separator: ", ")),
|
||||||
emojis: conversation.accounts.flatMap { $0.emojis })
|
emojis: conversation.accounts.flatMap(\.emojis))
|
||||||
.font(.scaledSubheadline)
|
.font(.scaledSubheadline)
|
||||||
.foregroundColor(theme.labelColor)
|
.foregroundColor(theme.labelColor)
|
||||||
.emojiSize(Font.scaledSubheadlineFont.emojiSize)
|
.emojiSize(Font.scaledSubheadlineFont.emojiSize)
|
||||||
|
|
|
@ -18,9 +18,9 @@ public struct ConversationsListView: View {
|
||||||
|
|
||||||
private var conversations: Binding<[Conversation]> {
|
private var conversations: Binding<[Conversation]> {
|
||||||
if viewModel.isLoadingFirstPage {
|
if viewModel.isLoadingFirstPage {
|
||||||
return Binding.constant(Conversation.placeholders())
|
Binding.constant(Conversation.placeholders())
|
||||||
} else {
|
} else {
|
||||||
return $viewModel.conversations
|
$viewModel.conversations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ public struct ConversationsListView: View {
|
||||||
}
|
}
|
||||||
Divider()
|
Divider()
|
||||||
}
|
}
|
||||||
} else if conversations.isEmpty && !viewModel.isLoadingFirstPage && !viewModel.isError {
|
} else if conversations.isEmpty, !viewModel.isLoadingFirstPage, !viewModel.isError {
|
||||||
EmptyView(iconName: "tray",
|
EmptyView(iconName: "tray",
|
||||||
title: "conversations.empty.title",
|
title: "conversations.empty.title",
|
||||||
message: "conversations.empty.message")
|
message: "conversations.empty.message")
|
||||||
|
@ -79,7 +79,7 @@ public struct ConversationsListView: View {
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
StatusEditorToolbarItem(visibility: .direct)
|
StatusEditorToolbarItem(visibility: .direct)
|
||||||
if UIDevice.current.userInterfaceIdiom == .pad && !preferences.showiPadSecondaryColumn {
|
if UIDevice.current.userInterfaceIdiom == .pad, !preferences.showiPadSecondaryColumn {
|
||||||
SecondaryColumnToolbarItem()
|
SecondaryColumnToolbarItem()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,11 +60,10 @@ class ConversationsListViewModel: ObservableObject {
|
||||||
|
|
||||||
func favorite(conversation: Conversation) async {
|
func favorite(conversation: Conversation) async {
|
||||||
guard let client, let message = conversation.lastStatus else { return }
|
guard let client, let message = conversation.lastStatus else { return }
|
||||||
let endpoint: Endpoint
|
let endpoint: Endpoint = if message.favourited ?? false {
|
||||||
if message.favourited ?? false {
|
Statuses.unfavorite(id: message.id)
|
||||||
endpoint = Statuses.unfavorite(id: message.id)
|
|
||||||
} else {
|
} else {
|
||||||
endpoint = Statuses.favorite(id: message.id)
|
Statuses.favorite(id: message.id)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
let status: Status = try await client.post(endpoint: endpoint)
|
let status: Status = try await client.post(endpoint: endpoint)
|
||||||
|
@ -74,11 +73,10 @@ class ConversationsListViewModel: ObservableObject {
|
||||||
|
|
||||||
func bookmark(conversation: Conversation) async {
|
func bookmark(conversation: Conversation) async {
|
||||||
guard let client, let message = conversation.lastStatus else { return }
|
guard let client, let message = conversation.lastStatus else { return }
|
||||||
let endpoint: Endpoint
|
let endpoint: Endpoint = if message.bookmarked ?? false {
|
||||||
if message.bookmarked ?? false {
|
Statuses.unbookmark(id: message.id)
|
||||||
endpoint = Statuses.unbookmark(id: message.id)
|
|
||||||
} else {
|
} else {
|
||||||
endpoint = Statuses.bookmark(id: message.id)
|
Statuses.bookmark(id: message.id)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
let status: Status = try await client.post(endpoint: endpoint)
|
let status: Status = try await client.post(endpoint: endpoint)
|
||||||
|
|
|
@ -34,7 +34,7 @@ let package = Package(
|
||||||
.product(name: "EmojiText", package: "EmojiText"),
|
.product(name: "EmojiText", package: "EmojiText"),
|
||||||
],
|
],
|
||||||
swiftSettings: [
|
swiftSettings: [
|
||||||
.enableExperimentalFeature("StrictConcurrency")
|
.enableExperimentalFeature("StrictConcurrency"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -160,19 +160,18 @@ public struct ConstellationDark: ColorSet {
|
||||||
public var scheme: ColorScheme = .dark
|
public var scheme: ColorScheme = .dark
|
||||||
public var tintColor: Color = .init(hex: 0xFFD966)
|
public var tintColor: Color = .init(hex: 0xFFD966)
|
||||||
public var primaryBackgroundColor: Color = .init(hex: 0x09192C)
|
public var primaryBackgroundColor: Color = .init(hex: 0x09192C)
|
||||||
public var secondaryBackgroundColor: Color = .init(hex: 0x304c7a)
|
public var secondaryBackgroundColor: Color = .init(hex: 0x304C7A)
|
||||||
public var labelColor: Color = .init(hex: 0xE2E4E2)
|
public var labelColor: Color = .init(hex: 0xE2E4E2)
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public struct ConstellationLight: ColorSet {
|
public struct ConstellationLight: ColorSet {
|
||||||
public var name: ColorSetName = .constellationLight
|
public var name: ColorSetName = .constellationLight
|
||||||
public var scheme: ColorScheme = .light
|
public var scheme: ColorScheme = .light
|
||||||
public var tintColor: Color = .init(hex: 0xc82238)
|
public var tintColor: Color = .init(hex: 0xC82238)
|
||||||
public var primaryBackgroundColor: Color = .init(hex: 0xf4f5f7)
|
public var primaryBackgroundColor: Color = .init(hex: 0xF4F5F7)
|
||||||
public var secondaryBackgroundColor: Color = .init(hex: 0xacc7e5)
|
public var secondaryBackgroundColor: Color = .init(hex: 0xACC7E5)
|
||||||
public var labelColor: Color = .black
|
public var labelColor: Color = .black
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
|
|
|
@ -27,7 +27,7 @@ extension Color: RawRepresentable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public var rawValue: Int {
|
public var rawValue: Int {
|
||||||
guard let coreImageColor = coreImageColor else {
|
guard let coreImageColor else {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
let red = Int(coreImageColor.red * 255 + 0.5)
|
let red = Int(coreImageColor.red * 255 + 0.5)
|
||||||
|
@ -37,7 +37,7 @@ extension Color: RawRepresentable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private var coreImageColor: CIColor? {
|
private var coreImageColor: CIColor? {
|
||||||
return CIColor(color: .init(self))
|
CIColor(color: .init(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,15 +22,15 @@ public class Theme: ObservableObject {
|
||||||
public var title: LocalizedStringKey {
|
public var title: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .system:
|
case .system:
|
||||||
return "settings.display.font.system"
|
"settings.display.font.system"
|
||||||
case .openDyslexic:
|
case .openDyslexic:
|
||||||
return "Open Dyslexic"
|
"Open Dyslexic"
|
||||||
case .hyperLegible:
|
case .hyperLegible:
|
||||||
return "Hyper Legible"
|
"Hyper Legible"
|
||||||
case .SFRounded:
|
case .SFRounded:
|
||||||
return "SF Rounded"
|
"SF Rounded"
|
||||||
case .custom:
|
case .custom:
|
||||||
return "settings.display.font.custom"
|
"settings.display.font.custom"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,9 +41,9 @@ public class Theme: ObservableObject {
|
||||||
public var description: LocalizedStringKey {
|
public var description: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .leading:
|
case .leading:
|
||||||
return "enum.avatar-position.leading"
|
"enum.avatar-position.leading"
|
||||||
case .top:
|
case .top:
|
||||||
return "enum.avatar-position.top"
|
"enum.avatar-position.top"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,9 +54,9 @@ public class Theme: ObservableObject {
|
||||||
public var description: LocalizedStringKey {
|
public var description: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .circle:
|
case .circle:
|
||||||
return "enum.avatar-shape.circle"
|
"enum.avatar-shape.circle"
|
||||||
case .rounded:
|
case .rounded:
|
||||||
return "enum.avatar-shape.rounded"
|
"enum.avatar-shape.rounded"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,11 +67,11 @@ public class Theme: ObservableObject {
|
||||||
public var description: LocalizedStringKey {
|
public var description: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .full:
|
case .full:
|
||||||
return "enum.status-actions-display.all"
|
"enum.status-actions-display.all"
|
||||||
case .discret:
|
case .discret:
|
||||||
return "enum.status-actions-display.only-buttons"
|
"enum.status-actions-display.only-buttons"
|
||||||
case .none:
|
case .none:
|
||||||
return "enum.status-actions-display.no-buttons"
|
"enum.status-actions-display.no-buttons"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,11 +82,11 @@ public class Theme: ObservableObject {
|
||||||
public var description: LocalizedStringKey {
|
public var description: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .large:
|
case .large:
|
||||||
return "enum.status-display-style.large"
|
"enum.status-display-style.large"
|
||||||
case .medium:
|
case .medium:
|
||||||
return "enum.status-display-style.medium"
|
"enum.status-display-style.medium"
|
||||||
case .compact:
|
case .compact:
|
||||||
return "enum.status-display-style.compact"
|
"enum.status-display-style.compact"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ struct ThemeApplier: ViewModifier {
|
||||||
private func allWindows() -> [UIWindow] {
|
private func allWindows() -> [UIWindow] {
|
||||||
UIApplication.shared.connectedScenes
|
UIApplication.shared.connectedScenes
|
||||||
.compactMap { $0 as? UIWindowScene }
|
.compactMap { $0 as? UIWindowScene }
|
||||||
.flatMap { $0.windows }
|
.flatMap(\.windows)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,9 +33,9 @@ public struct AvatarView: View {
|
||||||
var cornerRadius: CGFloat {
|
var cornerRadius: CGFloat {
|
||||||
switch self {
|
switch self {
|
||||||
case .badge, .boost, .list:
|
case .badge, .boost, .list:
|
||||||
return size.width / 2
|
size.width / 2
|
||||||
default:
|
default:
|
||||||
return 4
|
4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ public struct AvatarView: View {
|
||||||
.fill(.gray)
|
.fill(.gray)
|
||||||
.frame(width: size.size.width, height: size.size.height)
|
.frame(width: size.size.width, height: size.size.height)
|
||||||
} else {
|
} else {
|
||||||
LazyImage(request: url.map{ makeImageRequest(for: $0) }) { state in
|
LazyImage(request: url.map { makeImageRequest(for: $0) }) { state in
|
||||||
if let image = state.image {
|
if let image = state.image {
|
||||||
image
|
image
|
||||||
.resizable()
|
.resizable()
|
||||||
|
@ -80,9 +80,9 @@ public struct AvatarView: View {
|
||||||
private var clipShape: some Shape {
|
private var clipShape: some Shape {
|
||||||
switch theme.avatarShape {
|
switch theme.avatarShape {
|
||||||
case .circle:
|
case .circle:
|
||||||
return AnyShape(Circle())
|
AnyShape(Circle())
|
||||||
case .rounded:
|
case .rounded:
|
||||||
return AnyShape(RoundedRectangle(cornerRadius: size.cornerRadius))
|
AnyShape(RoundedRectangle(cornerRadius: size.cornerRadius))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,6 @@ public struct EmojiTextApp: View {
|
||||||
|
|
||||||
private func isRTL() -> Bool {
|
private func isRTL() -> Bool {
|
||||||
// Arabic, Hebrew, Persian, Urdu, Kurdish, Azeri, Dhivehi
|
// Arabic, Hebrew, Persian, Urdu, Kurdish, Azeri, Dhivehi
|
||||||
return ["ar", "he", "fa", "ur", "ku", "az", "dv"].contains(language)
|
["ar", "he", "fa", "ur", "ku", "az", "dv"].contains(language)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ public struct SecondaryColumnToolbarItem: ToolbarContent {
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
|
|
||||||
public var body: some ToolbarContent {
|
public var body: some ToolbarContent {
|
||||||
ToolbarItem(placement: isSecondaryColumn ? .navigationBarLeading : .navigationBarTrailing) {
|
ToolbarItem(placement: isSecondaryColumn ? .navigationBarLeading : .navigationBarTrailing) {
|
||||||
Button {
|
Button {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
|
|
|
@ -29,7 +29,7 @@ let package = Package(
|
||||||
.product(name: "KeychainSwift", package: "keychain-swift"),
|
.product(name: "KeychainSwift", package: "keychain-swift"),
|
||||||
],
|
],
|
||||||
swiftSettings: [
|
swiftSettings: [
|
||||||
.enableExperimentalFeature("StrictConcurrency")
|
.enableExperimentalFeature("StrictConcurrency"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class CurrentAccount: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetchConnections() async {
|
public func fetchConnections() async {
|
||||||
guard let client = client else { return }
|
guard let client else { return }
|
||||||
do {
|
do {
|
||||||
let connections: [String] = try await client.get(endpoint: Instances.peers)
|
let connections: [String] = try await client.get(endpoint: Instances.peers)
|
||||||
client.addConnections(connections)
|
client.addConnections(connections)
|
||||||
|
@ -56,7 +56,7 @@ public class CurrentAccount: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetchCurrentAccount() async {
|
public func fetchCurrentAccount() async {
|
||||||
guard let client = client, client.isAuth else {
|
guard let client, client.isAuth else {
|
||||||
account = nil
|
account = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class CurrentInstance: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetchCurrentInstance() async {
|
public func fetchCurrentInstance() async {
|
||||||
guard let client = client else { return }
|
guard let client else { return }
|
||||||
instance = try? await client.get(endpoint: Instances.instance)
|
instance = try? await client.get(endpoint: Instances.instance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,37 +15,37 @@ public enum Duration: Int, CaseIterable {
|
||||||
public var description: LocalizedStringKey {
|
public var description: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .infinite:
|
case .infinite:
|
||||||
return "enum.durations.infinite"
|
"enum.durations.infinite"
|
||||||
case .fiveMinutes:
|
case .fiveMinutes:
|
||||||
return "enum.durations.fiveMinutes"
|
"enum.durations.fiveMinutes"
|
||||||
case .thirtyMinutes:
|
case .thirtyMinutes:
|
||||||
return "enum.durations.thirtyMinutes"
|
"enum.durations.thirtyMinutes"
|
||||||
case .oneHour:
|
case .oneHour:
|
||||||
return "enum.durations.oneHour"
|
"enum.durations.oneHour"
|
||||||
case .sixHours:
|
case .sixHours:
|
||||||
return "enum.durations.sixHours"
|
"enum.durations.sixHours"
|
||||||
case .twelveHours:
|
case .twelveHours:
|
||||||
return "enum.durations.twelveHours"
|
"enum.durations.twelveHours"
|
||||||
case .oneDay:
|
case .oneDay:
|
||||||
return "enum.durations.oneDay"
|
"enum.durations.oneDay"
|
||||||
case .threeDays:
|
case .threeDays:
|
||||||
return "enum.durations.threeDays"
|
"enum.durations.threeDays"
|
||||||
case .sevenDays:
|
case .sevenDays:
|
||||||
return "enum.durations.sevenDays"
|
"enum.durations.sevenDays"
|
||||||
case .custom:
|
case .custom:
|
||||||
return "enum.durations.custom"
|
"enum.durations.custom"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func mutingDurations() -> [Duration] {
|
public static func mutingDurations() -> [Duration] {
|
||||||
return Self.allCases.filter { $0 != .custom }
|
allCases.filter { $0 != .custom }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func filterDurations() -> [Duration] {
|
public static func filterDurations() -> [Duration] {
|
||||||
return [.infinite, .thirtyMinutes, .oneHour, .sixHours, .twelveHours, .oneDay, .sevenDays, .custom]
|
[.infinite, .thirtyMinutes, .oneHour, .sixHours, .twelveHours, .oneDay, .sevenDays, .custom]
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func pollDurations() -> [Duration] {
|
public static func pollDurations() -> [Duration] {
|
||||||
return [.fiveMinutes, .thirtyMinutes, .oneHour, .sixHours, .twelveHours, .oneDay, .threeDays, .sevenDays]
|
[.fiveMinutes, .thirtyMinutes, .oneHour, .sixHours, .twelveHours, .oneDay, .threeDays, .sevenDays]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,15 +7,15 @@ public enum PollVotingFrequency: String, CaseIterable {
|
||||||
|
|
||||||
public var canVoteMultipleTimes: Bool {
|
public var canVoteMultipleTimes: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .multipleVotes: return true
|
case .multipleVotes: true
|
||||||
case .oneVote: return false
|
case .oneVote: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var displayString: LocalizedStringKey {
|
public var displayString: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .oneVote: return "env.poll-vote-frequency.one"
|
case .oneVote: "env.poll-vote-frequency.one"
|
||||||
case .multipleVotes: return "env.poll-vote-frequency.multiple"
|
case .multipleVotes: "env.poll-vote-frequency.multiple"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ public enum PreferredShareButtonBehavior: Int, CaseIterable, Codable {
|
||||||
|
|
||||||
public var title: LocalizedStringKey {
|
public var title: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .linkOnly: return "settings.content.sharing.share-behavior.link-only"
|
case .linkOnly: "settings.content.sharing.share-behavior.link-only"
|
||||||
case .linkAndText: return "settings.content.sharing.share-behavior.link-and-text"
|
case .linkAndText: "settings.content.sharing.share-behavior.link-and-text"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import Network
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
|
|
||||||
extension UNNotificationResponse: @unchecked Sendable { }
|
extension UNNotificationResponse: @unchecked Sendable {}
|
||||||
extension UNUserNotificationCenter: @unchecked Sendable { }
|
extension UNUserNotificationCenter: @unchecked Sendable {}
|
||||||
|
|
||||||
public struct PushAccount: Equatable {
|
public struct PushAccount: Equatable {
|
||||||
public let server: String
|
public let server: String
|
||||||
|
@ -157,7 +157,7 @@ extension PushNotificationsService: UNUserNotificationCenterDelegate {
|
||||||
|
|
||||||
extension Data {
|
extension Data {
|
||||||
var hexString: String {
|
var hexString: String {
|
||||||
return map { String(format: "%02.2hhx", arguments: [$0]) }.joined()
|
map { String(format: "%02.2hhx", arguments: [$0]) }.joined()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ public class PushNotificationSubscriptionSettings: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateSubscription() async {
|
public func updateSubscription() async {
|
||||||
guard let pushToken = pushToken else { return }
|
guard let pushToken else { return }
|
||||||
let client = Client(server: account.server, oauthToken: account.token)
|
let client = Client(server: account.server, oauthToken: account.token)
|
||||||
do {
|
do {
|
||||||
var listenerURL = PushNotificationsService.Constants.endpoint
|
var listenerURL = PushNotificationsService.Constants.endpoint
|
||||||
|
|
|
@ -45,25 +45,25 @@ public enum SheetDestination: Identifiable {
|
||||||
switch self {
|
switch self {
|
||||||
case .editStatusEditor, .newStatusEditor, .replyToStatusEditor, .quoteStatusEditor,
|
case .editStatusEditor, .newStatusEditor, .replyToStatusEditor, .quoteStatusEditor,
|
||||||
.mentionStatusEditor, .settings, .accountPushNotficationsSettings:
|
.mentionStatusEditor, .settings, .accountPushNotficationsSettings:
|
||||||
return "statusEditor"
|
"statusEditor"
|
||||||
case .listEdit:
|
case .listEdit:
|
||||||
return "listEdit"
|
"listEdit"
|
||||||
case .listAddAccount:
|
case .listAddAccount:
|
||||||
return "listAddAccount"
|
"listAddAccount"
|
||||||
case .addAccount:
|
case .addAccount:
|
||||||
return "addAccount"
|
"addAccount"
|
||||||
case .addTagGroup:
|
case .addTagGroup:
|
||||||
return "addTagGroup"
|
"addTagGroup"
|
||||||
case .addRemoteLocalTimeline:
|
case .addRemoteLocalTimeline:
|
||||||
return "addRemoteLocalTimeline"
|
"addRemoteLocalTimeline"
|
||||||
case .statusEditHistory:
|
case .statusEditHistory:
|
||||||
return "statusEditHistory"
|
"statusEditHistory"
|
||||||
case .report:
|
case .report:
|
||||||
return "report"
|
"report"
|
||||||
case .shareImage:
|
case .shareImage:
|
||||||
return "shareImage"
|
"shareImage"
|
||||||
case .editTagGroup:
|
case .editTagGroup:
|
||||||
return "editTagGroup"
|
"editTagGroup"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,9 +83,9 @@ public class RouterPath: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func handleStatus(status: AnyStatus, url: URL) -> OpenURLAction.Result {
|
public func handleStatus(status: AnyStatus, url: URL) -> OpenURLAction.Result {
|
||||||
if url.pathComponents.count == 3 && url.pathComponents[1] == "tags" &&
|
if url.pathComponents.count == 3, url.pathComponents[1] == "tags",
|
||||||
url.host() == status.account.url?.host(),
|
url.host() == status.account.url?.host(),
|
||||||
let tag = url.pathComponents.last
|
let tag = url.pathComponents.last
|
||||||
{
|
{
|
||||||
// OK this test looks weird but it's
|
// OK this test looks weird but it's
|
||||||
// A 3 component path i.e. ["/", "tags", "tagname"]
|
// A 3 component path i.e. ["/", "tags", "tagname"]
|
||||||
|
@ -97,7 +97,7 @@ public class RouterPath: ObservableObject {
|
||||||
} else if let mention = status.mentions.first(where: { $0.url == url }) {
|
} else if let mention = status.mentions.first(where: { $0.url == url }) {
|
||||||
navigate(to: .accountDetail(id: mention.id))
|
navigate(to: .accountDetail(id: mention.id))
|
||||||
return .handled
|
return .handled
|
||||||
} else if let client = client,
|
} else if let client,
|
||||||
client.isAuth,
|
client.isAuth,
|
||||||
client.hasConnection(with: url),
|
client.hasConnection(with: url),
|
||||||
let id = Int(url.lastPathComponent)
|
let id = Int(url.lastPathComponent)
|
||||||
|
@ -126,7 +126,7 @@ public class RouterPath: ObservableObject {
|
||||||
await navigateToAccountFrom(acct: acct, url: url)
|
await navigateToAccountFrom(acct: acct, url: url)
|
||||||
}
|
}
|
||||||
return .handled
|
return .handled
|
||||||
} else if let client = client,
|
} else if let client,
|
||||||
client.isAuth,
|
client.isAuth,
|
||||||
client.hasConnection(with: url),
|
client.hasConnection(with: url),
|
||||||
let id = Int(url.lastPathComponent)
|
let id = Int(url.lastPathComponent)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import AudioToolbox
|
||||||
import AVKit
|
import AVKit
|
||||||
import CoreHaptics
|
import CoreHaptics
|
||||||
import UIKit
|
import UIKit
|
||||||
import AudioToolbox
|
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public class SoundEffectManager {
|
public class SoundEffectManager {
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class StreamWatcher: ObservableObject {
|
||||||
|
|
||||||
private func receiveMessage() {
|
private func receiveMessage() {
|
||||||
task?.receive(completionHandler: { [weak self] result in
|
task?.receive(completionHandler: { [weak self] result in
|
||||||
guard let self = self else { return }
|
guard let self else { return }
|
||||||
switch result {
|
switch result {
|
||||||
case let .success(message):
|
case let .success(message):
|
||||||
switch message {
|
switch message {
|
||||||
|
@ -83,8 +83,8 @@ public class StreamWatcher: ObservableObject {
|
||||||
print("Error decoding streaming event string")
|
print("Error decoding streaming event string")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let rawEvent = try self.decoder.decode(RawStreamEvent.self, from: data)
|
let rawEvent = try decoder.decode(RawStreamEvent.self, from: data)
|
||||||
if let event = self.rawEventToEvent(rawEvent: rawEvent) {
|
if let event = rawEventToEvent(rawEvent: rawEvent) {
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
self.events.append(event)
|
self.events.append(event)
|
||||||
self.latestEvent = event
|
self.latestEvent = event
|
||||||
|
@ -101,10 +101,10 @@ public class StreamWatcher: ObservableObject {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
self.receiveMessage()
|
receiveMessage()
|
||||||
|
|
||||||
case .failure:
|
case .failure:
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(self.retryDelay)) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(retryDelay)) {
|
||||||
self.retryDelay += 30
|
self.retryDelay += 30
|
||||||
self.stopWatching()
|
self.stopWatching()
|
||||||
self.connect()
|
self.connect()
|
||||||
|
|
|
@ -65,9 +65,9 @@ public class UserPreferences: ObservableObject {
|
||||||
public var description: LocalizedStringKey {
|
public var description: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .iconWithText:
|
case .iconWithText:
|
||||||
return "enum.swipeactions.icon-with-text"
|
"enum.swipeactions.icon-with-text"
|
||||||
case .iconOnly:
|
case .iconOnly:
|
||||||
return "enum.swipeactions.icon-only"
|
"enum.swipeactions.icon-only"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,9 +84,9 @@ public class UserPreferences: ObservableObject {
|
||||||
|
|
||||||
public var postVisibility: Models.Visibility {
|
public var postVisibility: Models.Visibility {
|
||||||
if useInstanceContentSettings {
|
if useInstanceContentSettings {
|
||||||
return serverPreferences?.postVisibility ?? .pub
|
serverPreferences?.postVisibility ?? .pub
|
||||||
} else {
|
} else {
|
||||||
return appDefaultPostVisibility
|
appDefaultPostVisibility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,25 +111,25 @@ public class UserPreferences: ObservableObject {
|
||||||
|
|
||||||
public var postIsSensitive: Bool {
|
public var postIsSensitive: Bool {
|
||||||
if useInstanceContentSettings {
|
if useInstanceContentSettings {
|
||||||
return serverPreferences?.postIsSensitive ?? false
|
serverPreferences?.postIsSensitive ?? false
|
||||||
} else {
|
} else {
|
||||||
return appDefaultPostsSensitive
|
appDefaultPostsSensitive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var autoExpandSpoilers: Bool {
|
public var autoExpandSpoilers: Bool {
|
||||||
if useInstanceContentSettings {
|
if useInstanceContentSettings {
|
||||||
return serverPreferences?.autoExpandSpoilers ?? true
|
serverPreferences?.autoExpandSpoilers ?? true
|
||||||
} else {
|
} else {
|
||||||
return appAutoExpandSpoilers
|
appAutoExpandSpoilers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var autoExpandMedia: ServerPreferences.AutoExpandMedia {
|
public var autoExpandMedia: ServerPreferences.AutoExpandMedia {
|
||||||
if useInstanceContentSettings {
|
if useInstanceContentSettings {
|
||||||
return serverPreferences?.autoExpandMedia ?? .hideSensitive
|
serverPreferences?.autoExpandMedia ?? .hideSensitive
|
||||||
} else {
|
} else {
|
||||||
return appAutoExpandMedia
|
appAutoExpandMedia
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,14 +177,14 @@ public class UserPreferences: ObservableObject {
|
||||||
|
|
||||||
public static func getIntOfVisibility(_ vis: Models.Visibility) -> Int {
|
public static func getIntOfVisibility(_ vis: Models.Visibility) -> Int {
|
||||||
switch vis {
|
switch vis {
|
||||||
case .direct:
|
case .direct:
|
||||||
return 0
|
0
|
||||||
case .priv:
|
case .priv:
|
||||||
return 1
|
1
|
||||||
case .unlisted:
|
case .unlisted:
|
||||||
return 2
|
2
|
||||||
case .pub:
|
case .pub:
|
||||||
return 3
|
3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ let package = Package(
|
||||||
.product(name: "DesignSystem", package: "DesignSystem"),
|
.product(name: "DesignSystem", package: "DesignSystem"),
|
||||||
],
|
],
|
||||||
swiftSettings: [
|
swiftSettings: [
|
||||||
.enableExperimentalFeature("StrictConcurrency")
|
.enableExperimentalFeature("StrictConcurrency"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -126,7 +126,7 @@ public struct ExploreView: View {
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private func makeSearchResultsView(results: SearchResults) -> some View {
|
private func makeSearchResultsView(results: SearchResults) -> some View {
|
||||||
if !results.accounts.isEmpty && (viewModel.searchScope == .all || viewModel.searchScope == .people) {
|
if !results.accounts.isEmpty, viewModel.searchScope == .all || viewModel.searchScope == .people {
|
||||||
Section("explore.section.users") {
|
Section("explore.section.users") {
|
||||||
ForEach(results.accounts) { account in
|
ForEach(results.accounts) { account in
|
||||||
if let relationship = results.relationships.first(where: { $0.id == account.id }) {
|
if let relationship = results.relationships.first(where: { $0.id == account.id }) {
|
||||||
|
@ -136,7 +136,7 @@ public struct ExploreView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !results.hashtags.isEmpty && (viewModel.searchScope == .all || viewModel.searchScope == .hashtags) {
|
if !results.hashtags.isEmpty, viewModel.searchScope == .all || viewModel.searchScope == .hashtags {
|
||||||
Section("explore.section.tags") {
|
Section("explore.section.tags") {
|
||||||
ForEach(results.hashtags) { tag in
|
ForEach(results.hashtags) { tag in
|
||||||
TagRowView(tag: tag)
|
TagRowView(tag: tag)
|
||||||
|
@ -145,7 +145,7 @@ public struct ExploreView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !results.statuses.isEmpty && (viewModel.searchScope == .all || viewModel.searchScope == .posts) {
|
if !results.statuses.isEmpty, viewModel.searchScope == .all || viewModel.searchScope == .posts {
|
||||||
Section("explore.section.posts") {
|
Section("explore.section.posts") {
|
||||||
ForEach(results.statuses) { status in
|
ForEach(results.statuses) { status in
|
||||||
StatusRowView(viewModel: { .init(status: status, client: client, routerPath: routerPath) })
|
StatusRowView(viewModel: { .init(status: status, client: client, routerPath: routerPath) })
|
||||||
|
|
|
@ -11,13 +11,13 @@ class ExploreViewModel: ObservableObject {
|
||||||
var localizedString: LocalizedStringKey {
|
var localizedString: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .all:
|
case .all:
|
||||||
return .init("explore.scope.all")
|
.init("explore.scope.all")
|
||||||
case .people:
|
case .people:
|
||||||
return .init("explore.scope.people")
|
.init("explore.scope.people")
|
||||||
case .hashtags:
|
case .hashtags:
|
||||||
return .init("explore.scope.hashtags")
|
.init("explore.scope.hashtags")
|
||||||
case .posts:
|
case .posts:
|
||||||
return .init("explore.scope.posts")
|
.init("explore.scope.posts")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ class ExploreViewModel: ObservableObject {
|
||||||
trendingStatuses = data.trendingStatuses
|
trendingStatuses = data.trendingStatuses
|
||||||
trendingLinks = data.trendingLinks
|
trendingLinks = data.trendingLinks
|
||||||
|
|
||||||
suggestedAccountsRelationShips = try await client.get(endpoint: Accounts.relationships(ids: suggestedAccounts.map { $0.id }))
|
suggestedAccountsRelationShips = try await client.get(endpoint: Accounts.relationships(ids: suggestedAccounts.map(\.id)))
|
||||||
withAnimation {
|
withAnimation {
|
||||||
isLoaded = true
|
isLoaded = true
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ class ExploreViewModel: ObservableObject {
|
||||||
following: nil),
|
following: nil),
|
||||||
forceVersion: .v2)
|
forceVersion: .v2)
|
||||||
let relationships: [Relationship] =
|
let relationships: [Relationship] =
|
||||||
try await client.get(endpoint: Accounts.relationships(ids: results.accounts.map { $0.id }))
|
try await client.get(endpoint: Accounts.relationships(ids: results.accounts.map(\.id)))
|
||||||
results.relationships = relationships
|
results.relationships = relationships
|
||||||
withAnimation {
|
withAnimation {
|
||||||
self.results[searchQuery] = results
|
self.results[searchQuery] = results
|
||||||
|
|
|
@ -31,7 +31,7 @@ let package = Package(
|
||||||
.product(name: "DesignSystem", package: "DesignSystem"),
|
.product(name: "DesignSystem", package: "DesignSystem"),
|
||||||
],
|
],
|
||||||
swiftSettings: [
|
swiftSettings: [
|
||||||
.enableExperimentalFeature("StrictConcurrency")
|
.enableExperimentalFeature("StrictConcurrency"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -25,7 +25,7 @@ let package = Package(
|
||||||
"SwiftSoup",
|
"SwiftSoup",
|
||||||
],
|
],
|
||||||
swiftSettings: [
|
swiftSettings: [
|
||||||
.enableExperimentalFeature("StrictConcurrency")
|
.enableExperimentalFeature("StrictConcurrency"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
|
|
|
@ -60,11 +60,11 @@ public final class Account: Codable, Identifiable, Hashable, Sendable, Equatable
|
||||||
public let discoverable: Bool?
|
public let discoverable: Bool?
|
||||||
|
|
||||||
public var haveAvatar: Bool {
|
public var haveAvatar: Bool {
|
||||||
return avatar.lastPathComponent != "missing.png"
|
avatar.lastPathComponent != "missing.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
public var haveHeader: Bool {
|
public var haveHeader: Bool {
|
||||||
return header.lastPathComponent != "missing.png"
|
header.lastPathComponent != "missing.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(id: String, username: String, displayName: String?, avatar: URL, header: URL, acct: String, note: HTMLString, createdAt: ServerDate, followersCount: Int, followingCount: Int, statusesCount: Int, lastStatusAt: String? = nil, fields: [Account.Field], locked: Bool, emojis: [Emoji], url: URL? = nil, source: Account.Source? = nil, bot: Bool, discoverable: Bool? = nil) {
|
public init(id: String, username: String, displayName: String?, avatar: URL, header: URL, acct: String, note: HTMLString, createdAt: ServerDate, followersCount: Int, followingCount: Int, statusesCount: Int, lastStatusAt: String? = nil, fields: [Account.Field], locked: Bool, emojis: [Emoji], url: URL? = nil, source: Account.Source? = nil, bot: Bool, discoverable: Bool? = nil) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ public struct HTMLString: Codable, Equatable, Hashable, @unchecked Sendable {
|
||||||
public var asMarkdown: String = ""
|
public var asMarkdown: String = ""
|
||||||
public var asRawText: String = ""
|
public var asRawText: String = ""
|
||||||
public var statusesURLs = [URL]()
|
public var statusesURLs = [URL]()
|
||||||
private(set) public var links = [Link]()
|
public private(set) var links = [Link]()
|
||||||
|
|
||||||
public var asSafeMarkdownAttributedString: AttributedString = .init()
|
public var asSafeMarkdownAttributedString: AttributedString = .init()
|
||||||
private var main_regex: NSRegularExpression?
|
private var main_regex: NSRegularExpression?
|
||||||
|
@ -162,9 +162,9 @@ public struct HTMLString: Codable, Equatable, Hashable, @unchecked Sendable {
|
||||||
if url == nil {
|
if url == nil {
|
||||||
url = URL(string: href, encodePath: true)
|
url = URL(string: href, encodePath: true)
|
||||||
}
|
}
|
||||||
if let linkUrl = url {
|
if let linkUrl = url {
|
||||||
linkRef = linkUrl.absoluteString
|
linkRef = linkUrl.absoluteString
|
||||||
let displayString = asMarkdown[start..<finish]
|
let displayString = asMarkdown[start ..< finish]
|
||||||
links.append(Link(linkUrl, displayString: String(displayString)))
|
links.append(Link(linkUrl, displayString: String(displayString)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,19 +203,19 @@ public struct HTMLString: Codable, Equatable, Hashable, @unchecked Sendable {
|
||||||
self.displayString = displayString
|
self.displayString = displayString
|
||||||
|
|
||||||
switch displayString.first {
|
switch displayString.first {
|
||||||
case "@":
|
case "@":
|
||||||
self.type = .mention
|
type = .mention
|
||||||
self.title = displayString
|
title = displayString
|
||||||
case "#":
|
case "#":
|
||||||
self.type = .hashtag
|
type = .hashtag
|
||||||
self.title = String(displayString.dropFirst())
|
title = String(displayString.dropFirst())
|
||||||
default:
|
default:
|
||||||
self.type = .url
|
type = .url
|
||||||
var hostNameUrl = url.host ?? url.absoluteString
|
var hostNameUrl = url.host ?? url.absoluteString
|
||||||
if hostNameUrl.hasPrefix("www.") {
|
if hostNameUrl.hasPrefix("www.") {
|
||||||
hostNameUrl = String(hostNameUrl.dropFirst(4))
|
hostNameUrl = String(hostNameUrl.dropFirst(4))
|
||||||
}
|
}
|
||||||
self.title = hostNameUrl
|
title = hostNameUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,15 +227,14 @@ public struct HTMLString: Codable, Equatable, Hashable, @unchecked Sendable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension URL {
|
public extension URL {
|
||||||
|
|
||||||
// It's common to use non-ASCII characters in URLs even though they're technically
|
// It's common to use non-ASCII characters in URLs even though they're technically
|
||||||
// invalid characters. Every modern browser handles this by silently encoding
|
// invalid characters. Every modern browser handles this by silently encoding
|
||||||
// the invalid characters on the user's behalf. However, trying to create a URL
|
// the invalid characters on the user's behalf. However, trying to create a URL
|
||||||
// object with un-encoded characters will result in nil so we need to encode the
|
// object with un-encoded characters will result in nil so we need to encode the
|
||||||
// invalid characters before creating the URL object. The unencoded version
|
// invalid characters before creating the URL object. The unencoded version
|
||||||
// should still be shown in the displayed status.
|
// should still be shown in the displayed status.
|
||||||
public init?(string: String, encodePath: Bool) {
|
init?(string: String, encodePath: Bool) {
|
||||||
var encodedUrlString = ""
|
var encodedUrlString = ""
|
||||||
if encodePath,
|
if encodePath,
|
||||||
string.starts(with: "http://") || string.starts(with: "https://"),
|
string.starts(with: "http://") || string.starts(with: "https://"),
|
||||||
|
@ -248,14 +247,14 @@ extension URL {
|
||||||
encodedUrlString = String(string[...startIndex])
|
encodedUrlString = String(string[...startIndex])
|
||||||
while let endIndex = string[string.index(after: startIndex)...].firstIndex(of: "/") {
|
while let endIndex = string[string.index(after: startIndex)...].firstIndex(of: "/") {
|
||||||
let componentStartIndex = string.index(after: startIndex)
|
let componentStartIndex = string.index(after: startIndex)
|
||||||
encodedUrlString = encodedUrlString + (string[componentStartIndex...endIndex].addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? "")
|
encodedUrlString = encodedUrlString + (string[componentStartIndex ... endIndex].addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? "")
|
||||||
startIndex = endIndex
|
startIndex = endIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
// The last part of the path may have a query string appended to it
|
// The last part of the path may have a query string appended to it
|
||||||
let componentStartIndex = string.index(after: startIndex)
|
let componentStartIndex = string.index(after: startIndex)
|
||||||
if let queryStartIndex = string[componentStartIndex...].firstIndex(of: "?") {
|
if let queryStartIndex = string[componentStartIndex...].firstIndex(of: "?") {
|
||||||
encodedUrlString = encodedUrlString + (string[componentStartIndex..<queryStartIndex].addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? "")
|
encodedUrlString = encodedUrlString + (string[componentStartIndex ..< queryStartIndex].addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? "")
|
||||||
encodedUrlString = encodedUrlString + (string[queryStartIndex...].addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")
|
encodedUrlString = encodedUrlString + (string[queryStartIndex...].addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")
|
||||||
} else {
|
} else {
|
||||||
encodedUrlString = encodedUrlString + (string[componentStartIndex...].addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? "")
|
encodedUrlString = encodedUrlString + (string[componentStartIndex...].addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? "")
|
||||||
|
|
|
@ -8,9 +8,9 @@ public struct AppAccount: Codable, Identifiable, Hashable {
|
||||||
|
|
||||||
public var key: String {
|
public var key: String {
|
||||||
if let oauthToken {
|
if let oauthToken {
|
||||||
return "\(server):\(oauthToken.createdAt)"
|
"\(server):\(oauthToken.createdAt)"
|
||||||
} else {
|
} else {
|
||||||
return "\(server):anonymous"
|
"\(server):anonymous"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public struct Language: Identifiable, Equatable, Hashable {
|
public struct Language: Identifiable, Equatable, Hashable {
|
||||||
nonisolated public var id: String { isoCode }
|
public nonisolated var id: String { isoCode }
|
||||||
|
|
||||||
public let isoCode: String
|
public let isoCode: String
|
||||||
public let nativeName: String?
|
public let nativeName: String?
|
||||||
|
|
|
@ -32,7 +32,7 @@ public struct Poll: Codable, Equatable, Hashable {
|
||||||
// the votersCount can be null according to the docs when multiple is false.
|
// the votersCount can be null according to the docs when multiple is false.
|
||||||
// Didn't find that to be true, but we make sure
|
// Didn't find that to be true, but we make sure
|
||||||
public var safeVotersCount: Int {
|
public var safeVotersCount: Int {
|
||||||
return votersCount ?? votesCount
|
votersCount ?? votesCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,14 +24,14 @@ public struct ServerFilter: Codable, Identifiable, Hashable, Sendable {
|
||||||
public let expiresAt: ServerDate?
|
public let expiresAt: ServerDate?
|
||||||
|
|
||||||
public func hasExpiry() -> Bool {
|
public func hasExpiry() -> Bool {
|
||||||
return expiresAt != nil
|
expiresAt != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isExpired() -> Bool {
|
public func isExpired() -> Bool {
|
||||||
if let expiresAtDate = expiresAt?.asDate {
|
if let expiresAtDate = expiresAt?.asDate {
|
||||||
return expiresAtDate < Date()
|
expiresAtDate < Date()
|
||||||
} else {
|
} else {
|
||||||
return false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,30 +40,30 @@ public extension ServerFilter.Context {
|
||||||
var iconName: String {
|
var iconName: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .home:
|
case .home:
|
||||||
return "rectangle.stack"
|
"rectangle.stack"
|
||||||
case .notifications:
|
case .notifications:
|
||||||
return "bell"
|
"bell"
|
||||||
case .public:
|
case .public:
|
||||||
return "globe.americas"
|
"globe.americas"
|
||||||
case .thread:
|
case .thread:
|
||||||
return "bubble.left.and.bubble.right"
|
"bubble.left.and.bubble.right"
|
||||||
case .account:
|
case .account:
|
||||||
return "person.crop.circle"
|
"person.crop.circle"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var name: String {
|
var name: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .home:
|
case .home:
|
||||||
return NSLocalizedString("filter.contexts.home", comment: "")
|
NSLocalizedString("filter.contexts.home", comment: "")
|
||||||
case .notifications:
|
case .notifications:
|
||||||
return NSLocalizedString("filter.contexts.notifications", comment: "")
|
NSLocalizedString("filter.contexts.notifications", comment: "")
|
||||||
case .public:
|
case .public:
|
||||||
return NSLocalizedString("filter.contexts.public", comment: "")
|
NSLocalizedString("filter.contexts.public", comment: "")
|
||||||
case .thread:
|
case .thread:
|
||||||
return NSLocalizedString("filter.contexts.conversations", comment: "")
|
NSLocalizedString("filter.contexts.conversations", comment: "")
|
||||||
case .account:
|
case .account:
|
||||||
return NSLocalizedString("filter.contexts.profiles", comment: "")
|
NSLocalizedString("filter.contexts.profiles", comment: "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,9 +72,9 @@ public extension ServerFilter.Action {
|
||||||
var label: String {
|
var label: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .warn:
|
case .warn:
|
||||||
return NSLocalizedString("filter.action.warning", comment: "")
|
NSLocalizedString("filter.action.warning", comment: "")
|
||||||
case .hide:
|
case .hide:
|
||||||
return NSLocalizedString("filter.action.hide", comment: "")
|
NSLocalizedString("filter.action.hide", comment: "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,11 @@ public struct ServerPreferences: Decodable {
|
||||||
public var description: LocalizedStringKey {
|
public var description: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .showAll:
|
case .showAll:
|
||||||
return "enum.expand-media.show"
|
"enum.expand-media.show"
|
||||||
case .hideAll:
|
case .hideAll:
|
||||||
return "enum.expand-media.hide"
|
"enum.expand-media.hide"
|
||||||
case .hideSensitive:
|
case .hideSensitive:
|
||||||
return "enum.expand-media.hide-sensitive"
|
"enum.expand-media.hide-sensitive"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ let package = Package(
|
||||||
.product(name: "Models", package: "Models"),
|
.product(name: "Models", package: "Models"),
|
||||||
],
|
],
|
||||||
swiftSettings: [
|
swiftSettings: [
|
||||||
.enableExperimentalFeature("StrictConcurrency")
|
.enableExperimentalFeature("StrictConcurrency"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
|
|
|
@ -33,49 +33,49 @@ public enum Accounts: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case let .accounts(id):
|
case let .accounts(id):
|
||||||
return "accounts/\(id)"
|
"accounts/\(id)"
|
||||||
case .favorites:
|
case .favorites:
|
||||||
return "favourites"
|
"favourites"
|
||||||
case .bookmarks:
|
case .bookmarks:
|
||||||
return "bookmarks"
|
"bookmarks"
|
||||||
case .followedTags:
|
case .followedTags:
|
||||||
return "followed_tags"
|
"followed_tags"
|
||||||
case let .featuredTags(id):
|
case let .featuredTags(id):
|
||||||
return "accounts/\(id)/featured_tags"
|
"accounts/\(id)/featured_tags"
|
||||||
case .verifyCredentials:
|
case .verifyCredentials:
|
||||||
return "accounts/verify_credentials"
|
"accounts/verify_credentials"
|
||||||
case .updateCredentials:
|
case .updateCredentials:
|
||||||
return "accounts/update_credentials"
|
"accounts/update_credentials"
|
||||||
case let .statuses(id, _, _, _, _, _):
|
case let .statuses(id, _, _, _, _, _):
|
||||||
return "accounts/\(id)/statuses"
|
"accounts/\(id)/statuses"
|
||||||
case .relationships:
|
case .relationships:
|
||||||
return "accounts/relationships"
|
"accounts/relationships"
|
||||||
case let .follow(id, _, _):
|
case let .follow(id, _, _):
|
||||||
return "accounts/\(id)/follow"
|
"accounts/\(id)/follow"
|
||||||
case let .unfollow(id):
|
case let .unfollow(id):
|
||||||
return "accounts/\(id)/unfollow"
|
"accounts/\(id)/unfollow"
|
||||||
case .familiarFollowers:
|
case .familiarFollowers:
|
||||||
return "accounts/familiar_followers"
|
"accounts/familiar_followers"
|
||||||
case .suggestions:
|
case .suggestions:
|
||||||
return "suggestions"
|
"suggestions"
|
||||||
case let .following(id, _):
|
case let .following(id, _):
|
||||||
return "accounts/\(id)/following"
|
"accounts/\(id)/following"
|
||||||
case let .followers(id, _):
|
case let .followers(id, _):
|
||||||
return "accounts/\(id)/followers"
|
"accounts/\(id)/followers"
|
||||||
case let .lists(id):
|
case let .lists(id):
|
||||||
return "accounts/\(id)/lists"
|
"accounts/\(id)/lists"
|
||||||
case .preferences:
|
case .preferences:
|
||||||
return "preferences"
|
"preferences"
|
||||||
case let .block(id):
|
case let .block(id):
|
||||||
return "accounts/\(id)/block"
|
"accounts/\(id)/block"
|
||||||
case let .unblock(id):
|
case let .unblock(id):
|
||||||
return "accounts/\(id)/unblock"
|
"accounts/\(id)/unblock"
|
||||||
case let .mute(id, _):
|
case let .mute(id, _):
|
||||||
return "accounts/\(id)/mute"
|
"accounts/\(id)/mute"
|
||||||
case let .unmute(id):
|
case let .unmute(id):
|
||||||
return "accounts/\(id)/unmute"
|
"accounts/\(id)/unmute"
|
||||||
case let .relationshipNote(id, _):
|
case let .relationshipNote(id, _):
|
||||||
return "accounts/\(id)/note"
|
"accounts/\(id)/note"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,13 +128,13 @@ public enum Accounts: Endpoint {
|
||||||
public var jsonValue: Encodable? {
|
public var jsonValue: Encodable? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .mute(_, json):
|
case let .mute(_, json):
|
||||||
return json
|
json
|
||||||
case let .relationshipNote(_, json):
|
case let .relationshipNote(_, json):
|
||||||
return json
|
json
|
||||||
case let .updateCredentials(json):
|
case let .updateCredentials(json):
|
||||||
return json
|
json
|
||||||
default:
|
default:
|
||||||
return nil
|
nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ public enum Apps: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .registerApp:
|
case .registerApp:
|
||||||
return "apps"
|
"apps"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,20 +8,20 @@ public enum Conversations: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .conversations:
|
case .conversations:
|
||||||
return "conversations"
|
"conversations"
|
||||||
case let .delete(id):
|
case let .delete(id):
|
||||||
return "conversations/\(id)"
|
"conversations/\(id)"
|
||||||
case let .read(id):
|
case let .read(id):
|
||||||
return "conversations/\(id)/read"
|
"conversations/\(id)/read"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func queryItems() -> [URLQueryItem]? {
|
public func queryItems() -> [URLQueryItem]? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .conversations(maxId):
|
case let .conversations(maxId):
|
||||||
return makePaginationParam(sinceId: nil, maxId: maxId, mindId: nil)
|
makePaginationParam(sinceId: nil, maxId: maxId, mindId: nil)
|
||||||
default:
|
default:
|
||||||
return nil
|
nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ public enum CustomEmojis: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .customEmojis:
|
case .customEmojis:
|
||||||
return "custom_emojis"
|
"custom_emojis"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,11 @@ public enum FollowRequests: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .list:
|
case .list:
|
||||||
return "follow_requests"
|
"follow_requests"
|
||||||
case let .accept(id):
|
case let .accept(id):
|
||||||
return "follow_requests/\(id)/authorize"
|
"follow_requests/\(id)/authorize"
|
||||||
case let .reject(id):
|
case let .reject(id):
|
||||||
return "follow_requests/\(id)/reject"
|
"follow_requests/\(id)/reject"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ public enum Instances: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .instance:
|
case .instance:
|
||||||
return "instance"
|
"instance"
|
||||||
case .peers:
|
case .peers:
|
||||||
return "instance/peers"
|
"instance/peers"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,13 @@ public enum Lists: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .lists, .createList:
|
case .lists, .createList:
|
||||||
return "lists"
|
"lists"
|
||||||
case let .list(id):
|
case let .list(id):
|
||||||
return "lists/\(id)"
|
"lists/\(id)"
|
||||||
case let .accounts(listId):
|
case let .accounts(listId):
|
||||||
return "lists/\(listId)/accounts"
|
"lists/\(listId)/accounts"
|
||||||
case let .updateAccounts(listId, _):
|
case let .updateAccounts(listId, _):
|
||||||
return "lists/\(listId)/accounts"
|
"lists/\(listId)/accounts"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,22 +7,22 @@ public enum Media: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .medias:
|
case .medias:
|
||||||
return "media"
|
"media"
|
||||||
case let .media(id, _):
|
case let .media(id, _):
|
||||||
return "media/\(id)"
|
"media/\(id)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func queryItems() -> [URLQueryItem]? {
|
public func queryItems() -> [URLQueryItem]? {
|
||||||
return nil
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public var jsonValue: Encodable? {
|
public var jsonValue: Encodable? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .media(_, json):
|
case let .media(_, json):
|
||||||
return json
|
json
|
||||||
default:
|
default:
|
||||||
return nil
|
nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,11 @@ public enum Notifications: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .notifications:
|
case .notifications:
|
||||||
return "notifications"
|
"notifications"
|
||||||
case let .notification(id):
|
case let .notification(id):
|
||||||
return "notifications/\(id)"
|
"notifications/\(id)"
|
||||||
case .clear:
|
case .clear:
|
||||||
return "notifications/clear"
|
"notifications/clear"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,18 +8,18 @@ public enum Oauth: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .authorize:
|
case .authorize:
|
||||||
return "oauth/authorize"
|
"oauth/authorize"
|
||||||
case .token:
|
case .token:
|
||||||
return "oauth/token"
|
"oauth/token"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var jsonValue: Encodable? {
|
public var jsonValue: Encodable? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .token(code, clientId, clientSecret):
|
case let .token(code, clientId, clientSecret):
|
||||||
return TokenData(clientId: clientId, clientSecret: clientSecret, code: code)
|
TokenData(clientId: clientId, clientSecret: clientSecret, code: code)
|
||||||
default:
|
default:
|
||||||
return nil
|
nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ public enum Polls: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case let .poll(id):
|
case let .poll(id):
|
||||||
return "polls/\(id)"
|
"polls/\(id)"
|
||||||
case let .vote(id, _):
|
case let .vote(id, _):
|
||||||
return "polls/\(id)/votes"
|
"polls/\(id)/votes"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ public enum Push: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .subscription, .createSub:
|
case .subscription, .createSub:
|
||||||
return "push/subscription"
|
"push/subscription"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ public enum Search: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .search:
|
case .search:
|
||||||
return "search"
|
"search"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,17 +12,17 @@ public enum ServerFilters: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .filters:
|
case .filters:
|
||||||
return "filters"
|
"filters"
|
||||||
case .createFilter:
|
case .createFilter:
|
||||||
return "filters"
|
"filters"
|
||||||
case let .filter(id):
|
case let .filter(id):
|
||||||
return "filters/\(id)"
|
"filters/\(id)"
|
||||||
case let .editFilter(id, _):
|
case let .editFilter(id, _):
|
||||||
return "filters/\(id)"
|
"filters/\(id)"
|
||||||
case let .addKeyword(id, _, _):
|
case let .addKeyword(id, _, _):
|
||||||
return "filters/\(id)/keywords"
|
"filters/\(id)/keywords"
|
||||||
case let .removeKeyword(id):
|
case let .removeKeyword(id):
|
||||||
return "filters/keywords/\(id)"
|
"filters/keywords/\(id)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,11 +39,11 @@ public enum ServerFilters: Endpoint {
|
||||||
public var jsonValue: Encodable? {
|
public var jsonValue: Encodable? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .createFilter(json):
|
case let .createFilter(json):
|
||||||
return json
|
json
|
||||||
case let .editFilter(_, json):
|
case let .editFilter(_, json):
|
||||||
return json
|
json
|
||||||
default:
|
default:
|
||||||
return nil
|
nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,39 +23,39 @@ public enum Statuses: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .postStatus:
|
case .postStatus:
|
||||||
return "statuses"
|
"statuses"
|
||||||
case let .status(id):
|
case let .status(id):
|
||||||
return "statuses/\(id)"
|
"statuses/\(id)"
|
||||||
case let .editStatus(id, _):
|
case let .editStatus(id, _):
|
||||||
return "statuses/\(id)"
|
"statuses/\(id)"
|
||||||
case let .context(id):
|
case let .context(id):
|
||||||
return "statuses/\(id)/context"
|
"statuses/\(id)/context"
|
||||||
case let .favorite(id):
|
case let .favorite(id):
|
||||||
return "statuses/\(id)/favourite"
|
"statuses/\(id)/favourite"
|
||||||
case let .unfavorite(id):
|
case let .unfavorite(id):
|
||||||
return "statuses/\(id)/unfavourite"
|
"statuses/\(id)/unfavourite"
|
||||||
case let .reblog(id):
|
case let .reblog(id):
|
||||||
return "statuses/\(id)/reblog"
|
"statuses/\(id)/reblog"
|
||||||
case let .unreblog(id):
|
case let .unreblog(id):
|
||||||
return "statuses/\(id)/unreblog"
|
"statuses/\(id)/unreblog"
|
||||||
case let .rebloggedBy(id, _):
|
case let .rebloggedBy(id, _):
|
||||||
return "statuses/\(id)/reblogged_by"
|
"statuses/\(id)/reblogged_by"
|
||||||
case let .favoritedBy(id, _):
|
case let .favoritedBy(id, _):
|
||||||
return "statuses/\(id)/favourited_by"
|
"statuses/\(id)/favourited_by"
|
||||||
case let .pin(id):
|
case let .pin(id):
|
||||||
return "statuses/\(id)/pin"
|
"statuses/\(id)/pin"
|
||||||
case let .unpin(id):
|
case let .unpin(id):
|
||||||
return "statuses/\(id)/unpin"
|
"statuses/\(id)/unpin"
|
||||||
case let .bookmark(id):
|
case let .bookmark(id):
|
||||||
return "statuses/\(id)/bookmark"
|
"statuses/\(id)/bookmark"
|
||||||
case let .unbookmark(id):
|
case let .unbookmark(id):
|
||||||
return "statuses/\(id)/unbookmark"
|
"statuses/\(id)/unbookmark"
|
||||||
case let .history(id):
|
case let .history(id):
|
||||||
return "statuses/\(id)/history"
|
"statuses/\(id)/history"
|
||||||
case let .translate(id, _):
|
case let .translate(id, _):
|
||||||
return "statuses/\(id)/translate"
|
"statuses/\(id)/translate"
|
||||||
case .report:
|
case .report:
|
||||||
return "reports"
|
"reports"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,11 +82,11 @@ public enum Statuses: Endpoint {
|
||||||
public var jsonValue: Encodable? {
|
public var jsonValue: Encodable? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .postStatus(json):
|
case let .postStatus(json):
|
||||||
return json
|
json
|
||||||
case let .editStatus(_, json):
|
case let .editStatus(_, json):
|
||||||
return json
|
json
|
||||||
default:
|
default:
|
||||||
return nil
|
nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,14 @@ public enum Streaming: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .streaming:
|
case .streaming:
|
||||||
return "streaming"
|
"streaming"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func queryItems() -> [URLQueryItem]? {
|
public func queryItems() -> [URLQueryItem]? {
|
||||||
switch self {
|
switch self {
|
||||||
default:
|
default:
|
||||||
return nil
|
nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,18 +8,18 @@ public enum Tags: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case let .tag(id):
|
case let .tag(id):
|
||||||
return "tags/\(id)/"
|
"tags/\(id)/"
|
||||||
case let .follow(id):
|
case let .follow(id):
|
||||||
return "tags/\(id)/follow"
|
"tags/\(id)/follow"
|
||||||
case let .unfollow(id):
|
case let .unfollow(id):
|
||||||
return "tags/\(id)/unfollow"
|
"tags/\(id)/unfollow"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func queryItems() -> [URLQueryItem]? {
|
public func queryItems() -> [URLQueryItem]? {
|
||||||
switch self {
|
switch self {
|
||||||
default:
|
default:
|
||||||
return nil
|
nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,13 @@ public enum Timelines: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .pub:
|
case .pub:
|
||||||
return "timelines/public"
|
"timelines/public"
|
||||||
case .home:
|
case .home:
|
||||||
return "timelines/home"
|
"timelines/home"
|
||||||
case let .list(listId, _, _, _):
|
case let .list(listId, _, _, _):
|
||||||
return "timelines/list/\(listId)"
|
"timelines/list/\(listId)"
|
||||||
case let .hashtag(tag, _, _):
|
case let .hashtag(tag, _, _):
|
||||||
return "timelines/tag/\(tag)"
|
"timelines/tag/\(tag)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,11 @@ public enum Trends: Endpoint {
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .tags:
|
case .tags:
|
||||||
return "trends/tags"
|
"trends/tags"
|
||||||
case .statuses:
|
case .statuses:
|
||||||
return "trends/statuses"
|
"trends/statuses"
|
||||||
case .links:
|
case .links:
|
||||||
return "trends/links"
|
"trends/links"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,15 +62,15 @@ public struct OpenAIClient {
|
||||||
var request: OpenAIRequest {
|
var request: OpenAIRequest {
|
||||||
switch self {
|
switch self {
|
||||||
case let .correct(input):
|
case let .correct(input):
|
||||||
return ChatRequest(content: "Fix the spelling and grammar mistakes in the following text: \(input)", temperature: 0.2)
|
ChatRequest(content: "Fix the spelling and grammar mistakes in the following text: \(input)", temperature: 0.2)
|
||||||
case let .addTags(input):
|
case let .addTags(input):
|
||||||
return ChatRequest(content: "Replace relevant words with camel-cased hashtags in the following text. Don't try to search for context or add hashtags if there is not enough context: \(input)", temperature: 0.1)
|
ChatRequest(content: "Replace relevant words with camel-cased hashtags in the following text. Don't try to search for context or add hashtags if there is not enough context: \(input)", temperature: 0.1)
|
||||||
case let .insertTags(input):
|
case let .insertTags(input):
|
||||||
return ChatRequest(content: "Return the input with added camel-cased hashtags at the end of the input. Don't try to search for context or add hashtags if there is not enough context: \(input)", temperature: 0.2)
|
ChatRequest(content: "Return the input with added camel-cased hashtags at the end of the input. Don't try to search for context or add hashtags if there is not enough context: \(input)", temperature: 0.2)
|
||||||
case let .shorten(input):
|
case let .shorten(input):
|
||||||
return ChatRequest(content: "Make a shorter version of this text: \(input)", temperature: 0.5)
|
ChatRequest(content: "Make a shorter version of this text: \(input)", temperature: 0.5)
|
||||||
case let .emphasize(input):
|
case let .emphasize(input):
|
||||||
return ChatRequest(content: "Make this text catchy, more fun: \(input)", temperature: 1)
|
ChatRequest(content: "Make this text catchy, more fun: \(input)", temperature: 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
||||||
|
|
||||||
public extension String {
|
public extension String {
|
||||||
func escape() -> String {
|
func escape() -> String {
|
||||||
return replacingOccurrences(of: "&", with: "&")
|
replacingOccurrences(of: "&", with: "&")
|
||||||
.replacingOccurrences(of: "<", with: "<")
|
.replacingOccurrences(of: "<", with: "<")
|
||||||
.replacingOccurrences(of: ">", with: ">")
|
.replacingOccurrences(of: ">", with: ">")
|
||||||
.replacingOccurrences(of: """, with: "\"")
|
.replacingOccurrences(of: """, with: "\"")
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
||||||
|
|
||||||
extension Data {
|
extension Data {
|
||||||
func base64UrlEncodedString() -> String {
|
func base64UrlEncodedString() -> String {
|
||||||
return base64EncodedString()
|
base64EncodedString()
|
||||||
.replacingOccurrences(of: "+", with: "-")
|
.replacingOccurrences(of: "+", with: "-")
|
||||||
.replacingOccurrences(of: "/", with: "_")
|
.replacingOccurrences(of: "/", with: "_")
|
||||||
.replacingOccurrences(of: "=", with: "")
|
.replacingOccurrences(of: "=", with: "")
|
||||||
|
|
|
@ -33,7 +33,7 @@ let package = Package(
|
||||||
.product(name: "DesignSystem", package: "DesignSystem"),
|
.product(name: "DesignSystem", package: "DesignSystem"),
|
||||||
],
|
],
|
||||||
swiftSettings: [
|
swiftSettings: [
|
||||||
.enableExperimentalFeature("StrictConcurrency")
|
.enableExperimentalFeature("StrictConcurrency"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -11,7 +11,7 @@ extension ConsolidatedNotification {
|
||||||
var notificationIds: [String] { notifications.map(\.id) }
|
var notificationIds: [String] { notifications.map(\.id) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Array where Element == ConsolidatedNotification {
|
extension [ConsolidatedNotification] {
|
||||||
var notificationCount: Int {
|
var notificationCount: Int {
|
||||||
reduce(0) { $0 + ($1.accounts.isEmpty ? 1 : $1.accounts.count) }
|
reduce(0) { $0 + ($1.accounts.isEmpty ? 1 : $1.accounts.count) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import Models
|
import Models
|
||||||
|
|
||||||
extension Array where Element == Models.Notification {
|
extension [Models.Notification] {
|
||||||
func consolidated(selectedType: Models.Notification.NotificationType?) async -> [ConsolidatedNotification] {
|
func consolidated(selectedType: Models.Notification.NotificationType?) async -> [ConsolidatedNotification] {
|
||||||
await withCheckedContinuation { result in
|
await withCheckedContinuation { result in
|
||||||
DispatchQueue.global().async {
|
DispatchQueue.global().async {
|
||||||
|
|
|
@ -6,42 +6,42 @@ extension Models.Notification.NotificationType {
|
||||||
public func label(count: Int) -> LocalizedStringKey {
|
public func label(count: Int) -> LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .status:
|
case .status:
|
||||||
return "notifications.label.status"
|
"notifications.label.status"
|
||||||
case .mention:
|
case .mention:
|
||||||
return ""
|
""
|
||||||
case .reblog:
|
case .reblog:
|
||||||
return "notifications.label.reblog \(count)"
|
"notifications.label.reblog \(count)"
|
||||||
case .follow:
|
case .follow:
|
||||||
return "notifications.label.follow \(count)"
|
"notifications.label.follow \(count)"
|
||||||
case .follow_request:
|
case .follow_request:
|
||||||
return "notifications.label.follow-request"
|
"notifications.label.follow-request"
|
||||||
case .favourite:
|
case .favourite:
|
||||||
return "notifications.label.favorite \(count)"
|
"notifications.label.favorite \(count)"
|
||||||
case .poll:
|
case .poll:
|
||||||
return "notifications.label.poll"
|
"notifications.label.poll"
|
||||||
case .update:
|
case .update:
|
||||||
return "notifications.label.update"
|
"notifications.label.update"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func notificationKey() -> String {
|
public func notificationKey() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .status:
|
case .status:
|
||||||
return "notifications.label.status.push"
|
"notifications.label.status.push"
|
||||||
case .mention:
|
case .mention:
|
||||||
return ""
|
""
|
||||||
case .reblog:
|
case .reblog:
|
||||||
return "notifications.label.reblog.push"
|
"notifications.label.reblog.push"
|
||||||
case .follow:
|
case .follow:
|
||||||
return "notifications.label.follow.push"
|
"notifications.label.follow.push"
|
||||||
case .follow_request:
|
case .follow_request:
|
||||||
return "notifications.label.follow-request.push"
|
"notifications.label.follow-request.push"
|
||||||
case .favourite:
|
case .favourite:
|
||||||
return "notifications.label.favorite.push"
|
"notifications.label.favorite.push"
|
||||||
case .poll:
|
case .poll:
|
||||||
return "notifications.label.poll.push"
|
"notifications.label.poll.push"
|
||||||
case .update:
|
case .update:
|
||||||
return "notifications.label.update.push"
|
"notifications.label.update.push"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,21 +86,21 @@ extension Models.Notification.NotificationType {
|
||||||
func menuTitle() -> LocalizedStringKey {
|
func menuTitle() -> LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .status:
|
case .status:
|
||||||
return "notifications.menu-title.status"
|
"notifications.menu-title.status"
|
||||||
case .mention:
|
case .mention:
|
||||||
return "notifications.menu-title.mention"
|
"notifications.menu-title.mention"
|
||||||
case .reblog:
|
case .reblog:
|
||||||
return "notifications.menu-title.reblog"
|
"notifications.menu-title.reblog"
|
||||||
case .follow:
|
case .follow:
|
||||||
return "notifications.menu-title.follow"
|
"notifications.menu-title.follow"
|
||||||
case .follow_request:
|
case .follow_request:
|
||||||
return "notifications.menu-title.follow-request"
|
"notifications.menu-title.follow-request"
|
||||||
case .favourite:
|
case .favourite:
|
||||||
return "notifications.menu-title.favorite"
|
"notifications.menu-title.favorite"
|
||||||
case .poll:
|
case .poll:
|
||||||
return "notifications.menu-title.poll"
|
"notifications.menu-title.poll"
|
||||||
case .update:
|
case .update:
|
||||||
return "notifications.menu-title.update"
|
"notifications.menu-title.update"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ class NotificationsViewModel: ObservableObject {
|
||||||
if let selectedType {
|
if let selectedType {
|
||||||
var excludedTypes = Models.Notification.NotificationType.allCases
|
var excludedTypes = Models.Notification.NotificationType.allCases
|
||||||
excludedTypes.removeAll(where: { $0 == selectedType })
|
excludedTypes.removeAll(where: { $0 == selectedType })
|
||||||
return excludedTypes.map { $0.rawValue }
|
return excludedTypes.map(\.rawValue)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ let package = Package(
|
||||||
.product(name: "DesignSystem", package: "DesignSystem"),
|
.product(name: "DesignSystem", package: "DesignSystem"),
|
||||||
],
|
],
|
||||||
swiftSettings: [
|
swiftSettings: [
|
||||||
.enableExperimentalFeature("StrictConcurrency")
|
.enableExperimentalFeature("StrictConcurrency"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -161,7 +161,7 @@ public struct StatusDetailView: View {
|
||||||
.id(status.id)
|
.id(status.id)
|
||||||
// VoiceOver / Switch Control focus workaround
|
// VoiceOver / Switch Control focus workaround
|
||||||
.onAppear {
|
.onAppear {
|
||||||
self.initialFocusBugWorkaround = true
|
initialFocusBugWorkaround = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,15 +24,15 @@ enum StatusEditorAIPrompt: CaseIterable {
|
||||||
func toRequestPrompt(text: String) -> OpenAIClient.Prompt {
|
func toRequestPrompt(text: String) -> OpenAIClient.Prompt {
|
||||||
switch self {
|
switch self {
|
||||||
case .correct:
|
case .correct:
|
||||||
return .correct(input: text)
|
.correct(input: text)
|
||||||
case .addTags:
|
case .addTags:
|
||||||
return .addTags(input: text)
|
.addTags(input: text)
|
||||||
case .insertTags:
|
case .insertTags:
|
||||||
return .insertTags(input: text)
|
.insertTags(input: text)
|
||||||
case .fit:
|
case .fit:
|
||||||
return .shorten(input: text)
|
.shorten(input: text)
|
||||||
case .emphasize:
|
case .emphasize:
|
||||||
return .emphasize(input: text)
|
.emphasize(input: text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,7 +162,7 @@ struct StatusEditorAccessoryView: View {
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private func languageTextView(isoCode: String, nativeName: String?, name: String?) -> some View {
|
private func languageTextView(isoCode: String, nativeName: String?, name: String?) -> some View {
|
||||||
if let nativeName = nativeName, let name = name {
|
if let nativeName, let name {
|
||||||
Text("\(nativeName) (\(name))")
|
Text("\(nativeName) (\(name))")
|
||||||
} else {
|
} else {
|
||||||
Text(isoCode.uppercased())
|
Text(isoCode.uppercased())
|
||||||
|
|
|
@ -8,7 +8,7 @@ actor StatusEditorCompressor {
|
||||||
}
|
}
|
||||||
|
|
||||||
func compressImageFrom(url: URL) async -> Data? {
|
func compressImageFrom(url: URL) async -> Data? {
|
||||||
return await withCheckedContinuation { continuation in
|
await withCheckedContinuation { continuation in
|
||||||
let sourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
|
let sourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
|
||||||
guard let source = CGImageSourceCreateWithURL(url as CFURL, sourceOptions) else {
|
guard let source = CGImageSourceCreateWithURL(url as CFURL, sourceOptions) else {
|
||||||
continuation.resume(returning: nil)
|
continuation.resume(returning: nil)
|
||||||
|
|
|
@ -66,7 +66,7 @@ struct StatusEditorMediaEditView: View {
|
||||||
Button {
|
Button {
|
||||||
if !imageDescription.isEmpty {
|
if !imageDescription.isEmpty {
|
||||||
isUpdating = true
|
isUpdating = true
|
||||||
if currentInstance.isEditAltTextSupported && viewModel.mode.isEditing {
|
if currentInstance.isEditAltTextSupported, viewModel.mode.isEditing {
|
||||||
Task {
|
Task {
|
||||||
await viewModel.editDescription(container: container, description: imageDescription)
|
await viewModel.editDescription(container: container, description: imageDescription)
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|
|
@ -43,18 +43,18 @@ enum StatusEditorUTTypeSupported: String, CaseIterable {
|
||||||
var isVideo: Bool {
|
var isVideo: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .video, .movie, .mp4, .quickTimeMovie:
|
case .video, .movie, .mp4, .quickTimeMovie:
|
||||||
return true
|
true
|
||||||
default:
|
default:
|
||||||
return false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isGif: Bool {
|
var isGif: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .gif, .gif2:
|
case .gif, .gif2:
|
||||||
return true
|
true
|
||||||
default:
|
default:
|
||||||
return false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ enum StatusEditorUTTypeSupported: String, CaseIterable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getVideoTransferable(item: NSItemProvider) async -> MovieFileTranseferable? {
|
private func getVideoTransferable(item: NSItemProvider) async -> MovieFileTranseferable? {
|
||||||
return await withCheckedContinuation { continuation in
|
await withCheckedContinuation { continuation in
|
||||||
_ = item.loadTransferable(type: MovieFileTranseferable.self) { result in
|
_ = item.loadTransferable(type: MovieFileTranseferable.self) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case let .success(success):
|
case let .success(success):
|
||||||
|
@ -113,7 +113,7 @@ enum StatusEditorUTTypeSupported: String, CaseIterable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getGifTransferable(item: NSItemProvider) async -> GifFileTranseferable? {
|
private func getGifTransferable(item: NSItemProvider) async -> GifFileTranseferable? {
|
||||||
return await withCheckedContinuation { continuation in
|
await withCheckedContinuation { continuation in
|
||||||
_ = item.loadTransferable(type: GifFileTranseferable.self) { result in
|
_ = item.loadTransferable(type: GifFileTranseferable.self) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case let .success(success):
|
case let .success(success):
|
||||||
|
@ -126,7 +126,7 @@ enum StatusEditorUTTypeSupported: String, CaseIterable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getImageTansferable(item: NSItemProvider) async -> ImageFileTranseferable? {
|
private func getImageTansferable(item: NSItemProvider) async -> ImageFileTranseferable? {
|
||||||
return await withCheckedContinuation { continuation in
|
await withCheckedContinuation { continuation in
|
||||||
_ = item.loadTransferable(type: ImageFileTranseferable.self) { result in
|
_ = item.loadTransferable(type: ImageFileTranseferable.self) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case let .success(success):
|
case let .success(success):
|
||||||
|
|
|
@ -157,7 +157,7 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
||||||
|
|
||||||
func evaluateLanguages() {
|
func evaluateLanguages() {
|
||||||
if let detectedLang = detectLanguage(text: statusText.string),
|
if let detectedLang = detectLanguage(text: statusText.string),
|
||||||
let selectedLanguage = selectedLanguage,
|
let selectedLanguage,
|
||||||
selectedLanguage != "",
|
selectedLanguage != "",
|
||||||
selectedLanguage != detectedLang
|
selectedLanguage != detectedLang
|
||||||
{
|
{
|
||||||
|
@ -311,14 +311,14 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
||||||
let range = NSMakeRange(0, statusText.string.utf16.count)
|
let range = NSMakeRange(0, statusText.string.utf16.count)
|
||||||
var ranges = hashtagRegex.matches(in: statusText.string,
|
var ranges = hashtagRegex.matches(in: statusText.string,
|
||||||
options: [],
|
options: [],
|
||||||
range: range).map { $0.range }
|
range: range).map(\.range)
|
||||||
ranges.append(contentsOf: mentionRegex.matches(in: statusText.string,
|
ranges.append(contentsOf: mentionRegex.matches(in: statusText.string,
|
||||||
options: [],
|
options: [],
|
||||||
range: range).map { $0.range })
|
range: range).map(\.range))
|
||||||
|
|
||||||
let urlRanges = urlRegex.matches(in: statusText.string,
|
let urlRanges = urlRegex.matches(in: statusText.string,
|
||||||
options: [],
|
options: [],
|
||||||
range: range).map { $0.range }
|
range: range).map(\.range)
|
||||||
|
|
||||||
var foundSuggestionRange = false
|
var foundSuggestionRange = false
|
||||||
for nsRange in ranges {
|
for nsRange in ranges {
|
||||||
|
|
|
@ -14,40 +14,40 @@ public extension StatusEditorViewModel {
|
||||||
var isInShareExtension: Bool {
|
var isInShareExtension: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .shareExtension:
|
case .shareExtension:
|
||||||
return true
|
true
|
||||||
default:
|
default:
|
||||||
return false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isEditing: Bool {
|
var isEditing: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .edit:
|
case .edit:
|
||||||
return true
|
true
|
||||||
default:
|
default:
|
||||||
return false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var replyToStatus: Status? {
|
var replyToStatus: Status? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .replyTo(status):
|
case let .replyTo(status):
|
||||||
return status
|
status
|
||||||
default:
|
default:
|
||||||
return nil
|
nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var title: LocalizedStringKey {
|
var title: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .new, .mention, .shareExtension:
|
case .new, .mention, .shareExtension:
|
||||||
return "status.editor.mode.new"
|
"status.editor.mode.new"
|
||||||
case .edit:
|
case .edit:
|
||||||
return "status.editor.mode.edit"
|
"status.editor.mode.edit"
|
||||||
case let .replyTo(status):
|
case let .replyTo(status):
|
||||||
return "status.editor.mode.reply-\(status.reblog?.account.displayNameWithoutEmojis ?? status.account.displayNameWithoutEmojis)"
|
"status.editor.mode.reply-\(status.reblog?.account.displayNameWithoutEmojis ?? status.account.displayNameWithoutEmojis)"
|
||||||
case let .quote(status):
|
case let .quote(status):
|
||||||
return "status.editor.mode.quote-\(status.reblog?.account.displayNameWithoutEmojis ?? status.account.displayNameWithoutEmojis)"
|
"status.editor.mode.quote-\(status.reblog?.account.displayNameWithoutEmojis ?? status.account.displayNameWithoutEmojis)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue