Clean up instance type/feature detection

Add akkoma detection
This commit is contained in:
Shadowfacts 2022-11-14 21:17:08 -05:00
parent 603e989879
commit 99a1c76cb1
5 changed files with 94 additions and 49 deletions

View File

@ -11,15 +11,18 @@ import Pachyderm
struct InstanceFeatures { struct InstanceFeatures {
private static let pleromaVersionRegex = try! NSRegularExpression(pattern: "\\(compatible; pleroma (.*)\\)", options: .caseInsensitive) private static let pleromaVersionRegex = try! NSRegularExpression(pattern: "\\(compatible; pleroma (.*)\\)", options: .caseInsensitive)
private static let akkomaVersionRegex = try! NSRegularExpression(pattern: "\\(compatible; akkoma (.*)\\)", options: .caseInsensitive)
private(set) var instanceType = InstanceType.mastodon private var instanceType: InstanceType = .mastodon(.vanilla, nil)
private(set) var version: Version?
private(set) var pleromaVersion: Version?
private(set) var hometownVersion: Version?
private(set) var maxStatusChars = 500 private(set) var maxStatusChars = 500
var localOnlyPosts: Bool { var localOnlyPosts: Bool {
instanceType == .hometown || instanceType == .glitch switch instanceType {
case .mastodon(.hometown(_), _), .mastodon(.glitch, _):
return true
default:
return false
}
} }
var mastodonAttachmentRestrictions: Bool { var mastodonAttachmentRestrictions: Bool {
@ -27,15 +30,20 @@ struct InstanceFeatures {
} }
var pollsAndAttachments: Bool { var pollsAndAttachments: Bool {
instanceType == .pleroma instanceType.isPleroma
} }
var boostToOriginalAudience: Bool { var boostToOriginalAudience: Bool {
instanceType == .pleroma || instanceType.isMastodon instanceType.isPleroma || instanceType.isMastodon
} }
var profilePinnedStatuses: Bool { var profilePinnedStatuses: Bool {
instanceType != .pixelfed switch instanceType {
case .pixelfed:
return false
default:
return true
}
} }
var trends: Bool { var trends: Bool {
@ -48,45 +56,65 @@ struct InstanceFeatures {
var reblogVisibility: Bool { var reblogVisibility: Bool {
(instanceType.isMastodon && hasVersion(2, 8, 0)) (instanceType.isMastodon && hasVersion(2, 8, 0))
|| (instanceType == .pleroma && hasPleromaVersion(2, 0, 0)) || (instanceType.isPleroma && hasPleromaVersion(2, 0, 0))
} }
var probablySupportsMarkdown: Bool { var probablySupportsMarkdown: Bool {
instanceType == .pleroma || instanceType == .glitch || instanceType == .hometown switch instanceType {
case .pleroma(_), .mastodon(.glitch, _), .mastodon(.hometown(_), _):
return true
default:
return false
}
}
var needsLocalOnlyEmojiHack: Bool {
if case .mastodon(.glitch, _) = instanceType {
return true
} else {
return false
}
} }
mutating func update(instance: Instance, nodeInfo: NodeInfo?) { mutating func update(instance: Instance, nodeInfo: NodeInfo?) {
var version: Version?
let ver = instance.version.lowercased() let ver = instance.version.lowercased()
if ver.contains("glitch") { if ver.contains("glitch") {
instanceType = .glitch instanceType = .mastodon(.glitch, Version(string: ver))
} else if nodeInfo?.software.name == "hometown" { } else if nodeInfo?.software.name == "hometown" {
instanceType = .hometown var mastoVersion: Version?
var hometownVersion: Version?
// like "1.0.6+3.5.2" // like "1.0.6+3.5.2"
let parts = ver.split(separator: "+") let parts = ver.split(separator: "+")
if parts.count == 2 { if parts.count == 2 {
version = Version(string: String(parts[1])) mastoVersion = Version(string: String(parts[1]))
hometownVersion = Version(string: String(parts[0])) hometownVersion = Version(string: String(parts[0]))
} else {
mastoVersion = Version(string: ver)
} }
instanceType = .mastodon(.hometown(hometownVersion), mastoVersion)
} else if ver.contains("pleroma") { } else if ver.contains("pleroma") {
instanceType = .pleroma var pleromaVersion: Version?
if let match = InstanceFeatures.pleromaVersionRegex.firstMatch(in: ver, range: NSRange(location: 0, length: ver.utf16.count)) { if let match = InstanceFeatures.pleromaVersionRegex.firstMatch(in: ver, range: NSRange(location: 0, length: ver.utf16.count)) {
pleromaVersion = Version(string: (ver as NSString).substring(with: match.range(at: 1))) pleromaVersion = Version(string: (ver as NSString).substring(with: match.range(at: 1)))
} }
instanceType = .pleroma(.vanilla(pleromaVersion))
} else if ver.contains("akkoma") {
var akkomaVersion: Version?
if let match = InstanceFeatures.akkomaVersionRegex.firstMatch(in: ver, range: NSRange(location: 0, length: ver.utf16.count)) {
akkomaVersion = Version(string: (ver as NSString).substring(with: match.range(at: 1)))
}
instanceType = .pleroma(.akkoma(akkomaVersion))
} else if ver.contains("pixelfed") { } else if ver.contains("pixelfed") {
instanceType = .pixelfed instanceType = .pixelfed
} else { } else {
instanceType = .mastodon instanceType = .mastodon(.vanilla, Version(string: ver))
} }
self.version = version ?? Version(string: ver)
maxStatusChars = instance.maxStatusCharacters ?? 500 maxStatusChars = instance.maxStatusCharacters ?? 500
} }
func hasVersion(_ major: Int, _ minor: Int, _ patch: Int) -> Bool { func hasVersion(_ major: Int, _ minor: Int, _ patch: Int) -> Bool {
if let version { if case .mastodon(_, .some(let version)) = instanceType {
return version >= Version(major, minor, patch) return version >= Version(major, minor, patch)
} else { } else {
return false return false
@ -94,30 +122,47 @@ struct InstanceFeatures {
} }
func hasPleromaVersion(_ major: Int, _ minor: Int, _ patch: Int) -> Bool { func hasPleromaVersion(_ major: Int, _ minor: Int, _ patch: Int) -> Bool {
if let pleromaVersion { switch instanceType {
return pleromaVersion >= Version(major, minor, patch) case .pleroma(.vanilla(.some(let version))), .pleroma(.akkoma(.some(let version))):
} else { return version >= Version(major, minor, patch)
default:
return false return false
} }
} }
} }
extension InstanceFeatures { extension InstanceFeatures {
enum InstanceType: Equatable { enum InstanceType {
case mastodon // vanilla case mastodon(MastodonType, Version?)
case pleroma case pleroma(PleromaType)
case hometown
case glitch
case pixelfed case pixelfed
var isMastodon: Bool { var isMastodon: Bool {
switch self { if case .mastodon(_, _) = self {
case .mastodon, .hometown, .glitch:
return true return true
default: } else {
return false return false
} }
} }
var isPleroma: Bool {
if case .pleroma(_) = self {
return true
} else {
return false
}
}
}
enum MastodonType {
case vanilla
case hometown(Version?)
case glitch
}
enum PleromaType {
case vanilla(Version?)
case akkoma(Version?)
} }
} }

View File

@ -39,7 +39,7 @@ class PostService: ObservableObject {
let sensitive = contentWarning != nil let sensitive = contentWarning != nil
let request = Client.createStatus( let request = Client.createStatus(
text: draft.textForPosting(on: mastodonController.instanceFeatures), text: textForPosting(),
contentType: Preferences.shared.statusContentType, contentType: Preferences.shared.statusContentType,
inReplyTo: draft.inReplyToID, inReplyTo: draft.inReplyToID,
media: uploadedAttachments, media: uploadedAttachments,
@ -104,6 +104,19 @@ class PostService: ObservableObject {
return try await mastodonController.run(req).0 return try await mastodonController.run(req).0
} }
private func textForPosting() -> String {
var text = draft.text
// when using dictation, iOS sometimes leaves a U+FFFC OBJECT REPLACEMENT CHARACTER behind in the text,
// which we want to strip out before actually posting the status
text = text.replacingOccurrences(of: "\u{fffc}", with: "")
if draft.localOnly && mastodonController.instanceFeatures.needsLocalOnlyEmojiHack {
text += " 👁"
}
return text
}
enum Error: Swift.Error, LocalizedError { enum Error: Swift.Error, LocalizedError {
case attachmentData(index: Int, cause: CompositionAttachmentData.Error) case attachmentData(index: Int, cause: CompositionAttachmentData.Error)
case attachmentUpload(index: Int, cause: Client.Error) case attachmentUpload(index: Int, cause: Client.Error)

View File

@ -86,19 +86,6 @@ class Draft: Codable, ObservableObject {
try container.encode(initialText, forKey: .initialText) try container.encode(initialText, forKey: .initialText)
} }
func textForPosting(on instance: InstanceFeatures) -> String {
var text = self.text
// when using dictation, iOS sometimes leaves a U+FFFC OBJECT REPLACEMENT CHARACTER behind in the text,
// which we want to strip out before actually posting the status
text = text.replacingOccurrences(of: "\u{fffc}", with: "")
if localOnly && instance.instanceType == .glitch {
text += " 👁"
}
return text
}
} }
extension Draft: Equatable { extension Draft: Equatable {

View File

@ -141,7 +141,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>() var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections(Section.allCases.filter { $0 != .discover }) snapshot.appendSections(Section.allCases.filter { $0 != .discover })
snapshot.appendItems([.bookmarks], toSection: .bookmarks) snapshot.appendItems([.bookmarks], toSection: .bookmarks)
if mastodonController.instanceFeatures.instanceType.isMastodon, if mastodonController.instanceFeatures.trends,
!Preferences.shared.hideDiscover { !Preferences.shared.hideDiscover {
addDiscoverSection(to: &snapshot) addDiscoverSection(to: &snapshot)
} }
@ -172,7 +172,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
private func ownInstanceLoaded(_ instance: Instance) { private func ownInstanceLoaded(_ instance: Instance) {
var snapshot = self.dataSource.snapshot() var snapshot = self.dataSource.snapshot()
if mastodonController.instanceFeatures.instanceType.isMastodon, if mastodonController.instanceFeatures.trends,
!snapshot.sectionIdentifiers.contains(.discover) { !snapshot.sectionIdentifiers.contains(.discover) {
snapshot.insertSections([.discover], afterSection: .bookmarks) snapshot.insertSections([.discover], afterSection: .bookmarks)
snapshot.appendItems([.trendingTags, .profileDirectory], toSection: .discover) snapshot.appendItems([.trendingTags, .profileDirectory], toSection: .discover)

View File

@ -163,7 +163,7 @@ class MainSidebarViewController: UIViewController {
snapshot.appendItems([ snapshot.appendItems([
.tab(.compose) .tab(.compose)
], toSection: .compose) ], toSection: .compose)
if mastodonController.instanceFeatures.instanceType.isMastodon, if mastodonController.instanceFeatures.trends,
!Preferences.shared.hideDiscover { !Preferences.shared.hideDiscover {
snapshot.insertSections([.discover], afterSection: .compose) snapshot.insertSections([.discover], afterSection: .compose)
} }
@ -188,7 +188,7 @@ class MainSidebarViewController: UIViewController {
} }
private func ownInstanceLoaded(_ instance: Instance) { private func ownInstanceLoaded(_ instance: Instance) {
if mastodonController.instanceFeatures.instanceType.isMastodon { if mastodonController.instanceFeatures.trends {
var snapshot = self.dataSource.snapshot() var snapshot = self.dataSource.snapshot()
if !snapshot.sectionIdentifiers.contains(.discover) { if !snapshot.sectionIdentifiers.contains(.discover) {
snapshot.appendSections([.discover]) snapshot.appendSections([.discover])