Compare commits
3 Commits
cc10a13785
...
68dbbca9b8
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 68dbbca9b8 | |
Shadowfacts | 7f1577f4a4 | |
Shadowfacts | e52f032d01 |
|
@ -18,6 +18,7 @@
|
||||||
04DACE8C212CB14B009840C4 /* MainTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */; };
|
04DACE8C212CB14B009840C4 /* MainTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */; };
|
||||||
04DACE8E212CC7CC009840C4 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DACE8D212CC7CC009840C4 /* ImageCache.swift */; };
|
04DACE8E212CC7CC009840C4 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DACE8D212CC7CC009840C4 /* ImageCache.swift */; };
|
||||||
04ED00B121481ED800567C53 /* SteppedProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04ED00B021481ED800567C53 /* SteppedProgressView.swift */; };
|
04ED00B121481ED800567C53 /* SteppedProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04ED00B021481ED800567C53 /* SteppedProgressView.swift */; };
|
||||||
|
D600613E25D07E170067FAD6 /* ProfileDirectoryFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D600613D25D07E170067FAD6 /* ProfileDirectoryFilterView.swift */; };
|
||||||
D6093F9B25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6093F9A25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift */; };
|
D6093F9B25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6093F9A25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift */; };
|
||||||
D6093FB025BE0B01004811E6 /* TrendingHashtagTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6093FAE25BE0B01004811E6 /* TrendingHashtagTableViewCell.swift */; };
|
D6093FB025BE0B01004811E6 /* TrendingHashtagTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6093FAE25BE0B01004811E6 /* TrendingHashtagTableViewCell.swift */; };
|
||||||
D6093FB125BE0B01004811E6 /* TrendingHashtagTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6093FAF25BE0B01004811E6 /* TrendingHashtagTableViewCell.xib */; };
|
D6093FB125BE0B01004811E6 /* TrendingHashtagTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6093FAF25BE0B01004811E6 /* TrendingHashtagTableViewCell.xib */; };
|
||||||
|
@ -282,7 +283,6 @@
|
||||||
D6DFC69E242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */; };
|
D6DFC69E242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */; };
|
||||||
D6DFC6A0242C4CCC00ACC392 /* WeakArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */; };
|
D6DFC6A0242C4CCC00ACC392 /* WeakArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */; };
|
||||||
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E0DC8D216EDF1E00369478 /* Previewing.swift */; };
|
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E0DC8D216EDF1E00369478 /* Previewing.swift */; };
|
||||||
D6E1EEF4285443EF00D20549 /* UIAction+Subtitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E1EEF3285443EF00D20549 /* UIAction+Subtitle.swift */; };
|
|
||||||
D6E343AB265AAD6B00C4AA01 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D6E343AA265AAD6B00C4AA01 /* Media.xcassets */; };
|
D6E343AB265AAD6B00C4AA01 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D6E343AA265AAD6B00C4AA01 /* Media.xcassets */; };
|
||||||
D6E343AD265AAD6B00C4AA01 /* ActionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E343AC265AAD6B00C4AA01 /* ActionViewController.swift */; };
|
D6E343AD265AAD6B00C4AA01 /* ActionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E343AC265AAD6B00C4AA01 /* ActionViewController.swift */; };
|
||||||
D6E343B0265AAD6B00C4AA01 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D6E343AE265AAD6B00C4AA01 /* MainInterface.storyboard */; };
|
D6E343B0265AAD6B00C4AA01 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D6E343AE265AAD6B00C4AA01 /* MainInterface.storyboard */; };
|
||||||
|
@ -366,6 +366,7 @@
|
||||||
04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabBarViewController.swift; sourceTree = "<group>"; };
|
04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabBarViewController.swift; sourceTree = "<group>"; };
|
||||||
04DACE8D212CC7CC009840C4 /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = "<group>"; };
|
04DACE8D212CC7CC009840C4 /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = "<group>"; };
|
||||||
04ED00B021481ED800567C53 /* SteppedProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SteppedProgressView.swift; sourceTree = "<group>"; };
|
04ED00B021481ED800567C53 /* SteppedProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SteppedProgressView.swift; sourceTree = "<group>"; };
|
||||||
|
D600613D25D07E170067FAD6 /* ProfileDirectoryFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileDirectoryFilterView.swift; sourceTree = "<group>"; };
|
||||||
D6093F9A25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagSearchResultsViewController.swift; sourceTree = "<group>"; };
|
D6093F9A25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagSearchResultsViewController.swift; sourceTree = "<group>"; };
|
||||||
D6093FAE25BE0B01004811E6 /* TrendingHashtagTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingHashtagTableViewCell.swift; sourceTree = "<group>"; };
|
D6093FAE25BE0B01004811E6 /* TrendingHashtagTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingHashtagTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
D6093FAF25BE0B01004811E6 /* TrendingHashtagTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TrendingHashtagTableViewCell.xib; sourceTree = "<group>"; };
|
D6093FAF25BE0B01004811E6 /* TrendingHashtagTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TrendingHashtagTableViewCell.xib; sourceTree = "<group>"; };
|
||||||
|
@ -634,7 +635,6 @@
|
||||||
D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackpadScrollGestureRecognizer.swift; sourceTree = "<group>"; };
|
D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackpadScrollGestureRecognizer.swift; sourceTree = "<group>"; };
|
||||||
D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakArray.swift; sourceTree = "<group>"; };
|
D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakArray.swift; sourceTree = "<group>"; };
|
||||||
D6E0DC8D216EDF1E00369478 /* Previewing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Previewing.swift; sourceTree = "<group>"; };
|
D6E0DC8D216EDF1E00369478 /* Previewing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Previewing.swift; sourceTree = "<group>"; };
|
||||||
D6E1EEF3285443EF00D20549 /* UIAction+Subtitle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAction+Subtitle.swift"; sourceTree = "<group>"; };
|
|
||||||
D6E343A8265AAD6B00C4AA01 /* OpenInTusker.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = OpenInTusker.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
D6E343A8265AAD6B00C4AA01 /* OpenInTusker.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = OpenInTusker.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
D6E343AA265AAD6B00C4AA01 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = "<group>"; };
|
D6E343AA265AAD6B00C4AA01 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = "<group>"; };
|
||||||
D6E343AC265AAD6B00C4AA01 /* ActionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionViewController.swift; sourceTree = "<group>"; };
|
D6E343AC265AAD6B00C4AA01 /* ActionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -806,6 +806,7 @@
|
||||||
D693A72925CF8C1E003A14E2 /* ProfileDirectoryViewController.swift */,
|
D693A72925CF8C1E003A14E2 /* ProfileDirectoryViewController.swift */,
|
||||||
D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */,
|
D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */,
|
||||||
D693A72E25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib */,
|
D693A72E25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib */,
|
||||||
|
D600613D25D07E170067FAD6 /* ProfileDirectoryFilterView.swift */,
|
||||||
);
|
);
|
||||||
path = Explore;
|
path = Explore;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1123,7 +1124,6 @@
|
||||||
D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */,
|
D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */,
|
||||||
D69693F32585941A00F4E116 /* UIWindowSceneDelegate+Close.swift */,
|
D69693F32585941A00F4E116 /* UIWindowSceneDelegate+Close.swift */,
|
||||||
D62E9984279CA23900C26176 /* URLSession+Development.swift */,
|
D62E9984279CA23900C26176 /* URLSession+Development.swift */,
|
||||||
D6E1EEF3285443EF00D20549 /* UIAction+Subtitle.swift */,
|
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1744,6 +1744,7 @@
|
||||||
D626493523BD94CE00612E6E /* CompositionAttachmentData.swift in Sources */,
|
D626493523BD94CE00612E6E /* CompositionAttachmentData.swift in Sources */,
|
||||||
D6531DF0246B867E000F9538 /* GifvAttachmentViewController.swift in Sources */,
|
D6531DF0246B867E000F9538 /* GifvAttachmentViewController.swift in Sources */,
|
||||||
D6285B5321EA708700FE4B39 /* StatusFormat.swift in Sources */,
|
D6285B5321EA708700FE4B39 /* StatusFormat.swift in Sources */,
|
||||||
|
D600613E25D07E170067FAD6 /* ProfileDirectoryFilterView.swift in Sources */,
|
||||||
D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */,
|
D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */,
|
||||||
0427033A22B31269000D31B6 /* AdvancedPrefsView.swift in Sources */,
|
0427033A22B31269000D31B6 /* AdvancedPrefsView.swift in Sources */,
|
||||||
D662AEEF263A3B880082A153 /* PollFinishedTableViewCell.swift in Sources */,
|
D662AEEF263A3B880082A153 /* PollFinishedTableViewCell.swift in Sources */,
|
||||||
|
@ -1764,7 +1765,6 @@
|
||||||
D64BC18F23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift in Sources */,
|
D64BC18F23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift in Sources */,
|
||||||
D62E9989279DB2D100C26176 /* InstanceFeatures.swift in Sources */,
|
D62E9989279DB2D100C26176 /* InstanceFeatures.swift in Sources */,
|
||||||
D693DE5923FE24310061E07D /* InteractivePushTransition.swift in Sources */,
|
D693DE5923FE24310061E07D /* InteractivePushTransition.swift in Sources */,
|
||||||
D6E1EEF4285443EF00D20549 /* UIAction+Subtitle.swift in Sources */,
|
|
||||||
D69693FA25859A8000F4E116 /* ComposeSceneDelegate.swift in Sources */,
|
D69693FA25859A8000F4E116 /* ComposeSceneDelegate.swift in Sources */,
|
||||||
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */,
|
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */,
|
||||||
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */,
|
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */,
|
||||||
|
@ -2205,7 +2205,8 @@
|
||||||
CURRENT_PROJECT_VERSION = 31;
|
CURRENT_PROJECT_VERSION = 31;
|
||||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||||
INFOPLIST_FILE = Tusker/Info.plist;
|
INFOPLIST_FILE = Tusker/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.3;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||||
|
"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.3;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
@ -2235,7 +2236,8 @@
|
||||||
CURRENT_PROJECT_VERSION = 31;
|
CURRENT_PROJECT_VERSION = 31;
|
||||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||||
INFOPLIST_FILE = Tusker/Info.plist;
|
INFOPLIST_FILE = Tusker/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.3;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||||
|
"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.3;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
|
|
@ -48,13 +48,13 @@ enum CompositionAttachmentData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getData(completion: @escaping (Result<(Data, UTType), Error>) -> Void) {
|
func getData(completion: @escaping (Result<(Data, String), Error>) -> Void) {
|
||||||
switch self {
|
switch self {
|
||||||
case let .image(image):
|
case let .image(image):
|
||||||
// Export as JPEG instead of PNG, otherweise photos straight from the camera are too large
|
// Export as JPEG instead of PNG, otherweise photos straight from the camera are too large
|
||||||
// for Mastodon in its default configuration (max of 10MB).
|
// for Mastodon in its default configuration (max of 10MB).
|
||||||
// The quality of 0.8 was chosen completely arbitrarily, it may need to be tuned in the future.
|
// The quality of 0.8 was chosen completely arbitrarily, it may need to be tuned in the future.
|
||||||
completion(.success((image.jpegData(compressionQuality: 0.8)!, .jpeg)))
|
completion(.success((image.jpegData(compressionQuality: 0.8)!, "image/jpeg")))
|
||||||
case let .asset(asset):
|
case let .asset(asset):
|
||||||
if asset.mediaType == .image {
|
if asset.mediaType == .image {
|
||||||
let options = PHImageRequestOptions()
|
let options = PHImageRequestOptions()
|
||||||
|
@ -68,19 +68,19 @@ enum CompositionAttachmentData {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let utType: UTType
|
let mimeType: String
|
||||||
if dataUTI == "public.heic" {
|
if dataUTI == "public.heic" {
|
||||||
// neither Mastodon nor Pleroma handles HEIC well, so convert to JPEG
|
// neither Mastodon nor Pleroma handles HEIC well, so convert to JPEG
|
||||||
let image = CIImage(data: data)!
|
let image = CIImage(data: data)!
|
||||||
let context = CIContext()
|
let context = CIContext()
|
||||||
let colorSpace = image.colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)!
|
let colorSpace = image.colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)!
|
||||||
data = context.jpegRepresentation(of: image, colorSpace: colorSpace, options: [:])!
|
data = context.jpegRepresentation(of: image, colorSpace: colorSpace, options: [:])!
|
||||||
utType = .jpeg
|
mimeType = "image/jpeg"
|
||||||
} else {
|
} else {
|
||||||
utType = UTType(dataUTI)!
|
mimeType = UTType(dataUTI)!.preferredMIMEType!
|
||||||
}
|
}
|
||||||
|
|
||||||
completion(.success((data, utType)))
|
completion(.success((data, mimeType)))
|
||||||
}
|
}
|
||||||
} else if asset.mediaType == .video {
|
} else if asset.mediaType == .video {
|
||||||
let options = PHVideoRequestOptions()
|
let options = PHVideoRequestOptions()
|
||||||
|
@ -109,11 +109,11 @@ enum CompositionAttachmentData {
|
||||||
|
|
||||||
case let .drawing(drawing):
|
case let .drawing(drawing):
|
||||||
let image = drawing.imageInLightMode(from: drawing.bounds, scale: 1)
|
let image = drawing.imageInLightMode(from: drawing.bounds, scale: 1)
|
||||||
completion(.success((image.pngData()!, .png)))
|
completion(.success((image.pngData()!, "image/png")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func exportVideoData(session: AVAssetExportSession, completion: @escaping (Result<(Data, UTType), Error>) -> Void) {
|
private static func exportVideoData(session: AVAssetExportSession, completion: @escaping (Result<(Data, String), Error>) -> Void) {
|
||||||
session.outputFileType = .mp4
|
session.outputFileType = .mp4
|
||||||
session.outputURL = FileManager.default.temporaryDirectory.appendingPathComponent("exported_video_\(UUID())").appendingPathExtension("mp4")
|
session.outputURL = FileManager.default.temporaryDirectory.appendingPathComponent("exported_video_\(UUID())").appendingPathExtension("mp4")
|
||||||
session.exportAsynchronously {
|
session.exportAsynchronously {
|
||||||
|
@ -123,7 +123,7 @@ enum CompositionAttachmentData {
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
let data = try Data(contentsOf: session.outputURL!)
|
let data = try Data(contentsOf: session.outputURL!)
|
||||||
completion(.success((data, .mpeg4Movie)))
|
completion(.success((data, "video/mp4")))
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(.videoExport(error)))
|
completion(.failure(.videoExport(error)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,7 +154,6 @@ extension ComposeAttachmentRow {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension View {
|
private extension View {
|
||||||
@available(iOS, obsoleted: 16.0)
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func contextMenu<M: View, P: View>(@ViewBuilder menuItems: () -> M, @ViewBuilder previewIfAvailable preview: () -> P) -> some View {
|
func contextMenu<M: View, P: View>(@ViewBuilder menuItems: () -> M, @ViewBuilder previewIfAvailable preview: () -> P) -> some View {
|
||||||
if #available(iOS 16.0, *) {
|
if #available(iOS 16.0, *) {
|
||||||
|
|
|
@ -50,7 +50,7 @@ struct ComposeAttachmentsList: View {
|
||||||
.disabled(!canAddAttachment)
|
.disabled(!canAddAttachment)
|
||||||
.foregroundColor(.blue)
|
.foregroundColor(.blue)
|
||||||
.frame(height: cellHeight / 2)
|
.frame(height: cellHeight / 2)
|
||||||
.sheetOrPopover(isPresented: $isShowingAssetPickerPopover, content: self.assetPickerPopover)
|
.popover(isPresented: $isShowingAssetPickerPopover, content: self.assetPickerPopover)
|
||||||
.listRowInsets(EdgeInsets(top: cellPadding / 2, leading: cellPadding / 2, bottom: cellPadding / 2, trailing: cellPadding / 2))
|
.listRowInsets(EdgeInsets(top: cellPadding / 2, leading: cellPadding / 2, bottom: cellPadding / 2, trailing: cellPadding / 2))
|
||||||
|
|
||||||
Button(action: self.createDrawing) {
|
Button(action: self.createDrawing) {
|
||||||
|
@ -134,21 +134,14 @@ struct ComposeAttachmentsList: View {
|
||||||
private func assetPickerPopover() -> some View {
|
private func assetPickerPopover() -> some View {
|
||||||
ComposeAssetPicker(draft: draft, delegate: uiState.delegate?.assetPickerDelegate)
|
ComposeAssetPicker(draft: draft, delegate: uiState.delegate?.assetPickerDelegate)
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
// on iPadOS 16, this is necessary to dismiss the popover when collapsing from regular -> compact size class
|
|
||||||
// otherwise, the popover isn't visible but it's still "presented", so the sheet can't be shown
|
|
||||||
self.isShowingAssetPickerPopover = false
|
self.isShowingAssetPickerPopover = false
|
||||||
}
|
}
|
||||||
// on iPadOS 16, this is necessary to show the dark color in the popover arrow
|
|
||||||
.background(Color(.systemBackground))
|
|
||||||
.environment(\.colorScheme, .dark)
|
.environment(\.colorScheme, .dark)
|
||||||
.edgesIgnoringSafeArea(.bottom)
|
.edgesIgnoringSafeArea(.bottom)
|
||||||
.withSheetDetentsIfAvailable()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addAttachment() {
|
private func addAttachment() {
|
||||||
if #available(iOS 16.0, *) {
|
if horizontalSizeClass == .regular {
|
||||||
isShowingAssetPickerPopover = true
|
|
||||||
} else if horizontalSizeClass == .regular {
|
|
||||||
isShowingAssetPickerPopover = true
|
isShowingAssetPickerPopover = true
|
||||||
} else {
|
} else {
|
||||||
uiState.delegate?.presentAssetPickerSheet()
|
uiState.delegate?.presentAssetPickerSheet()
|
||||||
|
@ -191,7 +184,6 @@ struct ComposeAttachmentsList: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate extension View {
|
fileprivate extension View {
|
||||||
@available(iOS, obsoleted: 15.0)
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func onDragWithPreviewIfAvailable<V>(_ data: @escaping () -> NSItemProvider, preview: () -> V) -> some View where V : View {
|
func onDragWithPreviewIfAvailable<V>(_ data: @escaping () -> NSItemProvider, preview: () -> V) -> some View where V : View {
|
||||||
if #available(iOS 15.0, *) {
|
if #available(iOS 15.0, *) {
|
||||||
|
@ -200,45 +192,6 @@ fileprivate extension View {
|
||||||
self.onDrag(data)
|
self.onDrag(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS, obsoleted: 16.0)
|
|
||||||
@ViewBuilder
|
|
||||||
func sheetOrPopover(isPresented: Binding<Bool>, @ViewBuilder content: @escaping () -> some View) -> some View {
|
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
self.modifier(SheetOrPopover(isPresented: isPresented, view: content))
|
|
||||||
} else {
|
|
||||||
self.popover(isPresented: isPresented, content: content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS, obsoleted: 16.0)
|
|
||||||
@ViewBuilder
|
|
||||||
func withSheetDetentsIfAvailable() -> some View {
|
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
self
|
|
||||||
.presentationDetents([.medium, .large])
|
|
||||||
.presentationDragIndicator(.visible)
|
|
||||||
} else {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 16.0, *)
|
|
||||||
struct SheetOrPopover<V: View>: ViewModifier {
|
|
||||||
@Binding var isPresented: Bool
|
|
||||||
@ViewBuilder let view: () -> V
|
|
||||||
|
|
||||||
@Environment(\.horizontalSizeClass) var sizeClass
|
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
|
||||||
let _ = print("isPresented: \(isPresented)")
|
|
||||||
if sizeClass == .compact {
|
|
||||||
content.sheet(isPresented: $isPresented, content: view)
|
|
||||||
} else {
|
|
||||||
content.popover(isPresented: $isPresented, content: view)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//struct ComposeAttachmentsList_Previews: PreviewProvider {
|
//struct ComposeAttachmentsList_Previews: PreviewProvider {
|
||||||
|
|
|
@ -12,7 +12,6 @@ protocol ComposeUIStateDelegate: AnyObject {
|
||||||
var assetPickerDelegate: AssetPickerViewControllerDelegate? { get }
|
var assetPickerDelegate: AssetPickerViewControllerDelegate? { get }
|
||||||
|
|
||||||
func dismissCompose(mode: ComposeUIState.DismissMode)
|
func dismissCompose(mode: ComposeUIState.DismissMode)
|
||||||
// @available(iOS, obsoleted: 16.0)
|
|
||||||
func presentAssetPickerSheet()
|
func presentAssetPickerSheet()
|
||||||
func presentComposeDrawing()
|
func presentComposeDrawing()
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ struct ComposeView: View {
|
||||||
ScrollView(.vertical) {
|
ScrollView(.vertical) {
|
||||||
mainStack(outerMinY: outer.frame(in: .global).minY)
|
mainStack(outerMinY: outer.frame(in: .global).minY)
|
||||||
}
|
}
|
||||||
.scrollDismissesKeyboardInteractivelyIfAvailable()
|
.scrollDismissesKeyboard(.interactively)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let poster = poster {
|
if let poster = poster {
|
||||||
|
@ -251,18 +251,6 @@ struct ComposeView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension View {
|
|
||||||
@available(iOS, obsoleted: 16.0)
|
|
||||||
@ViewBuilder
|
|
||||||
func scrollDismissesKeyboardInteractivelyIfAvailable() -> some View {
|
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
self.scrollDismissesKeyboard(.interactively)
|
|
||||||
} else {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//struct ComposeView_Previews: PreviewProvider {
|
//struct ComposeView_Previews: PreviewProvider {
|
||||||
// static var previews: some View {
|
// static var previews: some View {
|
||||||
// ComposeView()
|
// ComposeView()
|
||||||
|
|
|
@ -17,7 +17,7 @@ protocol LargeImageContentView: UIView {
|
||||||
func grayscaleStateChanged()
|
func grayscaleStateChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
class LargeImageImageContentView: UIImageView, LargeImageContentView {
|
class LargeImageImageContentView: UIImageView, LargeImageContentView, ImageAnalysisInteractionDelegate {
|
||||||
|
|
||||||
@available(iOS 16.0, *)
|
@available(iOS 16.0, *)
|
||||||
private static let analyzer = ImageAnalyzer()
|
private static let analyzer = ImageAnalyzer()
|
||||||
|
@ -76,10 +76,7 @@ class LargeImageImageContentView: UIImageView, LargeImageContentView {
|
||||||
self.image = image
|
self.image = image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 16.0, *)
|
|
||||||
extension LargeImageImageContentView: ImageAnalysisInteractionDelegate {
|
|
||||||
func presentingViewController(for interaction: ImageAnalysisInteraction) -> UIViewController? {
|
func presentingViewController(for interaction: ImageAnalysisInteraction) -> UIViewController? {
|
||||||
return owner
|
return owner
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,12 +255,9 @@ extension NotificationsTableViewController: MenuActionProvider {
|
||||||
|
|
||||||
extension NotificationsTableViewController: StatusTableViewCellDelegate {
|
extension NotificationsTableViewController: StatusTableViewCellDelegate {
|
||||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
} else {
|
|
||||||
cellHeightChanged()
|
cellHeightChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
|
extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
|
||||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||||
|
|
|
@ -265,12 +265,9 @@ extension ProfileStatusesViewController: TuskerNavigationDelegate {
|
||||||
|
|
||||||
extension ProfileStatusesViewController: StatusTableViewCellDelegate {
|
extension ProfileStatusesViewController: StatusTableViewCellDelegate {
|
||||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
} else {
|
|
||||||
cellHeightChanged()
|
cellHeightChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extension ProfileStatusesViewController: UITableViewDataSourcePrefetching, StatusTablePrefetching {
|
extension ProfileStatusesViewController: UITableViewDataSourcePrefetching, StatusTablePrefetching {
|
||||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||||
|
|
|
@ -290,12 +290,9 @@ extension TimelineTableViewController: TuskerNavigationDelegate {
|
||||||
|
|
||||||
extension TimelineTableViewController: StatusTableViewCellDelegate {
|
extension TimelineTableViewController: StatusTableViewCellDelegate {
|
||||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
} else {
|
|
||||||
cellHeightChanged()
|
cellHeightChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extension TimelineTableViewController: MenuActionProvider {
|
extension TimelineTableViewController: MenuActionProvider {
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,7 +163,6 @@ class DiffableTimelineLikeTableViewController<Section: Hashable & CaseIterable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS, deprecated: 16.0)
|
|
||||||
func cellHeightChanged() {
|
func cellHeightChanged() {
|
||||||
// causes the table view to recalculate the cell heights
|
// causes the table view to recalculate the cell heights
|
||||||
tableView.beginUpdates()
|
tableView.beginUpdates()
|
||||||
|
|
|
@ -20,23 +20,19 @@ class EnhancedNavigationViewController: UINavigationController {
|
||||||
override var viewControllers: [UIViewController] {
|
override var viewControllers: [UIViewController] {
|
||||||
didSet {
|
didSet {
|
||||||
poppedViewControllers = []
|
poppedViewControllers = []
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
// TODO: this for loop might not be necessary
|
|
||||||
for vc in viewControllers {
|
for vc in viewControllers {
|
||||||
configureNavItem(vc.navigationItem)
|
configureNavItem(vc.navigationItem)
|
||||||
}
|
}
|
||||||
updateTopNavItemState()
|
updateTopNavItemState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
self.interactivePushTransition = InteractivePushTransition(navigationController: self)
|
self.interactivePushTransition = InteractivePushTransition(navigationController: self)
|
||||||
|
|
||||||
if #available(iOS 16.0, *),
|
if let topViewController {
|
||||||
let topViewController {
|
|
||||||
configureNavItem(topViewController.navigationItem)
|
configureNavItem(topViewController.navigationItem)
|
||||||
updateTopNavItemState()
|
updateTopNavItemState()
|
||||||
}
|
}
|
||||||
|
@ -46,9 +42,7 @@ class EnhancedNavigationViewController: UINavigationController {
|
||||||
let popped = performAfterAnimating(block: {
|
let popped = performAfterAnimating(block: {
|
||||||
super.popViewController(animated: animated)
|
super.popViewController(animated: animated)
|
||||||
}, after: {
|
}, after: {
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
self.updateTopNavItemState()
|
self.updateTopNavItemState()
|
||||||
}
|
|
||||||
}, animated: animated)
|
}, animated: animated)
|
||||||
if let popped {
|
if let popped {
|
||||||
poppedViewControllers.insert(popped, at: 0)
|
poppedViewControllers.insert(popped, at: 0)
|
||||||
|
@ -60,9 +54,7 @@ class EnhancedNavigationViewController: UINavigationController {
|
||||||
let popped = performAfterAnimating(block: {
|
let popped = performAfterAnimating(block: {
|
||||||
super.popToRootViewController(animated: animated)
|
super.popToRootViewController(animated: animated)
|
||||||
}, after: {
|
}, after: {
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
self.updateTopNavItemState()
|
self.updateTopNavItemState()
|
||||||
}
|
|
||||||
}, animated: animated)
|
}, animated: animated)
|
||||||
if let popped {
|
if let popped {
|
||||||
poppedViewControllers = popped
|
poppedViewControllers = popped
|
||||||
|
@ -74,9 +66,7 @@ class EnhancedNavigationViewController: UINavigationController {
|
||||||
let popped = performAfterAnimating(block: {
|
let popped = performAfterAnimating(block: {
|
||||||
super.popToViewController(viewController, animated: animated)
|
super.popToViewController(viewController, animated: animated)
|
||||||
}, after: {
|
}, after: {
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
self.updateTopNavItemState()
|
self.updateTopNavItemState()
|
||||||
}
|
|
||||||
}, animated: animated)
|
}, animated: animated)
|
||||||
if let popped {
|
if let popped {
|
||||||
poppedViewControllers.insert(contentsOf: popped, at: 0)
|
poppedViewControllers.insert(contentsOf: popped, at: 0)
|
||||||
|
@ -91,16 +81,12 @@ class EnhancedNavigationViewController: UINavigationController {
|
||||||
self.poppedViewControllers = []
|
self.poppedViewControllers = []
|
||||||
}
|
}
|
||||||
|
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
configureNavItem(viewController.navigationItem)
|
configureNavItem(viewController.navigationItem)
|
||||||
}
|
|
||||||
|
|
||||||
super.pushViewController(viewController, animated: animated)
|
super.pushViewController(viewController, animated: animated)
|
||||||
|
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
updateTopNavItemState()
|
updateTopNavItemState()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func pushPoppedViewController() {
|
func pushPoppedViewController() {
|
||||||
guard !poppedViewControllers.isEmpty else {
|
guard !poppedViewControllers.isEmpty else {
|
||||||
|
@ -129,9 +115,7 @@ class EnhancedNavigationViewController: UINavigationController {
|
||||||
pushViewController(target, animated: true)
|
pushViewController(target, animated: true)
|
||||||
}, after: {
|
}, after: {
|
||||||
self.viewControllers.insert(contentsOf: toInsert, at: self.viewControllers.count - 1)
|
self.viewControllers.insert(contentsOf: toInsert, at: self.viewControllers.count - 1)
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
self.updateTopNavItemState()
|
self.updateTopNavItemState()
|
||||||
}
|
|
||||||
}, animated: true)
|
}, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +135,6 @@ class EnhancedNavigationViewController: UINavigationController {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS 16.0, *)
|
|
||||||
private func configureNavItem(_ navItem: UINavigationItem) {
|
private func configureNavItem(_ navItem: UINavigationItem) {
|
||||||
guard useBrowserStyleNavigation,
|
guard useBrowserStyleNavigation,
|
||||||
UIDevice.current.userInterfaceIdiom != .phone else {
|
UIDevice.current.userInterfaceIdiom != .phone else {
|
||||||
|
@ -200,7 +183,6 @@ class EnhancedNavigationViewController: UINavigationController {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS 16.0, *)
|
|
||||||
private func updateTopNavItemState() {
|
private func updateTopNavItemState() {
|
||||||
guard useBrowserStyleNavigation,
|
guard useBrowserStyleNavigation,
|
||||||
UIDevice.current.userInterfaceIdiom != .phone,
|
UIDevice.current.userInterfaceIdiom != .phone,
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import UniformTypeIdentifiers
|
|
||||||
|
|
||||||
class PostService: ObservableObject {
|
class PostService: ObservableObject {
|
||||||
private let mastodonController: MastodonController
|
private let mastodonController: MastodonController
|
||||||
|
@ -67,15 +66,15 @@ class PostService: ObservableObject {
|
||||||
attachments.reserveCapacity(draft.attachments.count)
|
attachments.reserveCapacity(draft.attachments.count)
|
||||||
for (index, attachment) in draft.attachments.enumerated() {
|
for (index, attachment) in draft.attachments.enumerated() {
|
||||||
let data: Data
|
let data: Data
|
||||||
let utType: UTType
|
let mimeType: String
|
||||||
do {
|
do {
|
||||||
(data, utType) = try await getData(for: attachment)
|
(data, mimeType) = try await getData(for: attachment)
|
||||||
currentStep += 1
|
currentStep += 1
|
||||||
} catch let error as CompositionAttachmentData.Error {
|
} catch let error as CompositionAttachmentData.Error {
|
||||||
throw Error.attachmentData(index: index, cause: error)
|
throw Error.attachmentData(index: index, cause: error)
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
let uploaded = try await uploadAttachment(data: data, utType: utType, description: attachment.attachmentDescription)
|
let uploaded = try await uploadAttachment(data: data, mimeType: mimeType, description: attachment.attachmentDescription)
|
||||||
attachments.append(uploaded)
|
attachments.append(uploaded)
|
||||||
currentStep += 1
|
currentStep += 1
|
||||||
} catch let error as Client.Error {
|
} catch let error as Client.Error {
|
||||||
|
@ -85,7 +84,7 @@ class PostService: ObservableObject {
|
||||||
return attachments
|
return attachments
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getData(for attachment: CompositionAttachment) async throws -> (Data, UTType) {
|
private func getData(for attachment: CompositionAttachment) async throws -> (Data, String) {
|
||||||
return try await withCheckedThrowingContinuation { continuation in
|
return try await withCheckedThrowingContinuation { continuation in
|
||||||
attachment.data.getData { result in
|
attachment.data.getData { result in
|
||||||
switch result {
|
switch result {
|
||||||
|
@ -98,8 +97,8 @@ class PostService: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func uploadAttachment(data: Data, utType: UTType, description: String?) async throws -> Attachment {
|
private func uploadAttachment(data: Data, mimeType: String, description: String?) async throws -> Attachment {
|
||||||
let formAttachment = FormAttachment(mimeType: utType.preferredMIMEType!, data: data, fileName: "file.\(utType.preferredFilenameExtension!)")
|
let formAttachment = FormAttachment(mimeType: mimeType, data: data, fileName: "file")
|
||||||
let req = Client.upload(attachment: formAttachment, description: description)
|
let req = Client.upload(attachment: formAttachment, description: description)
|
||||||
return try await mastodonController.run(req).0
|
return try await mastodonController.run(req).0
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,39 +195,16 @@ class ContentTextView: LinkTextView, BaseEmojiLabel {
|
||||||
|
|
||||||
func getLinkAtPoint(_ point: CGPoint) -> (URL, NSRange)? {
|
func getLinkAtPoint(_ point: CGPoint) -> (URL, NSRange)? {
|
||||||
let locationInTextContainer = CGPoint(x: point.x - textContainerInset.left, y: point.y - textContainerInset.top)
|
let locationInTextContainer = CGPoint(x: point.x - textContainerInset.left, y: point.y - textContainerInset.top)
|
||||||
if #available(iOS 16.0, *),
|
|
||||||
let textLayoutManager {
|
|
||||||
guard let fragment = textLayoutManager.textLayoutFragment(for: point),
|
|
||||||
let lineFragment = fragment.textLineFragments.first(where: { lineFragment in
|
|
||||||
lineFragment.typographicBounds.offsetBy(dx: fragment.layoutFragmentFrame.minX, dy: fragment.layoutFragmentFrame.minY).contains(point)
|
|
||||||
}) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
let charIndex = lineFragment.characterIndex(for: point)
|
|
||||||
|
|
||||||
var range = NSRange()
|
|
||||||
guard let link = lineFragment.attributedString.attribute(.link, at: charIndex, longestEffectiveRange: &range, in: lineFragment.attributedString.fullRange) as? URL else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// lineFragment.attributedString is the NSTextLayoutFragment's string, and so range is in its index space
|
|
||||||
// but we need to return a range in our whole attributedString's space, so convert it
|
|
||||||
let textLayoutFragmentStart = textLayoutManager.offset(from: textLayoutManager.documentRange.location, to: fragment.rangeInElement.location)
|
|
||||||
let rangeInSelf = NSRange(location: range.location + textLayoutFragmentStart, length: range.length)
|
|
||||||
return (link, rangeInSelf)
|
|
||||||
} else {
|
|
||||||
var partialFraction: CGFloat = 0
|
var partialFraction: CGFloat = 0
|
||||||
let characterIndex = layoutManager.characterIndex(for: locationInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: &partialFraction)
|
let characterIndex = layoutManager.characterIndex(for: locationInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: &partialFraction)
|
||||||
guard characterIndex < textStorage.length && partialFraction < 1 else {
|
if characterIndex < textStorage.length && partialFraction < 1 {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var range = NSRange()
|
var range = NSRange()
|
||||||
guard let link = textStorage.attribute(.link, at: characterIndex, longestEffectiveRange: &range, in: textStorage.fullRange) as? URL else {
|
if let link = textStorage.attribute(.link, at: characterIndex, longestEffectiveRange: &range, in: textStorage.fullRange) as? URL {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return (link, range)
|
return (link, range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func handleLinkTapped(url: URL, text: String) {
|
func handleLinkTapped(url: URL, text: String) {
|
||||||
if let mention = getMention(for: url, text: text) {
|
if let mention = getMention(for: url, text: text) {
|
||||||
|
@ -320,26 +297,9 @@ extension ContentTextView: UIContextMenuInteractionDelegate {
|
||||||
|
|
||||||
// Determine the line rects that the link takes up in the coordinate space of this view.
|
// Determine the line rects that the link takes up in the coordinate space of this view.
|
||||||
var rects = [CGRect]()
|
var rects = [CGRect]()
|
||||||
if #available(iOS 16.0, *),
|
|
||||||
let textLayoutManager,
|
|
||||||
let contentManager = textLayoutManager.textContentManager {
|
|
||||||
// convert from NSRange to NSTextRange
|
|
||||||
// i have no idea under what circumstances any of these calls could fail
|
|
||||||
guard let startLoc = contentManager.location(contentManager.documentRange.location, offsetBy: range.location),
|
|
||||||
let endLoc = contentManager.location(startLoc, offsetBy: range.length),
|
|
||||||
let textRange = NSTextRange(location: startLoc, end: endLoc) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// .standard because i have no idea what the difference is
|
|
||||||
textLayoutManager.enumerateTextSegments(in: textRange, type: .standard, options: []) { range, rect, float, textContainer in
|
|
||||||
rects.append(rect)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
layoutManager.enumerateEnclosingRects(forGlyphRange: range, withinSelectedGlyphRange: NSRange(location: NSNotFound, length: 0), in: textContainer) { (rect, stop) in
|
layoutManager.enumerateEnclosingRects(forGlyphRange: range, withinSelectedGlyphRange: NSRange(location: NSNotFound, length: 0), in: textContainer) { (rect, stop) in
|
||||||
rects.append(rect)
|
rects.append(rect)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Try to create a snapshot view of this view to disply as the preview.
|
// Try to create a snapshot view of this view to disply as the preview.
|
||||||
// If a snapshot view cannot be created, we bail and use the system-provided preview.
|
// If a snapshot view cannot be created, we bail and use the system-provided preview.
|
||||||
|
|
|
@ -12,7 +12,6 @@ import Combine
|
||||||
import AVKit
|
import AVKit
|
||||||
|
|
||||||
protocol StatusTableViewCellDelegate: TuskerNavigationDelegate, MenuActionProvider {
|
protocol StatusTableViewCellDelegate: TuskerNavigationDelegate, MenuActionProvider {
|
||||||
// @available(iOS, obsoleted: 16.0)
|
|
||||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell)
|
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,12 +332,8 @@ class BaseStatusTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
@IBAction func collapseButtonPressed() {
|
@IBAction func collapseButtonPressed() {
|
||||||
setCollapsed(!collapsed, animated: true)
|
setCollapsed(!collapsed, animated: true)
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
invalidateIntrinsicContentSize()
|
|
||||||
} else {
|
|
||||||
delegate?.statusCellCollapsedStateChanged(self)
|
delegate?.statusCellCollapsedStateChanged(self)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func setCollapsed(_ collapsed: Bool, animated: Bool) {
|
func setCollapsed(_ collapsed: Bool, animated: Bool) {
|
||||||
self.collapsed = collapsed
|
self.collapsed = collapsed
|
||||||
|
|
Loading…
Reference in New Issue