Move InstanceFeatures to separate package

This commit is contained in:
Shadowfacts 2023-03-05 14:52:19 -05:00
parent 247bb31c56
commit 391ea1b46a
9 changed files with 129 additions and 48 deletions

9
Packages/InstanceFeatures/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc

View File

@ -0,0 +1,23 @@
{
"pins" : [
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system.git",
"state" : {
"revision" : "025bcb1165deab2e20d4eaba79967ce73013f496",
"version" : "1.2.1"
}
},
{
"identity" : "swift-url",
"kind" : "remoteSourceControl",
"location" : "https://github.com/karwa/swift-url.git",
"state" : {
"branch" : "main",
"revision" : "220f6ab9d8a7e0742f85eb9f21b745942e001ae6"
}
}
],
"version" : 2
}

View File

@ -0,0 +1,31 @@
// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "InstanceFeatures",
platforms: [
.iOS(.v15),
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "InstanceFeatures",
targets: ["InstanceFeatures"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(path: "../Pachyderm"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "InstanceFeatures",
dependencies: ["Pachyderm"]),
.testTarget(
name: "InstanceFeaturesTests",
dependencies: ["InstanceFeatures"]),
]
)

View File

@ -0,0 +1,3 @@
# InstanceFeatures
A description of this package.

View File

