forked from shadowfacts/Tusker
Clean up instance type/feature detection
Add akkoma detection
This commit is contained in:
parent
603e989879
commit
99a1c76cb1
|
@ -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?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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])
|
||||||
|
|
Loading…
Reference in New Issue