This commit is contained in:
Thomas Ricouard 2023-11-01 18:58:44 +01:00
parent 4c7a7986c5
commit 3e3c69c41c
30 changed files with 142 additions and 147 deletions

View file

@ -6,11 +6,11 @@ import Env
import Explore import Explore
import LinkPresentation import LinkPresentation
import Lists import Lists
import MediaUI
import Models import Models
import Status import Status
import SwiftUI import SwiftUI
import Timeline import Timeline
import MediaUI
@MainActor @MainActor
extension View { extension View {

View file

@ -1,5 +1,5 @@
import SwiftUI
import Env import Env
import SwiftUI
extension IceCubesApp { extension IceCubesApp {
@CommandsBuilder @CommandsBuilder

View file

@ -1,7 +1,7 @@
import SwiftUI
import Env import Env
import Status
import MediaUI import MediaUI
import Status
import SwiftUI
extension IceCubesApp { extension IceCubesApp {
var appScene: some Scene { var appScene: some Scene {
@ -26,9 +26,9 @@ extension IceCubesApp {
.sheet(item: $quickLook.selectedMediaAttachment) { selectedMediaAttachment in .sheet(item: $quickLook.selectedMediaAttachment) { selectedMediaAttachment in
MediaUIView(selectedAttachment: selectedMediaAttachment, MediaUIView(selectedAttachment: selectedMediaAttachment,
attachments: quickLook.mediaAttachments) attachments: quickLook.mediaAttachments)
.presentationBackground(.ultraThinMaterial) .presentationBackground(.ultraThinMaterial)
.presentationCornerRadius(16) .presentationCornerRadius(16)
.withEnvironments() .withEnvironments()
} }
.onChange(of: pushNotificationsService.handledNotification) { _, newValue in .onChange(of: pushNotificationsService.handledNotification) { _, newValue in
if newValue != nil { if newValue != nil {
@ -72,7 +72,6 @@ extension IceCubesApp {
} }
} }
var otherScenes: some Scene { var otherScenes: some Scene {
WindowGroup(for: WindowDestination.self) { destination in WindowGroup(for: WindowDestination.self) { destination in
Group { Group {

View file

@ -1,5 +1,5 @@
import SwiftUI
import Env import Env
import SwiftUI
extension IceCubesApp { extension IceCubesApp {
var sidebarView: some View { var sidebarView: some View {
@ -46,5 +46,4 @@ extension IceCubesApp {
.frame(maxWidth: .secondaryColumnWidth) .frame(maxWidth: .secondaryColumnWidth)
.id(appAccountsManager.currentAccount.id) .id(appAccountsManager.currentAccount.id)
} }
} }

View file

@ -1,5 +1,5 @@
import SwiftUI
import Env import Env
import SwiftUI
extension IceCubesApp { extension IceCubesApp {
var tabBarView: some View { var tabBarView: some View {
@ -46,7 +46,6 @@ extension IceCubesApp {
.id(appAccountsManager.currentClient.id) .id(appAccountsManager.currentClient.id)
} }
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

View file

@ -4,12 +4,12 @@ import AVFoundation
import DesignSystem import DesignSystem
import Env import Env
import KeychainSwift import KeychainSwift
import MediaUI
import Network import Network
import RevenueCat import RevenueCat
import Status import Status
import SwiftUI import SwiftUI
import Timeline import Timeline
import MediaUI
@main @main
struct IceCubesApp: App { struct IceCubesApp: App {

View file

@ -169,7 +169,7 @@ struct SettingsTabs: View {
private var otherSections: some View { private var otherSections: some View {
@Bindable var preferences = preferences @Bindable var preferences = preferences
Section("settings.section.other") { Section("settings.section.other") {
if !ProcessInfo.processInfo.isMacCatalystApp{ if !ProcessInfo.processInfo.isMacCatalystApp {
Picker(selection: $preferences.preferredBrowser) { Picker(selection: $preferences.preferredBrowser) {
ForEach(PreferredBrowser.allCases, id: \.rawValue) { browser in ForEach(PreferredBrowser.allCases, id: \.rawValue) { browser in
switch browser { switch browser {

View file

@ -106,11 +106,10 @@ struct ConversationMessageView: View {
Button { Button {
Task { Task {
do { do {
let status: Status let status: Status = if isLiked {
if isLiked { try await client.post(endpoint: Statuses.unfavorite(id: message.id))
status = try await client.post(endpoint: Statuses.unfavorite(id: message.id))
} else { } else {
status = try await client.post(endpoint: Statuses.favorite(id: message.id)) try await client.post(endpoint: Statuses.favorite(id: message.id))
} }
withAnimation { withAnimation {
isLiked = status.favourited == true isLiked = status.favourited == true
@ -123,11 +122,10 @@ struct ConversationMessageView: View {
} }
Button { Task { Button { Task {
do { do {
let status: Status let status: Status = if isBookmarked {
if isBookmarked { try await client.post(endpoint: Statuses.unbookmark(id: message.id))
status = try await client.post(endpoint: Statuses.unbookmark(id: message.id))
} else { } else {
status = try await client.post(endpoint: Statuses.bookmark(id: message.id)) try await client.post(endpoint: Statuses.bookmark(id: message.id))
} }
withAnimation { withAnimation {
isBookmarked = status.bookmarked == true isBookmarked = status.bookmarked == true

View file

@ -16,10 +16,10 @@ import UIKit
window = windowScene.keyWindow window = windowScene.keyWindow
#if targetEnvironment(macCatalyst) #if targetEnvironment(macCatalyst)
if let titlebar = windowScene.titlebar { if let titlebar = windowScene.titlebar {
titlebar.titleVisibility = .hidden titlebar.titleVisibility = .hidden
titlebar.toolbar = nil titlebar.toolbar = nil
} }
#endif #endif
} }
} }

View file

@ -12,31 +12,31 @@ import SwiftUI
/// A LazyImage (Nuke) with a geometry reader under the hood in order to use a Resize Processor to optimize performances on lists. /// A LazyImage (Nuke) with a geometry reader under the hood in order to use a Resize Processor to optimize performances on lists.
/// This views also allows smooth resizing of the images by debouncing the update of the ImageProcessor. /// This views also allows smooth resizing of the images by debouncing the update of the ImageProcessor.
public struct LazyResizableImage<Content: View>: View { public struct LazyResizableImage<Content: View>: View {
public init(url: URL?, @ViewBuilder content: @escaping (LazyImageState, GeometryProxy) -> Content) { public init(url: URL?, @ViewBuilder content: @escaping (LazyImageState, GeometryProxy) -> Content) {
self.imageURL = url imageURL = url
self.content = content self.content = content
} }
let imageURL: URL? let imageURL: URL?
@State private var resizeProcessor: ImageProcessors.Resize? @State private var resizeProcessor: ImageProcessors.Resize?
@State private var debouncedTask: Task<Void, Never>? @State private var debouncedTask: Task<Void, Never>?
@ViewBuilder @ViewBuilder
private var content: (LazyImageState, _ proxy: GeometryProxy) -> Content private var content: (LazyImageState, _ proxy: GeometryProxy) -> Content
public var body: some View { public var body: some View {
GeometryReader { proxy in GeometryReader { proxy in
LazyImage(url: imageURL) { state in LazyImage(url: imageURL) { state in
content(state, proxy) content(state, proxy)
} }
.processors([resizeProcessor == nil ? .resize(size: proxy.size) : resizeProcessor!]) .processors([resizeProcessor == nil ? .resize(size: proxy.size) : resizeProcessor!])
.onChange(of: proxy.size, initial: true) { oldValue, newValue in .onChange(of: proxy.size, initial: true) { _, newValue in
debouncedTask?.cancel() debouncedTask?.cancel()
debouncedTask = Task { debouncedTask = Task {
do { try await Task.sleep(for: .milliseconds(200)) } catch { return } do { try await Task.sleep(for: .milliseconds(200)) } catch { return }
resizeProcessor = .resize(size: newValue) resizeProcessor = .resize(size: newValue)
}
}
} }
}
} }
}
} }

View file

@ -4,8 +4,9 @@ import SwiftUI
@MainActor @MainActor
public extension View { public extension View {
func statusEditorToolbarItem(routerPath: RouterPath, func statusEditorToolbarItem(routerPath _: RouterPath,
visibility: Models.Visibility) -> some ToolbarContent { visibility: Models.Visibility) -> some ToolbarContent
{
StatusEditorToolbarItem(visibility: visibility) StatusEditorToolbarItem(visibility: visibility)
} }
} }

View file

@ -88,20 +88,18 @@ import SwiftUI
} }
public var pendingLocation: Alignment { public var pendingLocation: Alignment {
get { let fromLeft = Locale.current.language.characterDirection == .leftToRight ? pendingShownLeft : !pendingShownLeft
let fromLeft = Locale.current.language.characterDirection == .leftToRight ? pendingShownLeft : !pendingShownLeft if pendingShownAtBottom {
if pendingShownAtBottom { if fromLeft {
if fromLeft { return .bottomLeading
return .bottomLeading
} else {
return .bottomTrailing
}
} else { } else {
if fromLeft { return .bottomTrailing
return .topLeading }
} else { } else {
return .topTrailing if fromLeft {
} return .topLeading
} else {
return .topTrailing
} }
} }
} }
@ -375,7 +373,7 @@ import SwiftUI
} }
public var totalNotificationsCount: Int { public var totalNotificationsCount: Int {
notificationsCount.compactMap{ $0.value }.reduce(0, +) notificationsCount.compactMap(\.value).reduce(0, +)
} }
public func reloadNotificationsCount(tokens: [OauthToken]) { public func reloadNotificationsCount(tokens: [OauthToken]) {
@ -456,6 +454,6 @@ import SwiftUI
collapseLongPosts = storage.collapseLongPosts collapseLongPosts = storage.collapseLongPosts
shareButtonBehavior = storage.shareButtonBehavior shareButtonBehavior = storage.shareButtonBehavior
pendingShownAtBottom = storage.pendingShownAtBottom pendingShownAtBottom = storage.pendingShownAtBottom
pendingShownLeft = storage.pendingShownLeft pendingShownLeft = storage.pendingShownLeft
} }
} }

View file

@ -1,6 +1,6 @@
import SwiftUI
import Models import Models
import NukeUI import NukeUI
import SwiftUI
struct MediaUIAttachmentImageView: View { struct MediaUIAttachmentImageView: View {
let url: URL let url: URL

View file

@ -67,7 +67,7 @@ public struct MediaUIAttachmentVideoView: View {
VideoPlayer(player: viewModel.player) VideoPlayer(player: viewModel.player)
.accessibilityAddTraits(.startsMediaSession) .accessibilityAddTraits(.startsMediaSession)
if !preferences.autoPlayVideo && !viewModel.forceAutoPlay { if !preferences.autoPlayVideo, !viewModel.forceAutoPlay {
Image(systemName: "play.fill") Image(systemName: "play.fill")
.font(isCompact ? .body : .largeTitle) .font(isCompact ? .body : .largeTitle)
.foregroundColor(theme.tintColor) .foregroundColor(theme.tintColor)

View file

@ -1,6 +1,6 @@
import CoreTransferable
import SwiftUI import SwiftUI
import UIKit import UIKit
import CoreTransferable
struct MediaUIImageTransferable: Codable, Transferable { struct MediaUIImageTransferable: Codable, Transferable {
let url: URL let url: URL

View file

@ -1,7 +1,7 @@
import Nuke
import SwiftUI
import Models import Models
import Nuke
import QuickLook import QuickLook
import SwiftUI
public struct MediaUIView: View, @unchecked Sendable { public struct MediaUIView: View, @unchecked Sendable {
private let data: [DisplayData] private let data: [DisplayData]
@ -36,8 +36,8 @@ public struct MediaUIView: View, @unchecked Sendable {
} }
public init(selectedAttachment: MediaAttachment, attachments: [MediaAttachment]) { public init(selectedAttachment: MediaAttachment, attachments: [MediaAttachment]) {
self.data = attachments.compactMap { DisplayData(from: $0) } data = attachments.compactMap { DisplayData(from: $0) }
self.initialItem = DisplayData(from: selectedAttachment) initialItem = DisplayData(from: selectedAttachment)
} }
} }
@ -45,9 +45,9 @@ private struct MediaToolBar: ToolbarContent {
let data: DisplayData let data: DisplayData
var body: some ToolbarContent { var body: some ToolbarContent {
#if !targetEnvironment(macCatalyst) #if !targetEnvironment(macCatalyst)
DismissToolbarItem() DismissToolbarItem()
#endif #endif
QuickLookToolbarItem(itemUrl: data.url) QuickLookToolbarItem(itemUrl: data.url)
AltTextToolbarItem(alt: data.description) AltTextToolbarItem(alt: data.description)
SavePhotoToolbarItem(url: data.url, type: data.type) SavePhotoToolbarItem(url: data.url, type: data.type)
@ -75,15 +75,15 @@ private struct AltTextToolbarItem: ToolbarContent {
var body: some ToolbarContent { var body: some ToolbarContent {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {
if let alt = alt { if let alt {
Button { Button {
isAlertDisplayed = true isAlertDisplayed = true
} label: { } label: {
Text("status.image.alt-text.abbreviation") Text("status.image.alt-text.abbreviation")
} }
.alert("status.editor.media.image-description", .alert("status.editor.media.image-description",
isPresented: $isAlertDisplayed isPresented: $isAlertDisplayed)
) { {
Button("alert.button.ok", action: {}) Button("alert.button.ok", action: {})
} message: { } message: {
Text(alt) Text(alt)
@ -118,8 +118,8 @@ private struct SavePhotoToolbarItem: ToolbarContent, @unchecked Sendable {
} label: { } label: {
switch state { switch state {
case .unsaved: Image(systemName: "arrow.down.circle") case .unsaved: Image(systemName: "arrow.down.circle")
case .saving : ProgressView() case .saving: ProgressView()
case .saved : Image(systemName: "checkmark.circle.fill") case .saved: Image(systemName: "checkmark.circle.fill")
} }
} }
} else { } else {
@ -205,7 +205,7 @@ private struct QuickLookToolbarItem: ToolbarContent, @unchecked Sendable {
in: .userDomainMask, in: .userDomainMask,
appropriateFor: nil, appropriateFor: nil,
create: false) create: false)
.appending(component: "quicklook") .appending(component: "quicklook")
} }
} }
@ -236,9 +236,9 @@ private struct DisplayData: Identifiable, Hashable {
guard let url = attachment.url else { return nil } guard let url = attachment.url else { return nil }
guard let type = attachment.supportedType else { return nil } guard let type = attachment.supportedType else { return nil }
self.id = attachment.id id = attachment.id
self.url = url self.url = url
self.description = attachment.description description = attachment.description
self.type = DisplayType(from: type) self.type = DisplayType(from: type)
} }
} }

View file

@ -3,7 +3,7 @@ import UIKit
// ref: https://stackoverflow.com/questions/74238414/is-there-an-easy-way-to-pinch-to-zoom-and-drag-any-view-in-swiftui // ref: https://stackoverflow.com/questions/74238414/is-there-an-easy-way-to-pinch-to-zoom-and-drag-any-view-in-swiftui
fileprivate let maxAllowedScale = 4.0 private let maxAllowedScale = 4.0
@MainActor @MainActor
struct MediaUIZoomableContainer<Content: View>: View { struct MediaUIZoomableContainer<Content: View>: View {
@ -61,7 +61,7 @@ struct MediaUIZoomableContainer<Content: View>: View {
} }
func makeCoordinator() -> Coordinator { func makeCoordinator() -> Coordinator {
return Coordinator(hostingController: UIHostingController(rootView: content), scale: $currentScale) Coordinator(hostingController: UIHostingController(rootView: content), scale: $currentScale)
} }
func updateUIView(_ uiView: UIScrollView, context: Context) { func updateUIView(_ uiView: UIScrollView, context: Context) {
@ -95,11 +95,11 @@ struct MediaUIZoomableContainer<Content: View>: View {
_currentScale = scale _currentScale = scale
} }
func viewForZooming(in scrollView: UIScrollView) -> UIView? { func viewForZooming(in _: UIScrollView) -> UIView? {
return hostingController.view hostingController.view
} }
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) { func scrollViewDidEndZooming(_: UIScrollView, with _: UIView?, atScale scale: CGFloat) {
currentScale = scale currentScale = scale
} }
} }

View file

@ -45,13 +45,13 @@ public struct MediaAttachment: Codable, Identifiable, Hashable, Equatable {
public let description: String? public let description: String?
public let meta: MetaContainer? public let meta: MetaContainer?
public static func imageWith(url: URL) -> MediaAttachment{ public static func imageWith(url: URL) -> MediaAttachment {
return .init(id: UUID().uuidString, .init(id: UUID().uuidString,
type: "image", type: "image",
url: url, url: url,
previewUrl: url, previewUrl: url,
description: nil, description: nil,
meta: nil) meta: nil)
} }
} }

View file

@ -4,7 +4,7 @@ import SwiftUI
@Model public class Draft { @Model public class Draft {
public var content: String = "" public var content: String = ""
public var creationDate: Date = Date() public var creationDate: Date = .init()
public init(content: String) { public init(content: String) {
self.content = content self.content = content

View file

@ -4,7 +4,7 @@ import SwiftUI
@Model public class LocalTimeline { @Model public class LocalTimeline {
public var instance: String = "" public var instance: String = ""
public var creationDate: Date = Date() public var creationDate: Date = .init()
public init(instance: String) { public init(instance: String) {
self.instance = instance self.instance = instance

View file

@ -6,7 +6,7 @@ import SwiftUI
public var title: String = "" public var title: String = ""
public var symbolName: String = "" public var symbolName: String = ""
public var tags: [String] = [] public var tags: [String] = []
public var creationDate: Date = Date() public var creationDate: Date = .init()
public init(title: String, symbolName: String, tags: [String]) { public init(title: String, symbolName: String, tags: [String]) {
self.title = title self.title = title

View file

@ -93,7 +93,8 @@ import SwiftUI
private func makeURL(scheme: String = "https", private func makeURL(scheme: String = "https",
endpoint: Endpoint, endpoint: Endpoint,
forceVersion: Version? = nil, forceVersion: Version? = nil,
forceServer: String? = nil) throws -> URL { forceServer: String? = nil) throws -> URL
{
var components = URLComponents() var components = URLComponents()
components.scheme = scheme components.scheme = scheme
components.host = forceServer ?? server components.host = forceServer ?? server
@ -140,7 +141,7 @@ import SwiftUI
} }
public func getWithLink<Entity: Decodable>(endpoint: Endpoint) async throws -> (Entity, LinkHandler?) { public func getWithLink<Entity: Decodable>(endpoint: Endpoint) async throws -> (Entity, LinkHandler?) {
let (data, httpResponse) = try await urlSession.data(for: try makeGet(endpoint: endpoint)) let (data, httpResponse) = try await urlSession.data(for: makeGet(endpoint: endpoint))
var linkHandler: LinkHandler? var linkHandler: LinkHandler?
if let response = httpResponse as? HTTPURLResponse, if let response = httpResponse as? HTTPURLResponse,
let link = response.allHeaderFields["Link"] as? String let link = response.allHeaderFields["Link"] as? String

View file

@ -211,7 +211,7 @@ public struct StatusRowView: View {
let attachments = viewModel.finalStatus.mediaAttachments let attachments = viewModel.finalStatus.mediaAttachments
if ProcessInfo.processInfo.isMacCatalystApp { if ProcessInfo.processInfo.isMacCatalystApp {
openWindow(value: WindowDestination.mediaViewer(attachments: attachments, openWindow(value: WindowDestination.mediaViewer(attachments: attachments,
selectedAttachment: attachments[0])) selectedAttachment: attachments[0]))
} else { } else {
quickLook.prepareFor(selectedMediaAttachment: attachments[0], mediaAttachments: attachments) quickLook.prepareFor(selectedMediaAttachment: attachments[0], mediaAttachments: attachments)
} }

View file

@ -1,10 +1,10 @@
import DesignSystem import DesignSystem
import Env import Env
import MediaUI
import Models import Models
import Nuke import Nuke
import NukeUI import NukeUI
import SwiftUI import SwiftUI
import MediaUI
@MainActor @MainActor
public struct StatusRowMediaPreviewView: View { public struct StatusRowMediaPreviewView: View {
@ -29,7 +29,7 @@ public struct StatusRowMediaPreviewView: View {
var availableWidth: CGFloat { var availableWidth: CGFloat {
if UIDevice.current.userInterfaceIdiom == .phone && if UIDevice.current.userInterfaceIdiom == .phone &&
(UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight) || theme.statusDisplayStyle == .medium (UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight) || theme.statusDisplayStyle == .medium
{ {
return sceneDelegate.windowWidth * 0.80 return sceneDelegate.windowWidth * 0.80
} }

View file

@ -4,9 +4,9 @@ import Models
import Network import Network
import Shimmer import Shimmer
import Status import Status
import SwiftData
import SwiftUI import SwiftUI
import SwiftUIIntrospect import SwiftUIIntrospect
import SwiftData
@MainActor @MainActor
public struct TimelineView: View { public struct TimelineView: View {