@ -7,17 +7,20 @@
//
import Foundation
import Combine
import Pachyderm
import Sentry
struct InstanceFeatures {
public class InstanceFeatures: ObservableObject {
private static let pleromaVersionRegex = try! NSRegularExpression(pattern: "\\(compatible; pleroma (.*)\\)", options: .caseInsensitive)
private static let akkomaVersionRegex = try! NSRegularExpression(pattern: "\\(compatible; akkoma (.*)\\)", options: .caseInsensitive)
private var instanceType: InstanceType = .mastodon(.vanilla, nil)
private(set) var maxStatusChars = 500
private let _featuresUpdated = PassthroughSubject<Void, Never>()
public var featuresUpdated: some Publisher<Void, Never> { _featuresUpdated }
var localOnlyPosts: Bool {
@Published private var instanceType: InstanceType = .mastodon(.vanilla, nil)
@Published public private(set) var maxStatusChars = 500
public var localOnlyPosts: Bool {
switch instanceType {
case .mastodon(.hometown(_), _), .mastodon(.glitch, _):
return true
@ -26,19 +29,19 @@ struct InstanceFeatures {
}
}
var mastodonAttachmentRestrictions: Bool {
public var mastodonAttachmentRestrictions: Bool {
instanceType.isMastodon
}
var pollsAndAttachments: Bool {
public var pollsAndAttachments: Bool {
instanceType.isPleroma
}
var boostToOriginalAudience: Bool {
public var boostToOriginalAudience: Bool {
instanceType.isPleroma || instanceType.isMastodon
}
var profilePinnedStatuses: Bool {
public var profilePinnedStatuses: Bool {
switch instanceType {
case .pixelfed:
return false
@ -47,24 +50,24 @@ struct InstanceFeatures {
}
}
var trends: Bool {
public var trends: Bool {
instanceType.isMastodon
}
var profileSuggestions: Bool {
public var profileSuggestions: Bool {
instanceType.isMastodon && hasMastodonVersion(3, 4, 0)
}
var trendingStatusesAndLinks: Bool {
public var trendingStatusesAndLinks: Bool {
instanceType.isMastodon && hasMastodonVersion(3, 5, 0)
}
var reblogVisibility: Bool {
public var reblogVisibility: Bool {
(instanceType.isMastodon && hasMastodonVersion(2, 8, 0))
|| (instanceType.isPleroma && hasPleromaVersion(2, 0, 0))
}
var probablySupportsMarkdown: Bool {
public var probablySupportsMarkdown: Bool {
switch instanceType {
case .pleroma(_), .mastodon(.glitch, _), .mastodon(.hometown(_), _):
return true
@ -73,7 +76,7 @@ struct InstanceFeatures {
}
}
var needsLocalOnlyEmojiHack: Bool {
public var needsLocalOnlyEmojiHack: Bool {
if case .mastodon(.glitch, _) = instanceType {
return true
} else {
@ -81,7 +84,7 @@ struct InstanceFeatures {
}
}
var needsWideColorGamutHack: Bool {
public var needsWideColorGamutHack: Bool {
if case .mastodon(_, .some(let version)) = instanceType {
return version < Version(4, 0, 0)
} else {
@ -89,23 +92,26 @@ struct InstanceFeatures {
}
}
var canFollowHashtags: Bool {
public var canFollowHashtags: Bool {
hasMastodonVersion(4, 0, 0)
}
var filtersV2: Bool {
public var filtersV2: Bool {
hasMastodonVersion(4, 0, 0)
}
var notificationsAllowedTypes: Bool {
public var notificationsAllowedTypes: Bool {
hasMastodonVersion(3, 5, 0)
}
var pollVotersCount: Bool {
public var pollVotersCount: Bool {
instanceType.isMastodon
}
mutating func update(instance: Instance, nodeInfo: NodeInfo?) {
public init() {
}
public func update(instance: Instance, nodeInfo: NodeInfo?) {
let ver = instance.version.lowercased()
if ver.contains("glitch") {
instanceType = .mastodon(.glitch, Version(string: ver))
@ -150,10 +156,10 @@ struct InstanceFeatures {
maxStatusChars = instance.maxStatusCharacters ?? 500
setInstanceBreadcrumb(instance: instance, nodeInfo: nodeInfo)
_featuresUpdated.send()
}
func hasMastodonVersion(_ major: Int, _ minor: Int, _ patch: Int) -> Bool {
public func hasMastodonVersion(_ major: Int, _ minor: Int, _ patch: Int) -> Bool {
if case .mastodon(_, .some(let version)) = instanceType {
return version >= Version(major, minor, patch)
} else {
@ -259,19 +265,3 @@ extension InstanceFeatures {
}
}
}
private func setInstanceBreadcrumb(instance: Instance, nodeInfo: NodeInfo?) {
let crumb = Breadcrumb(level: .info, category: "MastodonController")
crumb.data = [
"instance": [
"version": instance.version
],
]
if let nodeInfo {
crumb.data!["nodeInfo"] = [
"software": nodeInfo.software.name,
"version": nodeInfo.software.version,
]
}
SentrySDK.addBreadcrumb(crumb)
}

View File

@ -1,13 +1,13 @@
//
// VersionTests.swift
// TuskerTests
// InstanceFeaturesTests
//
// Created by Shadowfacts on 4/2/22.
// Copyright © 2022 Shadowfacts. All rights reserved.
//
import XCTest
@testable import Tusker
@testable import InstanceFeatures
class VersionTests: XCTestCase {

View File

@ -113,7 +113,6 @@
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2425217ABF63005076CC /* UserActivityType.swift */; };
D62E9985279CA23900C26176 /* URLSession+Development.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62E9984279CA23900C26176 /* URLSession+Development.swift */; };
D62E9987279D094F00C26176 /* StatusMetaIndicatorsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62E9986279D094F00C26176 /* StatusMetaIndicatorsView.swift */; };
D62E9989279DB2D100C26176 /* InstanceFeatures.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62E9988279DB2D100C26176 /* InstanceFeatures.swift */; };
D62FF04823D7CDD700909D6E /* AttributedStringHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */; };
D6311C5025B3765B00B27539 /* ImageDataCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6311C4F25B3765B00B27539 /* ImageDataCache.swift */; };
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; };
@ -373,6 +372,7 @@
D6F6A557291F4F1600F496A8 /* MuteAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F6A556291F4F1600F496A8 /* MuteAccountView.swift */; };
D6F6A5592920676800F496A8 /* ComposeToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F6A5582920676800F496A8 /* ComposeToolbar.swift */; };
D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F953EF21251A2900CF0F2B /* MastodonController.swift */; };
D6FA94E129B52898006AAC51 /* InstanceFeatures in Frameworks */ = {isa = PBXBuildFile; productRef = D6FA94E029B52898006AAC51 /* InstanceFeatures */; };
D6FF9860255C717400845181 /* AccountSwitchingContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6FF985F255C717400845181 /* AccountSwitchingContainerViewController.swift */; };
/* End PBXBuildFile section */
@ -530,7 +530,6 @@
D62D2425217ABF63005076CC /* UserActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivityType.swift; sourceTree = "<group>"; };
D62E9984279CA23900C26176 /* URLSession+Development.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+Development.swift"; sourceTree = "<group>"; };
D62E9986279D094F00C26176 /* StatusMetaIndicatorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusMetaIndicatorsView.swift; sourceTree = "<group>"; };
D62E9988279DB2D100C26176 /* InstanceFeatures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceFeatures.swift; sourceTree = "<group>"; };
D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringHelperTests.swift; sourceTree = "<group>"; };
D6311C4F25B3765B00B27539 /* ImageDataCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDataCache.swift; sourceTree = "<group>"; };
D6333B362137838300CE884A /* AttributedString+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Helpers.swift"; sourceTree = "<group>"; };
@ -803,6 +802,7 @@
D6F6A556291F4F1600F496A8 /* MuteAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuteAccountView.swift; sourceTree = "<group>"; };
D6F6A5582920676800F496A8 /* ComposeToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeToolbar.swift; sourceTree = "<group>"; };
D6F953EF21251A2900CF0F2B /* MastodonController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonController.swift; sourceTree = "<group>"; };
D6FA94DF29B52891006AAC51 /* InstanceFeatures */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = InstanceFeatures; path = Packages/InstanceFeatures; sourceTree = "<group>"; };
D6FF985F255C717400845181 /* AccountSwitchingContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSwitchingContainerViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -811,6 +811,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D6FA94E129B52898006AAC51 /* InstanceFeatures in Frameworks */,
D674A50927F9128D00BA03AC /* Pachyderm in Frameworks */,
D659F35E2953A212002D944A /* TTTKit in Frameworks */,
D6B0026E29B5248800C70BE2 /* UserAccounts in Frameworks */,
@ -1539,6 +1540,7 @@
D6BEA243291A0C83002F4D01 /* Duckable */,
D68A76F22953915C001DA1B3 /* TTTKit */,
D6B0026C29B5245400C70BE2 /* UserAccounts */,
D6FA94DF29B52891006AAC51 /* InstanceFeatures */,
D6D4DDCE212518A000E1C4BB /* Tusker */,
D6D4DDE3212518A200E1C4BB /* TuskerTests */,
D6D4DDEE212518A200E1C4BB /* TuskerUITests */,
@ -1694,7 +1696,6 @@
D6F953F121251A2F00CF0F2B /* API */ = {
isa = PBXGroup;
children = (
D62E9988279DB2D100C26176 /* InstanceFeatures.swift */,
D6F953EF21251A2900CF0F2B /* MastodonController.swift */,
D6E9CDA7281A427800BBC98E /* PostService.swift */,
D61ABEFD28F1C92600B29151 /* FavoriteService.swift */,
@ -1743,6 +1744,7 @@
D6BEA244291A0EDE002F4D01 /* Duckable */,
D659F35D2953A212002D944A /* TTTKit */,
D6B0026D29B5248800C70BE2 /* UserAccounts */,
D6FA94E029B52898006AAC51 /* InstanceFeatures */,
);
productName = Tusker;
productReference = D6D4DDCC212518A000E1C4BB /* Tusker.app */;
@ -1999,7 +2001,6 @@
D6969E9E240C81B9002843CE /* NSTextAttachment+Emoji.swift in Sources */,
D6BC9DB3232D4C07002CA326 /* WellnessPrefsView.swift in Sources */,
D64BC18F23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift in Sources */,
D62E9989279DB2D100C26176 /* InstanceFeatures.swift in Sources */,
D6ADB6F028ED1F25009924AB /* CachedImageView.swift in Sources */,
D693DE5923FE24310061E07D /* InteractivePushTransition.swift in Sources */,
D69693FA25859A8000F4E116 /* ComposeSceneDelegate.swift in Sources */,
@ -2975,6 +2976,10 @@
isa = XCSwiftPackageProductDependency;
productName = Duckable;
};
D6FA94E029B52898006AAC51 /* InstanceFeatures */ = {
isa = XCSwiftPackageProductDependency;
productName = InstanceFeatures;
};
/* End XCSwiftPackageProductDependency section */
/* Begin XCVersionGroup section */

View File

@ -10,6 +10,8 @@ import Foundation
import Pachyderm
import Combine
import UserAccounts
import InstanceFeatures
import Sentry
class MastodonController: ObservableObject {
@ -48,11 +50,11 @@ class MastodonController: ObservableObject {
var accountPreferences: AccountPreferences!
let client: Client!
let instanceFeatures = InstanceFeatures()
@Published private(set) var account: Account!
@Published private(set) var instance: Instance!
@Published private(set) var nodeInfo: NodeInfo!
@Published private(set) var instanceFeatures = InstanceFeatures()
@Published private(set) var lists: [List] = []
@Published private(set) var customEmojis: [Emoji]?
@Published private(set) var followedHashtags: [FollowedHashtag] = []
@ -84,11 +86,12 @@ class MastodonController: ObservableObject {
}
.sink { [unowned self] (instance, nodeInfo) in
self.instanceFeatures.update(instance: instance, nodeInfo: nodeInfo)
setInstanceBreadcrumb(instance: instance, nodeInfo: nodeInfo)
}
.store(in: &cancellables)
$instanceFeatures
.filter { [unowned self] in $0.canFollowHashtags && self.followedHashtags.isEmpty }
instanceFeatures.featuresUpdated
.filter { [unowned self] _ in self.instanceFeatures.canFollowHashtags && self.followedHashtags.isEmpty }
.sink { [unowned self] _ in
Task {
await self.loadFollowedHashtags()
@ -454,3 +457,19 @@ class MastodonController: ObservableObject {
}
}
private func setInstanceBreadcrumb(instance: Instance, nodeInfo: NodeInfo?) {
let crumb = Breadcrumb(level: .info, category: "MastodonController")
crumb.data = [
"instance": [
"version": instance.version
],
]
if let nodeInfo {
crumb.data!["nodeInfo"] = [
"software": nodeInfo.software.name,
"version": nodeInfo.software.version,
]
}
SentrySDK.addBreadcrumb(crumb)
}

View File

@ -10,6 +10,7 @@ import UIKit
import Photos
import UniformTypeIdentifiers
import PencilKit
import InstanceFeatures
enum CompositionAttachmentData {
case asset(PHAsset)