Skip to content

Commit

Permalink
Various fixes to share as image + copy text
Browse files Browse the repository at this point in the history
  • Loading branch information
Dimillian committed Sep 27, 2024
1 parent 93beb2f commit 27b3856
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 91 deletions.
6 changes: 4 additions & 2 deletions Packages/StatusKit/Sources/StatusKit/Row/StatusRowView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public struct StatusRowView: View {
@Environment(Client.self) private var client

@State private var showSelectableText: Bool = false
@State private var isShareAsImageSheetPresented: Bool = false
@State private var isBlockConfirmationPresented = false

public enum Context { case timeline, detail }
Expand All @@ -33,7 +34,8 @@ public struct StatusRowView: View {
var contextMenu: some View {
StatusRowContextMenu(viewModel: viewModel,
showTextForSelection: $showSelectableText,
isBlockConfirmationPresented: $isBlockConfirmationPresented)
isBlockConfirmationPresented: $isBlockConfirmationPresented,
isShareAsImageSheetPresented: $isShareAsImageSheetPresented)
}

public var body: some View {
Expand Down Expand Up @@ -213,7 +215,7 @@ public struct StatusRowView: View {
}
.sheet(isPresented: $showSelectableText) {
let content = viewModel.status.reblog?.content.asSafeMarkdownAttributedString ?? viewModel.status.content.asSafeMarkdownAttributedString
SelectTextView(content: content)
StatusRowSelectableTextView(content: content)
}
.environment(
StatusDataControllerProvider.shared.dataController(for: viewModel.finalStatus,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ struct StatusRowActionsView: View {
@Environment(CurrentAccount.self) private var currentAccount
@Environment(StatusDataController.self) private var statusDataController
@Environment(UserPreferences.self) private var userPreferences
@Environment(Client.self) private var client
@Environment(SceneDelegate.self) private var sceneDelegate

@Environment(\.openWindow) private var openWindow
@Environment(\.isStatusFocused) private var isFocused
@Environment(\.horizontalSizeClass) var horizontalSizeClass

@State private var showTextForSelection: Bool = false
@State private var isShareAsImageSheetPresented: Bool = false

@Binding var isBlockConfirmationPresented: Bool

Expand Down Expand Up @@ -190,7 +193,8 @@ struct StatusRowActionsView: View {
Menu {
StatusRowContextMenu(viewModel: viewModel,
showTextForSelection: $showTextForSelection,
isBlockConfirmationPresented: $isBlockConfirmationPresented)
isBlockConfirmationPresented: $isBlockConfirmationPresented,
isShareAsImageSheetPresented: $isShareAsImageSheetPresented)
.onAppear {
Task {
await viewModel.loadAuthorRelationship()
Expand All @@ -215,7 +219,36 @@ struct StatusRowActionsView: View {
.fixedSize(horizontal: false, vertical: true)
.sheet(isPresented: $showTextForSelection) {
let content = viewModel.status.reblog?.content.asSafeMarkdownAttributedString ?? viewModel.status.content.asSafeMarkdownAttributedString
SelectTextView(content: content)
StatusRowSelectableTextView(content: content)
.tint(theme.tintColor)
}
.sheet(isPresented: $isShareAsImageSheetPresented) {
let view =
HStack {
StatusRowView(viewModel: viewModel, context: .timeline)
.padding(8)
}
.environment(\.isInCaptureMode, true)
.environment(RouterPath())
.environment(QuickLook.shared)
.environment(theme)
.environment(client)
.environment(sceneDelegate)
.environment(UserPreferences.shared)
.environment(CurrentAccount.shared)
.environment(CurrentInstance.shared)
.environment(statusDataController)
.preferredColorScheme(theme.selectedScheme == .dark ? .dark : .light)
.foregroundColor(theme.labelColor)
.background(theme.primaryBackgroundColor)
.frame(width: sceneDelegate.windowWidth - 12)
.tint(theme.tintColor)
let renderer = ImageRenderer(content: AnyView(view))
renderer.isOpaque = true
renderer.scale = 3.0
return StatusRowShareAsImageView(viewModel: viewModel,
renderer: renderer)
.tint(theme.tintColor)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import SwiftUI
public struct StatusRowCardView: View {
@Environment(\.openURL) private var openURL
@Environment(\.openWindow) private var openWindow
@Environment(\.isInCaptureMode) private var isInCaptureMode: Bool
@Environment(\.isCompact) private var isCompact: Bool

@Environment(Theme.self) private var theme
Expand Down Expand Up @@ -106,7 +105,7 @@ public struct StatusRowCardView: View {

@ViewBuilder
private func defaultLinkPreview(_ title: String, _ url: URL) -> some View {
if let imageURL = card.image, !isInCaptureMode {
if let imageURL = card.image {
DefaultPreviewImage(url: imageURL, originalWidth: card.width, originalHeight: card.height)
}

Expand Down Expand Up @@ -134,7 +133,7 @@ public struct StatusRowCardView: View {

private func compactLinkPreview(_ title: String, _ url: URL) -> some View {
HStack(alignment: .top) {
if let imageURL = card.image, !isInCaptureMode {
if let imageURL = card.image {
LazyResizableImage(url: imageURL) { state, _ in
if let image = state.image {
image
Expand Down Expand Up @@ -212,7 +211,7 @@ public struct StatusRowCardView: View {
private func iconLinkPreview(_ title: String, _ url: URL) -> some View {
// ..where the image is known to be a square icon
HStack {
if let imageURL = card.image, !isInCaptureMode {
if let imageURL = card.image {
LazyResizableImage(url: imageURL) { state, _ in
if let image = state.image {
image
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import SwiftUI

@MainActor
struct StatusRowContextMenu: View {
@Environment(\.displayScale) var displayScale
@Environment(\.openWindow) var openWindow

@Environment(Client.self) private var client
Expand All @@ -21,6 +20,7 @@ struct StatusRowContextMenu: View {
var viewModel: StatusRowViewModel
@Binding var showTextForSelection: Bool
@Binding var isBlockConfirmationPresented: Bool
@Binding var isShareAsImageSheetPresented: Bool

var boostLabel: some View {
if viewModel.status.visibility == .priv, viewModel.status.account.id == account.account?.id {
Expand Down Expand Up @@ -101,30 +101,7 @@ struct StatusRowContextMenu: View {
}

Button {
let view = HStack {
StatusRowView(viewModel: viewModel, context: .timeline)
.padding(16)
}
.environment(\.isInCaptureMode, true)
.environment(Theme.shared)
.environment(preferences)
.environment(account)
.environment(currentInstance)
.environment(SceneDelegate())
.environment(quickLook)
.environment(viewModel.client)
.environment(RouterPath())
.preferredColorScheme(Theme.shared.selectedScheme == .dark ? .dark : .light)
.foregroundColor(Theme.shared.labelColor)
.background(Theme.shared.primaryBackgroundColor)
.frame(width: sceneDelegate.windowWidth - 12)
.tint(Theme.shared.tintColor)
let renderer = ImageRenderer(content: view)
renderer.scale = displayScale
renderer.isOpaque = false
if let image = renderer.uiImage {
viewModel.routerPath.presentedSheet = .shareImage(image: image, status: viewModel.status)
}
isShareAsImageSheetPresented = true
} label: {
Label("status.action.share-image", systemImage: "photo")
}
Expand Down Expand Up @@ -283,60 +260,3 @@ struct StatusRowContextMenu: View {
}
}
}

struct ActivityView: UIViewControllerRepresentable {
let image: Image

func makeUIViewController(context _: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
UIActivityViewController(activityItems: [image], applicationActivities: nil)
}

func updateUIViewController(_: UIActivityViewController, context _: UIViewControllerRepresentableContext<ActivityView>) {}
}

struct SelectTextView: View {
@Environment(\.dismiss) private var dismiss
let content: AttributedString

var body: some View {
NavigationStack {
SelectableText(content: content)
.padding()
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
dismiss()
} label: {
Text("action.done").bold()
}
}
}
.background(Theme.shared.primaryBackgroundColor)
.navigationTitle("status.action.select-text")
.navigationBarTitleDisplayMode(.inline)
}
}
}

struct SelectableText: UIViewRepresentable {
let content: AttributedString

func makeUIView(context _: Context) -> UITextView {
let attributedText = NSMutableAttributedString(content)
attributedText.addAttribute(
.font,
value: Font.scaledBodyFont,
range: NSRange(location: 0, length: content.characters.count)
)

let textView = UITextView()
textView.isEditable = false
textView.attributedText = attributedText
textView.textColor = UIColor(Color.label)
textView.backgroundColor = UIColor(Theme.shared.primaryBackgroundColor)
return textView
}

func updateUIView(_: UITextView, context _: Context) {}
func makeCoordinator() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import SwiftUI

@MainActor
struct StatusRowHeaderView: View {
@Environment(\.isInCaptureMode) private var isInCaptureMode: Bool
@Environment(\.isStatusFocused) private var isFocused
@Environment(\.redactionReasons) private var redactionReasons

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import SwiftUI
import DesignSystem

struct StatusRowSelectableTextView: View {
@Environment(\.dismiss) private var dismiss
@Environment(Theme.self) private var theme

let content: AttributedString

var body: some View {
NavigationStack {
SelectableText(content: content)
.padding()
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
dismiss()
} label: {
Text("action.done").bold()
}
}
}
.navigationTitle("status.action.select-text")
.navigationBarTitleDisplayMode(.inline)
}
.presentationBackground(.ultraThinMaterial)
.presentationCornerRadius(16)
}
}

fileprivate struct SelectableText: UIViewRepresentable {
let content: AttributedString

func makeUIView(context _: Context) -> UITextView {
let attributedText = NSMutableAttributedString(content)
attributedText.addAttribute(
.font,
value: Font.scaledBodyFont,
range: NSRange(location: 0, length: content.characters.count)
)

let textView = UITextView()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.isEditable = false
textView.isScrollEnabled = true
textView.attributedText = attributedText
textView.textColor = UIColor(Color.label)
textView.select(textView)
textView.selectedRange = .init(location: 0, length: attributedText.string.utf8.count)
textView.backgroundColor = .clear
return textView
}

func updateUIView(_: UITextView, context _: Context) {}
func makeCoordinator() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import SwiftUI
import Env
import Network
import DesignSystem

struct StatusRowShareAsImageView: View {
@Environment(\.dismiss) private var dismiss
@Environment(Theme.self) private var theme

let viewModel: StatusRowViewModel
@StateObject var renderer: ImageRenderer<AnyView>

var rendererImage: Image {
Image(uiImage: renderer.uiImage ?? UIImage())
}

var body: some View {
NavigationStack {
Form {
Section {
Button {
viewModel.routerPath.presentedSheet = .shareImage(image: renderer.uiImage ?? UIImage(),
status: viewModel.status)
} label: {
Label("status.action.share-image", systemImage: "square.and.arrow.up")
}
}
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor.opacity(0.4))
#endif

Section {
rendererImage
.resizable()
.scaledToFit()
}
.listRowBackground(theme.primaryBackgroundColor)
}
.scrollContentBackground(.hidden)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
dismiss()
} label: {
Text("action.done")
.bold()
}
}
}
.navigationTitle("Share post as image")
.navigationBarTitleDisplayMode(.inline)
}
.presentationBackground(.ultraThinMaterial)
.presentationCornerRadius(16)
}
}

0 comments on commit 27b3856

Please sign in to comment.