Bigger media viewer window

This commit is contained in:
Thomas Ricouard 2023-11-14 19:48:14 +01:00
parent bf618d3c5f
commit f451d7cb8c
14 changed files with 68 additions and 19 deletions

View file

@ -11,7 +11,7 @@ extension IceCubesApp {
.keyboardShortcut("n", modifiers: .shift) .keyboardShortcut("n", modifiers: .shift)
Button("menu.new-post") { Button("menu.new-post") {
if ProcessInfo.processInfo.isMacCatalystApp { if ProcessInfo.processInfo.isMacCatalystApp {
openWindow(value: WindowDestination.newStatusEditor(visibility: userPreferences.postVisibility)) openWindow(value: WindowDestinationEditor.newStatusEditor(visibility: userPreferences.postVisibility))
} else { } else {
sidebarRouterPath.presentedSheet = .newStatusEditor(visibility: userPreferences.postVisibility) sidebarRouterPath.presentedSheet = .newStatusEditor(visibility: userPreferences.postVisibility)
} }

View file

@ -72,8 +72,9 @@ extension IceCubesApp {
} }
} }
@SceneBuilder
var otherScenes: some Scene { var otherScenes: some Scene {
WindowGroup(for: WindowDestination.self) { destination in WindowGroup(for: WindowDestinationEditor.self) { destination in
Group { Group {
switch destination.wrappedValue { switch destination.wrappedValue {
case let .newStatusEditor(visibility): case let .newStatusEditor(visibility):
@ -84,6 +85,21 @@ extension IceCubesApp {
StatusEditorView(mode: .quote(status: status)) StatusEditorView(mode: .quote(status: status))
case let .replyToStatusEditor(status): case let .replyToStatusEditor(status):
StatusEditorView(mode: .replyTo(status: status)) StatusEditorView(mode: .replyTo(status: status))
case .none:
EmptyView()
}
}
.withEnvironments()
.withModelContainer()
.applyTheme(theme)
.frame(minWidth: 300, minHeight: 400)
}
.defaultSize(width: 600, height: 800)
.windowResizability(.contentMinSize)
WindowGroup(for: WindowDestinationMedia.self) { destination in
Group {
switch destination.wrappedValue {
case let .mediaViewer(attachments, selectedAttachment): case let .mediaViewer(attachments, selectedAttachment):
MediaUIView(selectedAttachment: selectedAttachment, MediaUIView(selectedAttachment: selectedAttachment,
attachments: attachments) attachments: attachments)
@ -94,8 +110,9 @@ extension IceCubesApp {
.withEnvironments() .withEnvironments()
.withModelContainer() .withModelContainer()
.applyTheme(theme) .applyTheme(theme)
.frame(minWidth: 300, minHeight: 400)
} }
.defaultSize(width: 600, height: 800) .defaultSize(width: 1200, height: 1000)
.windowResizability(.automatic) .windowResizability(.contentMinSize)
} }
} }

View file

@ -58,7 +58,7 @@ struct SideBarView<Content: View>: View {
private var postButton: some View { private var postButton: some View {
Button { Button {
if ProcessInfo.processInfo.isMacCatalystApp { if ProcessInfo.processInfo.isMacCatalystApp {
openWindow(value: WindowDestination.newStatusEditor(visibility: userPreferences.postVisibility)) openWindow(value: WindowDestinationEditor.newStatusEditor(visibility: userPreferences.postVisibility))
} else { } else {
routerPath.presentedSheet = .newStatusEditor(visibility: userPreferences.postVisibility) routerPath.presentedSheet = .newStatusEditor(visibility: userPreferences.postVisibility)
} }

View file

@ -83,7 +83,7 @@ struct AccountDetailHeaderView: View {
let attachement = MediaAttachment.imageWith(url: account.header) let attachement = MediaAttachment.imageWith(url: account.header)
if ProcessInfo.processInfo.isMacCatalystApp { if ProcessInfo.processInfo.isMacCatalystApp {
openWindow(value: WindowDestination.mediaViewer(attachments: [attachement], openWindow(value: WindowDestinationMedia.mediaViewer(attachments: [attachement],
selectedAttachment: attachement)) selectedAttachment: attachement))
} else { } else {
quickLook.prepareFor(selectedMediaAttachment: attachement, mediaAttachments: [attachement]) quickLook.prepareFor(selectedMediaAttachment: attachement, mediaAttachments: [attachement])
@ -118,7 +118,7 @@ struct AccountDetailHeaderView: View {
} }
let attachement = MediaAttachment.imageWith(url: account.avatar) let attachement = MediaAttachment.imageWith(url: account.avatar)
if ProcessInfo.processInfo.isMacCatalystApp { if ProcessInfo.processInfo.isMacCatalystApp {
openWindow(value: WindowDestination.mediaViewer(attachments: [attachement], openWindow(value: WindowDestinationMedia.mediaViewer(attachments: [attachement],
selectedAttachment: attachement)) selectedAttachment: attachement))
} else { } else {
quickLook.prepareFor(selectedMediaAttachment: attachement, mediaAttachments: [attachement]) quickLook.prepareFor(selectedMediaAttachment: attachement, mediaAttachments: [attachement])

View file

@ -202,7 +202,7 @@ struct ConversationMessageView: View {
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { .onTapGesture {
if ProcessInfo.processInfo.isMacCatalystApp { if ProcessInfo.processInfo.isMacCatalystApp {
openWindow(value: WindowDestination.mediaViewer(attachments: [attachement], openWindow(value: WindowDestinationMedia.mediaViewer(attachments: [attachement],
selectedAttachment: attachement)) selectedAttachment: attachement))
} else { } else {
quickLook.prepareFor(selectedMediaAttachment: attachement, mediaAttachments: [attachement]) quickLook.prepareFor(selectedMediaAttachment: attachement, mediaAttachments: [attachement])

View file

@ -27,7 +27,7 @@ public struct StatusEditorToolbarItem: ToolbarContent {
Button { Button {
Task { @MainActor in Task { @MainActor in
if ProcessInfo.processInfo.isMacCatalystApp { if ProcessInfo.processInfo.isMacCatalystApp {
openWindow(value: WindowDestination.newStatusEditor(visibility: visibility)) openWindow(value: WindowDestinationEditor.newStatusEditor(visibility: visibility))
} else { } else {
routerPath.presentedSheet = .newStatusEditor(visibility: visibility) routerPath.presentedSheet = .newStatusEditor(visibility: visibility)
HapticManager.shared.fireHaptic(.buttonPress) HapticManager.shared.fireHaptic(.buttonPress)

View file

@ -25,14 +25,17 @@ public enum RouterDestination: Hashable {
case tagsList(tags: [Tag]) case tagsList(tags: [Tag])
} }
public enum WindowDestination: Hashable, Codable { public enum WindowDestinationEditor: Hashable, Codable {
case newStatusEditor(visibility: Models.Visibility) case newStatusEditor(visibility: Models.Visibility)
case mediaViewer(attachments: [MediaAttachment], selectedAttachment: MediaAttachment)
case editStatusEditor(status: Status) case editStatusEditor(status: Status)
case replyToStatusEditor(status: Status) case replyToStatusEditor(status: Status)
case quoteStatusEditor(status: Status) case quoteStatusEditor(status: Status)
} }
public enum WindowDestinationMedia: Hashable, Codable {
case mediaViewer(attachments: [MediaAttachment], selectedAttachment: MediaAttachment)
}
public enum SheetDestination: Identifiable { public enum SheetDestination: Identifiable {
case newStatusEditor(visibility: Models.Visibility) case newStatusEditor(visibility: Models.Visibility)
case editStatusEditor(status: Status) case editStatusEditor(status: Status)

View file

@ -89,7 +89,7 @@ public struct ExploreView: View {
SoundEffectManager.shared.playSound(.refresh) SoundEffectManager.shared.playSound(.refresh)
} }
} }
.listStyle(.grouped) .listStyle(.plain)
.scrollContentBackground(.hidden) .scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor) .background(theme.secondaryBackgroundColor)
.navigationTitle("explore.navigation-title") .navigationTitle("explore.navigation-title")

View file

@ -7,6 +7,7 @@ public struct MediaUIView: View, @unchecked Sendable {
private let data: [DisplayData] private let data: [DisplayData]
private let initialItem: DisplayData? private let initialItem: DisplayData?
@State private var scrolledItem: DisplayData? @State private var scrolledItem: DisplayData?
@FocusState private var isFocused: Bool
public var body: some View { public var body: some View {
NavigationStack { NavigationStack {
@ -20,6 +21,17 @@ public struct MediaUIView: View, @unchecked Sendable {
} }
.scrollTargetLayout() .scrollTargetLayout()
} }
.focusable()
.focused($isFocused)
.focusEffectDisabled()
.onKeyPress(.leftArrow, action: {
scrollToPrevious()
return .handled
})
.onKeyPress(.rightArrow, action: {
scrollToNext()
return .handled
})
.scrollTargetBehavior(.viewAligned) .scrollTargetBehavior(.viewAligned)
.scrollPosition(id: $scrolledItem) .scrollPosition(id: $scrolledItem)
.toolbar { .toolbar {
@ -30,6 +42,7 @@ public struct MediaUIView: View, @unchecked Sendable {
.onAppear { .onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
scrolledItem = initialItem scrolledItem = initialItem
isFocused = true
} }
} }
} }
@ -39,6 +52,22 @@ public struct MediaUIView: View, @unchecked Sendable {
data = attachments.compactMap { DisplayData(from: $0) } data = attachments.compactMap { DisplayData(from: $0) }
initialItem = DisplayData(from: selectedAttachment) initialItem = DisplayData(from: selectedAttachment)
} }
private func scrollToPrevious() {
if let scrolledItem, let index = data.firstIndex(of: scrolledItem), index > 0 {
withAnimation {
self.scrolledItem = data[index - 1]
}
}
}
private func scrollToNext() {
if let scrolledItem, let index = data.firstIndex(of: scrolledItem), index < data.count - 1{
withAnimation {
self.scrolledItem = data[index + 1]
}
}
}
} }
private struct MediaToolBar: ToolbarContent { private struct MediaToolBar: ToolbarContent {

View file

@ -210,7 +210,7 @@ public struct StatusRowView: View {
HapticManager.shared.fireHaptic(.notification(.success)) HapticManager.shared.fireHaptic(.notification(.success))
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: WindowDestinationMedia.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

@ -206,7 +206,7 @@ struct StatusRowActionsView: View {
case .respond: case .respond:
SoundEffectManager.shared.playSound(.share) SoundEffectManager.shared.playSound(.share)
if ProcessInfo.processInfo.isMacCatalystApp { if ProcessInfo.processInfo.isMacCatalystApp {
openWindow(value: WindowDestination.replyToStatusEditor(status: viewModel.localStatus ?? viewModel.status)) openWindow(value: WindowDestinationEditor.replyToStatusEditor(status: viewModel.localStatus ?? viewModel.status))
} else { } else {
viewModel.routerPath.presentedSheet = .replyToStatusEditor(status: viewModel.localStatus ?? viewModel.status) viewModel.routerPath.presentedSheet = .replyToStatusEditor(status: viewModel.localStatus ?? viewModel.status)
} }

View file

@ -54,7 +54,7 @@ struct StatusRowContextMenu: View {
} }
Button { Button {
if ProcessInfo.processInfo.isMacCatalystApp { if ProcessInfo.processInfo.isMacCatalystApp {
openWindow(value: WindowDestination.replyToStatusEditor(status: viewModel.status)) openWindow(value: WindowDestinationEditor.replyToStatusEditor(status: viewModel.status))
} else { } else {
viewModel.routerPath.presentedSheet = .replyToStatusEditor(status: viewModel.status) viewModel.routerPath.presentedSheet = .replyToStatusEditor(status: viewModel.status)
} }
@ -63,7 +63,7 @@ struct StatusRowContextMenu: View {
} }
Button { Button {
if ProcessInfo.processInfo.isMacCatalystApp { if ProcessInfo.processInfo.isMacCatalystApp {
openWindow(value: WindowDestination.quoteStatusEditor(status: viewModel.status)) openWindow(value: WindowDestinationEditor.quoteStatusEditor(status: viewModel.status))
} else { } else {
viewModel.routerPath.presentedSheet = .quoteStatusEditor(status: viewModel.status) viewModel.routerPath.presentedSheet = .quoteStatusEditor(status: viewModel.status)
} }
@ -164,7 +164,7 @@ struct StatusRowContextMenu: View {
if currentInstance.isEditSupported { if currentInstance.isEditSupported {
Button { Button {
if ProcessInfo.processInfo.isMacCatalystApp { if ProcessInfo.processInfo.isMacCatalystApp {
openWindow(value: WindowDestination.editStatusEditor(status: viewModel.status.reblogAsAsStatus ?? viewModel.status)) openWindow(value: WindowDestinationEditor.editStatusEditor(status: viewModel.status.reblogAsAsStatus ?? viewModel.status))
} else { } else {
viewModel.routerPath.presentedSheet = .editStatusEditor(status: viewModel.status.reblogAsAsStatus ?? viewModel.status) viewModel.routerPath.presentedSheet = .editStatusEditor(status: viewModel.status.reblogAsAsStatus ?? viewModel.status)
} }

View file

@ -116,7 +116,7 @@ public struct StatusRowMediaPreviewView: View {
private func tabAction(for index: Int) { private func tabAction(for index: Int) {
if ProcessInfo.processInfo.isMacCatalystApp { if ProcessInfo.processInfo.isMacCatalystApp {
openWindow( openWindow(
value: WindowDestination.mediaViewer( value: WindowDestinationMedia.mediaViewer(
attachments: attachments, attachments: attachments,
selectedAttachment: attachments[index] selectedAttachment: attachments[index]
) )

View file

@ -22,7 +22,7 @@ let package = Package(
.package(name: "Status", path: "../Status"), .package(name: "Status", path: "../Status"),
.package(name: "DesignSystem", path: "../DesignSystem"), .package(name: "DesignSystem", path: "../DesignSystem"),
.package(url: "https://github.com/siteline/SwiftUI-Introspect.git", from: "1.0.0"), .package(url: "https://github.com/siteline/SwiftUI-Introspect.git", from: "1.0.0"),
.package(url: "https://github.com/mergesort/Bodega", from: "2.0.2"), .package(url: "https://github.com/mergesort/Bodega", exact: "2.1.0"),
], ],
targets: [ targets: [
.target( .target(