Compare commits

...

9 Commits

18 changed files with 148 additions and 53 deletions

View File

@ -37,7 +37,7 @@ class ToggleFollowHashtagService {
config.systemImageName = "checkmark" config.systemImageName = "checkmark"
config.dismissAutomaticallyAfter = 2 config.dismissAutomaticallyAfter = 2
} catch { } catch {
config = ToastConfiguration(from: error, with: "Error Unfollowing Hashtag", in: presenter) { [unowned self] toast in config = ToastConfiguration(from: error, with: "Error Unfollowing Hashtag", in: presenter) { toast in
toast.dismissToast(animated: true) toast.dismissToast(animated: true)
await self.toggleFollow() await self.toggleFollow()
} }
@ -54,7 +54,7 @@ class ToggleFollowHashtagService {
config.systemImageName = "checkmark" config.systemImageName = "checkmark"
config.dismissAutomaticallyAfter = 2 config.dismissAutomaticallyAfter = 2
} catch { } catch {
config = ToastConfiguration(from: error, with: "Error Following Hashtag", in: presenter) { [unowned self] toast in config = ToastConfiguration(from: error, with: "Error Following Hashtag", in: presenter) { toast in
toast.dismissToast(animated: true) toast.dismissToast(animated: true)
await self.toggleFollow() await self.toggleFollow()
} }

View File

@ -39,7 +39,9 @@ class OpenInSafariActivity: UIActivity {
static func completionHandler(navigator: TuskerNavigationDelegate, url: URL) -> UIActivityViewController.CompletionWithItemsHandler { static func completionHandler(navigator: TuskerNavigationDelegate, url: URL) -> UIActivityViewController.CompletionWithItemsHandler {
return { (activityType, _, _, _) in return { (activityType, _, _, _) in
if activityType == .openInSafari { if activityType == .openInSafari {
navigator.show(SFSafariViewController(url: url)) let vc = SFSafariViewController(url: url)
vc.preferredControlTintColor = Preferences.shared.accentColor.color
navigator.show(vc)
} }
} }
} }

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -22,10 +22,10 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="a8U-KI-8PM"> <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="a8U-KI-8PM">
<rect key="frame" x="0.0" y="44" width="414" height="818"/> <rect key="frame" x="0.0" y="48" width="414" height="814"/>
<subviews> <subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" verticalHuggingPriority="249" translatesAutoresizingMaskIntoConstraints="NO" id="uQy-Yw-Dba"> <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" verticalHuggingPriority="249" translatesAutoresizingMaskIntoConstraints="NO" id="uQy-Yw-Dba">
<rect key="frame" x="0.0" y="0.0" width="414" height="722"/> <rect key="frame" x="0.0" y="0.0" width="414" height="718"/>
<subviews> <subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" verticalHuggingPriority="249" scrollEnabled="NO" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="hxN-7J-Usc"> <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" verticalHuggingPriority="249" scrollEnabled="NO" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="hxN-7J-Usc">
<rect key="frame" x="0.0" y="0.0" width="414" height="166.5"/> <rect key="frame" x="0.0" y="0.0" width="414" height="166.5"/>
@ -46,11 +46,12 @@
<viewLayoutGuide key="frameLayoutGuide" id="Rgd-t7-8QN"/> <viewLayoutGuide key="frameLayoutGuide" id="Rgd-t7-8QN"/>
</scrollView> </scrollView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ofm-5l-nAp"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ofm-5l-nAp">
<rect key="frame" x="52" y="730" width="310.5" height="50"/> <rect key="frame" x="52" y="726" width="310.5" height="50"/>
<color key="backgroundColor" systemColor="systemBlueColor"/> <color key="backgroundColor" systemColor="tintColor"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="50" id="jHf-W0-qQn"/> <constraint firstAttribute="height" constant="50" id="jHf-W0-qQn"/>
</constraints> </constraints>
<color key="tintColor" systemColor="tintColor"/>
<state key="normal" title="Send Report"> <state key="normal" title="Send Report">
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state> </state>
@ -62,7 +63,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="JiJ-Ng-jOz"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="JiJ-Ng-jOz">
<rect key="frame" x="168.5" y="788" width="77" height="30"/> <rect key="frame" x="168.5" y="784" width="77" height="30"/>
<state key="normal" title="Don't Send"/> <state key="normal" title="Don't Send"/>
<connections> <connections>
<action selector="cancelPressed:" destination="-1" eventType="touchUpInside" id="o4R-0Q-STS"/> <action selector="cancelPressed:" destination="-1" eventType="touchUpInside" id="o4R-0Q-STS"/>
@ -87,12 +88,12 @@
</objects> </objects>
<resources> <resources>
<systemColor name="labelColor"> <systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor> </systemColor>
<systemColor name="systemBackgroundColor"> <systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor> </systemColor>
<systemColor name="systemBlueColor"> <systemColor name="tintColor">
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor> </systemColor>
</resources> </resources>

View File

@ -49,9 +49,9 @@ struct PinnedTimelinesView: View {
.onMove { indices, newOffset in .onMove { indices, newOffset in
pinnedTimelines.move(fromOffsets: indices, toOffset: newOffset) pinnedTimelines.move(fromOffsets: indices, toOffset: newOffset)
} }
.onDelete { indices in .onDelete(perform: pinnedTimelines.count == 1 ? nil : { indices in
pinnedTimelines.remove(atOffsets: indices) pinnedTimelines.remove(atOffsets: indices)
} })
Menu { Menu {
ForEach([Timeline.home, .public(local: true), .public(local: false)], id: \.id) { timeline in ForEach([Timeline.home, .public(local: true), .public(local: false)], id: \.id) { timeline in

View File

@ -75,7 +75,9 @@ class TrendingLinksViewController: EnhancedTableViewController {
return nil return nil
} }
return UIContextMenuConfiguration(identifier: nil) { return UIContextMenuConfiguration(identifier: nil) {
return SFSafariViewController(url: url) let vc = SFSafariViewController(url: url)
vc.preferredControlTintColor = Preferences.shared.accentColor.color
return vc
} actionProvider: { _ in } actionProvider: { _ in
return UIMenu(children: self.actionsForTrendingLink(card: item.card)) return UIMenu(children: self.actionsForTrendingLink(card: item.card))
} }

View File

@ -60,7 +60,11 @@ class TrendingStatusesViewController: UIViewController {
} }
return config return config
} }
let layout = UICollectionViewCompositionalLayout.list(using: config) let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
section.contentInsetsReference = .readableContent
return section
}
view = UICollectionView(frame: .zero, collectionViewLayout: layout) view = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.delegate = self collectionView.delegate = self
collectionView.dragDelegate = self collectionView.dragDelegate = self

View File

@ -10,19 +10,25 @@ import UIKit
class FastSwitchingAccountView: UIView { class FastSwitchingAccountView: UIView {
private static let selectedColor = UIColor { (traits) in private lazy var selectedColor = UIColor { [unowned self] (traits) in
if traits.userInterfaceStyle == .dark { var hue: CGFloat = 0
return UIColor(hue: 211 / 360, saturation: 85 / 100, brightness: 100 / 100, alpha: 1) var saturation: CGFloat = 0
} else { var brightness: CGFloat = 0
return UIColor(hue: 211 / 360, saturation: 70 / 100, brightness: 100 / 100, alpha: 1) var alpha: CGFloat = 0
} self.tintColor.resolvedColor(with: traits).getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
brightness = min(1, brightness + 0.4)
saturation = max(0, saturation - 0.3)
return UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha)
} }
private static let currentColor = UIColor { (traits) in private lazy var currentColor = UIColor { [unowned self] (traits) in
if traits.userInterfaceStyle == .dark { var hue: CGFloat = 0
return UIColor(hue: 211 / 360, saturation: 85 / 100, brightness: 85 / 100, alpha: 1) var saturation: CGFloat = 0
} else { var brightness: CGFloat = 0
return UIColor(hue: 211 / 360, saturation: 50 / 100, brightness: 100 / 100, alpha: 1) var alpha: CGFloat = 0
} self.tintColor.resolvedColor(with: traits).getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
brightness = min(1, brightness + 0.3)
saturation = max(0, saturation - 0.2)
return UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha)
} }
var isSelected = false { var isSelected = false {
didSet { didSet {
@ -139,9 +145,9 @@ class FastSwitchingAccountView: UIView {
private func updateLabelColors() { private func updateLabelColors() {
let color: UIColor let color: UIColor
if isSelected { if isSelected {
color = FastSwitchingAccountView.selectedColor color = selectedColor
} else if isCurrent { } else if isCurrent {
color = FastSwitchingAccountView.currentColor color = currentColor
} else { } else {
color = .white color = .white
} }

View File

@ -80,7 +80,15 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
} }
return config return config
} }
let layout = UICollectionViewCompositionalLayout.list(using: config) let layout = UICollectionViewCompositionalLayout { [unowned self] sectionIndex, environment in
if case .header = dataSource.sectionIdentifier(for: sectionIndex) {
return .list(using: .init(appearance: .plain), layoutEnvironment: environment)
} else {
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
section.contentInsetsReference = .readableContent
return section
}
}
view = UICollectionView(frame: .zero, collectionViewLayout: layout) view = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.delegate = self collectionView.delegate = self
collectionView.dragDelegate = self collectionView.dragDelegate = self

View File

@ -278,7 +278,9 @@ extension SearchViewController: UICollectionViewDelegate {
return nil return nil
} }
return UIContextMenuConfiguration { return UIContextMenuConfiguration {
SFSafariViewController(url: url) let vc = SFSafariViewController(url: url)
vc.preferredControlTintColor = Preferences.shared.accentColor.color
return vc
} actionProvider: { _ in } actionProvider: { _ in
UIMenu(children: self.actionsForTrendingLink(card: card)) UIMenu(children: self.actionsForTrendingLink(card: card))
} }

View File

@ -9,6 +9,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import Combine import Combine
import Sentry
class TimelineViewController: UIViewController, TimelineLikeCollectionViewController, CollectionViewController, RefreshableViewController { class TimelineViewController: UIViewController, TimelineLikeCollectionViewController, CollectionViewController, RefreshableViewController {
let timeline: Timeline let timeline: Timeline
@ -86,7 +87,12 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
} }
return config return config
} }
let layout = UICollectionViewCompositionalLayout.list(using: config) // just setting layout.configuration.contentInsetsReference doesn't work with UICollectionViewCompositionalLayout.list
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
section.contentInsetsReference = .readableContent
return section
}
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.delegate = self collectionView.delegate = self
collectionView.dragDelegate = self collectionView.dragDelegate = self
@ -335,7 +341,7 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
} }
loadViewIfNeeded() loadViewIfNeeded()
var loaded = false var loaded = false
await controller.restoreInitial { await controller.restoreInitial { @MainActor in
let hasStatusesToRestore = await loadStatusesToRestore(position: position) let hasStatusesToRestore = await loadStatusesToRestore(position: position)
if hasStatusesToRestore { if hasStatusesToRestore {
applyItemsToRestore(position: position) applyItemsToRestore(position: position)
@ -351,26 +357,45 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
guard !unloaded.isEmpty else { guard !unloaded.isEmpty else {
return true return true
} }
let statuses = await withTaskGroup(of: Status?.self) { group -> [Status] in let results = await withTaskGroup(of: (String, Result<Status, Swift.Error>).self) { group -> [(String, Result<Status, Swift.Error>)] in
for id in unloaded { for id in unloaded {
group.addTask { group.addTask {
do { do {
let (status, _) = try await self.mastodonController.run(Client.getStatus(id: id)) let (status, _) = try await self.mastodonController.run(Client.getStatus(id: id))
return status return (id, .success(status))
} catch { } catch {
print(error) return (id, .failure(error))
return nil
} }
} }
} }
return await group.reduce(into: []) { partialResult, status in return await group.reduce(into: []) { partialResult, result in
if let status { partialResult.append(result)
partialResult.append(status) }
} }
var statuses = [Status]()
for (id, result) in results {
switch result {
case .success(let status):
statuses.append(status)
case .failure(let error):
let crumb = Breadcrumb(level: .error, category: "TimelineViewController")
crumb.message = "Error loading status"
crumb.data = [
"error": String(describing: error),
"id": id
]
SentrySDK.addBreadcrumb(crumb: crumb)
} }
} }
await mastodonController.persistentContainer.addAll(statuses: statuses, in: mastodonController.persistentContainer.viewContext) await mastodonController.persistentContainer.addAll(statuses: statuses, in: mastodonController.persistentContainer.viewContext)
let crumb = Breadcrumb(level: .info, category: "TimelineViewController")
crumb.message = "Original position statusIDs"
crumb.data = [
"statusIDs": position.statusIDs,
]
SentrySDK.addBreadcrumb(crumb: crumb)
// update the timeline position in case some statuses couldn't be loaded // update the timeline position in case some statuses couldn't be loaded
if let center = position.centerStatusID { if let center = position.centerStatusID {
let nearestLoadedStatusToCenter = position.statusIDs[position.statusIDs.firstIndex(of: center)!...].first(where: { id in let nearestLoadedStatusToCenter = position.statusIDs[position.statusIDs.firstIndex(of: center)!...].first(where: { id in
@ -383,9 +408,17 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
!unloaded.contains(id) || statuses.contains(where: { $0.id == id }) !unloaded.contains(id) || statuses.contains(where: { $0.id == id })
} }
let crumb2 = Breadcrumb(level: .info, category: "TimelineViewController")
crumb2.message = "Filtered position statusIDs"
crumb2.data = [
"statusIDs": position.statusIDs,
]
SentrySDK.addBreadcrumb(crumb: crumb2)
return !position.statusIDs.isEmpty return !position.statusIDs.isEmpty
} }
@MainActor
private func applyItemsToRestore(position: TimelinePosition) { private func applyItemsToRestore(position: TimelinePosition) {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>() var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.statuses]) snapshot.appendSections([.statuses])
@ -393,6 +426,12 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
let centerStatusID = position.centerStatusID let centerStatusID = position.centerStatusID
let items = position.statusIDs.map { Item.status(id: $0, collapseState: .unknown, filterState: .unknown) } let items = position.statusIDs.map { Item.status(id: $0, collapseState: .unknown, filterState: .unknown) }
snapshot.appendItems(items, toSection: .statuses) snapshot.appendItems(items, toSection: .statuses)
let crumb = Breadcrumb(level: .info, category: "TimelineViewController")
crumb.message = "Restoring statuses"
crumb.data = [
"statusIDs": position.statusIDs
]
SentrySDK.addBreadcrumb(crumb: crumb)
dataSource.apply(snapshot, animatingDifferences: false) { dataSource.apply(snapshot, animatingDifferences: false) {
if let centerStatusID, if let centerStatusID,
let index = statusIDs.firstIndex(of: centerStatusID), let index = statusIDs.firstIndex(of: centerStatusID),
@ -426,7 +465,12 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
private func filterResult(state: FilterState, statusID: String) -> (Filterer.Result, NSAttributedString?) { private func filterResult(state: FilterState, statusID: String) -> (Filterer.Result, NSAttributedString?) {
let status = { let status = {
let status = self.mastodonController.persistentContainer.status(for: statusID)! guard let status = self.mastodonController.persistentContainer.status(for: statusID) else {
let crumb = Breadcrumb(level: .fatal, category: "TimelineViewController")
crumb.message = "Looking up status \(statusID)"
SentrySDK.addBreadcrumb(crumb: crumb)
preconditionFailure("Missing status for filtering")
}
// if the status is a reblog of another one, filter based on that one // if the status is a reblog of another one, filter based on that one
if let reblogged = status.reblog { if let reblogged = status.reblog {
return (reblogged, true) return (reblogged, true)

View File

@ -70,8 +70,8 @@ extension MenuActionProvider {
accountID != ownAccount.id { accountID != ownAccount.id {
actionsSection.append(relationshipAction(fetchRelationship, accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.followAction(for: $0, mastodonController: $1) })) actionsSection.append(relationshipAction(fetchRelationship, accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.followAction(for: $0, mastodonController: $1) }))
actionsSection.append(UIDeferredMenuElement.uncached({ elementHandler in actionsSection.append(UIDeferredMenuElement.uncached({ elementHandler in
let listActions = mastodonController.lists.map { list in var listActions = mastodonController.lists.map { list in
UIAction(title: list.title, image: UIImage(systemName: "plus")) { [unowned self] _ in UIAction(title: list.title, image: UIImage(systemName: "list.bullet")) { [unowned self] _ in
let req = List.add(list, accounts: [accountID]) let req = List.add(list, accounts: [accountID])
mastodonController.run(req) { response in mastodonController.run(req) { response in
if case .failure(let error) = response { if case .failure(let error) = response {
@ -80,6 +80,21 @@ extension MenuActionProvider {
} }
} }
} }
listActions.append(UIAction(title: "New List…", image: UIImage(systemName: "plus"), handler: { [unowned self] _ in
Task { @MainActor in
let service = CreateListService(mastodonController: mastodonController, present: { [unowned self] in
self.navigationDelegate!.present($0, animated: true)
}) { list in
let req = List.add(list, accounts: [accountID])
mastodonController.run(req) { response in
if case .failure(let error) = response {
self.handleError(error, title: "Error Adding to List")
}
}
}
service.run()
}
}))
elementHandler([UIMenu(title: "Add to List", image: UIImage(systemName: "list.bullet"), children: listActions)]) elementHandler([UIMenu(title: "Add to List", image: UIImage(systemName: "list.bullet"), children: listActions)])
})) }))
suppressSection.append(relationshipAction(fetchRelationship, accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.blockAction(for: $0, mastodonController: $1) })) suppressSection.append(relationshipAction(fetchRelationship, accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.blockAction(for: $0, mastodonController: $1) }))

View File

@ -257,6 +257,7 @@ class UserActivityManager {
if let navigationController = mainViewController.getTabController(tab: .explore) as? UINavigationController, if let navigationController = mainViewController.getTabController(tab: .explore) as? UINavigationController,
let exploreController = navigationController.viewControllers.first as? ExploreViewController { let exploreController = navigationController.viewControllers.first as? ExploreViewController {
navigationController.popToRootViewController(animated: false) navigationController.popToRootViewController(animated: false)
exploreController.loadViewIfNeeded()
exploreController.searchController.isActive = true exploreController.searchController.isActive = true
exploreController.searchController.searchBar.becomeFirstResponder() exploreController.searchController.searchBar.becomeFirstResponder()
} }

View File

@ -50,7 +50,9 @@ extension TuskerNavigationDelegate {
url.scheme == "https" || url.scheme == "http" { url.scheme == "https" || url.scheme == "http" {
let config = SFSafariViewController.Configuration() let config = SFSafariViewController.Configuration()
config.entersReaderIfAvailable = Preferences.shared.inAppSafariAutomaticReaderMode config.entersReaderIfAvailable = Preferences.shared.inAppSafariAutomaticReaderMode
present(SFSafariViewController(url: url, configuration: config), animated: true) let vc = SFSafariViewController(url: url, configuration: config)
vc.preferredControlTintColor = Preferences.shared.accentColor.color
present(vc, animated: true)
} else if UIApplication.shared.canOpenURL(url) { } else if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:]) UIApplication.shared.open(url, options: [:])
} else { } else {

View File

@ -185,7 +185,9 @@ class ContentTextView: LinkTextView, BaseEmojiLabel {
} else if let tag = getHashtag(for: url, text: text) { } else if let tag = getHashtag(for: url, text: text) {
return HashtagTimelineViewController(for: tag, mastodonController: mastodonController!) return HashtagTimelineViewController(for: tag, mastodonController: mastodonController!)
} else if url.scheme == "https" || url.scheme == "http" { } else if url.scheme == "https" || url.scheme == "http" {
return SFSafariViewController(url: url) let vc = SFSafariViewController(url: url)
vc.preferredControlTintColor = Preferences.shared.accentColor.color
return vc
} else { } else {
return nil return nil
} }

View File

@ -56,7 +56,7 @@ class PollOptionView: UIView {
let fillView = UIView() let fillView = UIView()
fillView.translatesAutoresizingMaskIntoConstraints = false fillView.translatesAutoresizingMaskIntoConstraints = false
fillView.backgroundColor = tintColor.withAlphaComponent(0.6) fillView.backgroundColor = .tintColor.withAlphaComponent(0.6)
fillView.layer.zPosition = -1 fillView.layer.zPosition = -1
fillView.layer.cornerRadius = layer.cornerRadius fillView.layer.cornerRadius = layer.cornerRadius
addSubview(fillView) addSubview(fillView)

View File

@ -294,7 +294,7 @@ class BaseStatusTableViewCell: UITableViewCell {
} else { } else {
let view = UIView() let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = tintColor.withAlphaComponent(0.5) view.backgroundColor = .tintColor.withAlphaComponent(0.5)
view.layer.cornerRadius = 2.5 view.layer.cornerRadius = 2.5
view.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner] view.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
prevThreadLinkView = view prevThreadLinkView = view
@ -316,7 +316,7 @@ class BaseStatusTableViewCell: UITableViewCell {
} else { } else {
let view = UIView() let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = tintColor.withAlphaComponent(0.5) view.backgroundColor = .tintColor.withAlphaComponent(0.5)
view.layer.cornerRadius = 2.5 view.layer.cornerRadius = 2.5
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
nextThreadLinkView = view nextThreadLinkView = view

View File

@ -229,7 +229,9 @@ extension StatusCardView: UIContextMenuInteractionDelegate {
guard let card = card else { return nil } guard let card = card else { return nil }
return UIContextMenuConfiguration(identifier: nil) { return UIContextMenuConfiguration(identifier: nil) {
return SFSafariViewController(url: URL(card.url)!) let vc = SFSafariViewController(url: URL(card.url)!)
vc.preferredControlTintColor = Preferences.shared.accentColor.color
return vc
} actionProvider: { (_) in } actionProvider: { (_) in
let actions = self.actionProvider?.actionsForURL(URL(card.url)!, source: .view(self)) ?? [] let actions = self.actionProvider?.actionsForURL(URL(card.url)!, source: .view(self)) ?? []
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: actions) return UIMenu(title: "", image: nil, identifier: nil, options: [], children: actions)

View File

@ -71,11 +71,15 @@ extension ToastConfiguration {
event.tags!["error_type"] = "invalid_response" event.tags!["error_type"] = "invalid_response"
case .invalidModel(let error): case .invalidModel(let error):
event.tags!["error_type"] = "invalid_model" event.tags!["error_type"] = "invalid_model"
event.tags!["underlying_error"] = String(describing: error) event.extra = [
"underlying_error": String(describing: error)
]
case .mastodonError(let code, let error): case .mastodonError(let code, let error):
event.tags!["error_type"] = "mastodon_error" event.tags!["error_type"] = "mastodon_error"
event.tags!["response_code"] = "\(code)" event.tags!["response_code"] = "\(code)"
event.tags!["underlying_error"] = error event.extra = [
"underlying_error": String(describing: error)
]
default: default:
break break
} }