refactor: Move ServerOperation and related types to core.model (#969)

This commit is contained in:
Nik Clayton 2024-10-03 13:41:46 +02:00 committed by GitHub
parent ea53b68b3f
commit fe1c586dae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 294 additions and 228 deletions

View File

@ -74,21 +74,21 @@ import app.pachli.core.common.extensions.show
import app.pachli.core.common.extensions.toggleVisibility import app.pachli.core.common.extensions.toggleVisibility
import app.pachli.core.common.extensions.viewBinding import app.pachli.core.common.extensions.viewBinding
import app.pachli.core.common.extensions.visible import app.pachli.core.common.extensions.visible
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_BY_DATE
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_FROM
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_AUDIO
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_EMBED
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_IMAGE
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_LINK
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_MEDIA
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_POLL
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_VIDEO
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_LIBRARY
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_PUBLIC
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_REPLY
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_SENSITIVE
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_LANGUAGE
import app.pachli.core.network.Server import app.pachli.core.network.Server
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_BY_DATE
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_FROM
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_AUDIO
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_EMBED
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_IMAGE
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_LINK
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_MEDIA
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_POLL
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_VIDEO
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_LIBRARY
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_PUBLIC
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_REPLY
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_SENSITIVE
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_LANGUAGE
import app.pachli.core.preferences.PrefKeys import app.pachli.core.preferences.PrefKeys
import app.pachli.core.ui.extensions.await import app.pachli.core.ui.extensions.await
import app.pachli.core.ui.extensions.awaitSingleChoiceItem import app.pachli.core.ui.extensions.awaitSingleChoiceItem

View File

@ -36,20 +36,20 @@ import app.pachli.components.search.adapter.SearchPagingSourceFactory
import app.pachli.core.accounts.AccountManager import app.pachli.core.accounts.AccountManager
import app.pachli.core.data.repository.ServerRepository import app.pachli.core.data.repository.ServerRepository
import app.pachli.core.database.model.AccountEntity import app.pachli.core.database.model.AccountEntity
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_BY_DATE import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_BY_DATE
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_FROM import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_FROM
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_AUDIO import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_AUDIO
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_EMBED import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_EMBED
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_IMAGE import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_IMAGE
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_LINK import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_LINK
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_MEDIA import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_MEDIA
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_POLL import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_POLL
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_VIDEO import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_VIDEO
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_LIBRARY import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_LIBRARY
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_PUBLIC import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_PUBLIC
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_REPLY import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_REPLY
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_SENSITIVE import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_SENSITIVE
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_LANGUAGE import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_LANGUAGE
import app.pachli.core.network.model.DeletedStatus import app.pachli.core.network.model.DeletedStatus
import app.pachli.core.network.model.Poll import app.pachli.core.network.model.Poll
import app.pachli.core.network.model.Status import app.pachli.core.network.model.Status

View File

@ -49,13 +49,13 @@ import app.pachli.core.data.repository.ServerRepository
import app.pachli.core.database.model.AccountEntity import app.pachli.core.database.model.AccountEntity
import app.pachli.core.database.model.TranslationState import app.pachli.core.database.model.TranslationState
import app.pachli.core.domain.DownloadUrlUseCase import app.pachli.core.domain.DownloadUrlUseCase
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_STATUSES_TRANSLATE
import app.pachli.core.navigation.AttachmentViewData import app.pachli.core.navigation.AttachmentViewData
import app.pachli.core.navigation.ComposeActivityIntent import app.pachli.core.navigation.ComposeActivityIntent
import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
import app.pachli.core.navigation.ReportActivityIntent import app.pachli.core.navigation.ReportActivityIntent
import app.pachli.core.navigation.TimelineActivityIntent import app.pachli.core.navigation.TimelineActivityIntent
import app.pachli.core.navigation.ViewMediaActivityIntent import app.pachli.core.navigation.ViewMediaActivityIntent
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_STATUSES_TRANSLATE
import app.pachli.core.network.model.Attachment import app.pachli.core.network.model.Attachment
import app.pachli.core.network.model.Status import app.pachli.core.network.model.Status
import app.pachli.core.network.parseAsMastodonHtml import app.pachli.core.network.parseAsMastodonHtml

View File

@ -30,9 +30,9 @@ import app.pachli.core.data.repository.ContentFiltersError.GetContentFiltersErro
import app.pachli.core.data.repository.ContentFiltersError.ServerDoesNotFilter import app.pachli.core.data.repository.ContentFiltersError.ServerDoesNotFilter
import app.pachli.core.data.repository.ContentFiltersError.ServerRepositoryError import app.pachli.core.data.repository.ContentFiltersError.ServerRepositoryError
import app.pachli.core.data.repository.ContentFiltersError.UpdateContentFilterError import app.pachli.core.data.repository.ContentFiltersError.UpdateContentFilterError
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_FILTERS_CLIENT
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_FILTERS_SERVER
import app.pachli.core.network.Server import app.pachli.core.network.Server
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_FILTERS_CLIENT
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_FILTERS_SERVER
import app.pachli.core.network.model.FilterAction import app.pachli.core.network.model.FilterAction
import app.pachli.core.network.model.FilterContext import app.pachli.core.network.model.FilterContext
import app.pachli.core.network.model.FilterKeyword import app.pachli.core.network.model.FilterKeyword

View File

@ -29,7 +29,7 @@ import app.pachli.core.data.repository.ServerRepository.Error.GetWellKnownNodeIn
import app.pachli.core.data.repository.ServerRepository.Error.UnsupportedSchema import app.pachli.core.data.repository.ServerRepository.Error.UnsupportedSchema
import app.pachli.core.data.repository.ServerRepository.Error.ValidateNodeInfo import app.pachli.core.data.repository.ServerRepository.Error.ValidateNodeInfo
import app.pachli.core.network.Server import app.pachli.core.network.Server
import app.pachli.core.network.model.nodeinfo.NodeInfo import app.pachli.core.network.model.nodeinfo.UnvalidatedNodeInfo
import app.pachli.core.network.retrofit.MastodonApi import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.core.network.retrofit.NodeInfoApi import app.pachli.core.network.retrofit.NodeInfoApi
import at.connyduck.calladapter.networkresult.fold import at.connyduck.calladapter.networkresult.fold
@ -100,7 +100,7 @@ class ServerRepository @Inject constructor(
Timber.d("Loading node info from %s", nodeInfoUrl) Timber.d("Loading node info from %s", nodeInfoUrl)
val nodeInfo = nodeInfoApi.nodeInfo(nodeInfoUrl).fold( val nodeInfo = nodeInfoApi.nodeInfo(nodeInfoUrl).fold(
{ NodeInfo.from(it).mapError { ValidateNodeInfo(nodeInfoUrl, it) } }, { it.validate().mapError { ValidateNodeInfo(nodeInfoUrl, it) } },
{ Err(GetNodeInfo(nodeInfoUrl, it)) }, { Err(GetNodeInfo(nodeInfoUrl, it)) },
).bind() ).bind()
@ -136,7 +136,7 @@ class ServerRepository @Inject constructor(
arrayOf(url, throwable.localizedMessage ?: ""), arrayOf(url, throwable.localizedMessage ?: ""),
) )
data class ValidateNodeInfo(val url: String, val error: NodeInfo.Error) : Error( data class ValidateNodeInfo(val url: String, val error: UnvalidatedNodeInfo.Error) : Error(
R.string.server_repository_error_validate_node_info, R.string.server_repository_error_validate_node_info,
arrayOf(url), arrayOf(url),
cause = error, cause = error,

View File

@ -23,7 +23,7 @@ import app.pachli.core.accounts.AccountManager
import app.pachli.core.common.di.ApplicationScope import app.pachli.core.common.di.ApplicationScope
import app.pachli.core.data.model.StatusDisplayOptions import app.pachli.core.data.model.StatusDisplayOptions
import app.pachli.core.database.model.AccountEntity import app.pachli.core.database.model.AccountEntity
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_STATUSES_TRANSLATE import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_STATUSES_TRANSLATE
import app.pachli.core.preferences.CardViewMode import app.pachli.core.preferences.CardViewMode
import app.pachli.core.preferences.PrefKeys import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.SharedPreferencesRepository import app.pachli.core.preferences.SharedPreferencesRepository

View File

@ -22,9 +22,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import app.pachli.core.data.repository.ContentFiltersRepository import app.pachli.core.data.repository.ContentFiltersRepository
import app.pachli.core.data.repository.HiltTestApplication_Application import app.pachli.core.data.repository.HiltTestApplication_Application
import app.pachli.core.data.repository.ServerRepository import app.pachli.core.data.repository.ServerRepository
import app.pachli.core.model.ServerKind
import app.pachli.core.model.ServerOperation
import app.pachli.core.network.Server import app.pachli.core.network.Server
import app.pachli.core.network.ServerKind
import app.pachli.core.network.ServerOperation
import app.pachli.core.network.retrofit.MastodonApi import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.core.testing.rules.MainCoroutineRule import app.pachli.core.testing.rules.MainCoroutineRule
import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Ok

View File

@ -36,4 +36,7 @@ dependencies {
implementation(libs.moshix.sealed.runtime) implementation(libs.moshix.sealed.runtime)
ksp(libs.moshix.sealed.codegen) ksp(libs.moshix.sealed.codegen)
implementation(libs.semver)
?.because("ServerOperation uses Version")
} }

View File

@ -0,0 +1,33 @@
/*
* Copyright 2024 Pachli Association
*
* This file is a part of Pachli.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Pachli; if not,
* see <http://www.gnu.org/licenses>.
*/
package app.pachli.core.model
/**
* A validated NodeInfo.
*
* See https://nodeinfo.diaspora.software/protocol.html and
* https://nodeinfo.diaspora.software/schema.html.
*/
data class NodeInfo(val software: Software) {
data class Software(
/** Software name, won't be null, empty, or blank */
val name: String,
/** Software version, won't be null, empty, or blank */
val version: String,
)
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2024 Pachli Association
*
* This file is a part of Pachli.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Pachli; if not,
* see <http://www.gnu.org/licenses>.
*/
package app.pachli.core.model
/**
* Servers that are known to implement the Mastodon client API
*/
enum class ServerKind {
AKKOMA,
FEDIBIRD,
FIREFISH,
FRIENDICA,
GLITCH,
GOTOSOCIAL,
HOMETOWN,
ICESHRIMP,
MASTODON,
PLEROMA,
PIXELFED,
SHARKEY,
/**
* Catch-all for servers we don't recognise but that responded to either
* /api/v1/instance or /api/v2/instance
*/
UNKNOWN,
;
companion object {
fun from(s: NodeInfo.Software) = when (s.name.lowercase()) {
"akkoma" -> AKKOMA
"fedibird" -> FEDIBIRD
"firefish" -> FIREFISH
"friendica" -> FRIENDICA
"gotosocial" -> GOTOSOCIAL
"hometown" -> HOMETOWN
"iceshrimp" -> ICESHRIMP
"mastodon" -> {
// Glitch doesn't report a different software name it stuffs it
// in the version (https://github.com/glitch-soc/mastodon/issues/2582).
if (s.version.contains("+glitch")) GLITCH else MASTODON
}
"pixelfed" -> PIXELFED
"pleroma" -> PLEROMA
"sharkey" -> SHARKEY
else -> UNKNOWN
}
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2024 Pachli Association
*
* This file is a part of Pachli.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Pachli; if not,
* see <http://www.gnu.org/licenses>.
*/
package app.pachli.core.model
import io.github.z4kn4fein.semver.Version
/**
* Identifiers for operations that the server may or may not support.
*/
enum class ServerOperation(id: String, versions: List<Version>) {
/** Client-side filters */
ORG_JOINMASTODON_FILTERS_CLIENT(
"org.joinmastodon.filters.client",
listOf(
// Initial introduction in Mastodon 2.4.3
Version(major = 1),
// "account" context available in filter views in Mastodon 3.1.0
Version(major = 1, minor = 1),
),
),
/** Server-side filters */
ORG_JOINMASTODON_FILTERS_SERVER(
"org.joinmastodon.filters.server",
listOf(
// Intitial introduction in Mastodon 4.0.0
Version(major = 1),
),
),
/** Translate a status */
ORG_JOINMASTODON_STATUSES_TRANSLATE(
"org.joinmastodon.statuses.translate",
listOf(
// Initial introduction in Mastodon 4.0.0
Version(major = 1),
// Spoiler warnings, polls, and media descriptions are also translated in Mastodon 4.2.0
Version(major = 1, minor = 1),
),
),
/** Search for posts from a particular account */
ORG_JOINMASTODON_SEARCH_QUERY_FROM(
"org.joinmastodon.search.query:from",
listOf(
// Initial introduction in Mastodon 3.5.0
Version(major = 1),
// Support for `from:me` in Mastodon 4.2.0
Version(major = 1, minor = 1),
),
),
ORG_JOINMASTODON_SEARCH_QUERY_LANGUAGE("org.joinmastodon.search.query:language", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_MEDIA("org.joinmastodon.search.query:has:media", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_IMAGE("org.joinmastodon.search.query:has:image", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_VIDEO("org.joinmastodon.search.query:has:video", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_AUDIO("org.joinmastodon.search.query:has:audio", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_POLL("org.joinmastodon.search.query:has:poll", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_LINK("org.joinmastodon.search.query:has:link", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_EMBED("org.joinmastodon.search.query:has:embed", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_IS_REPLY("org.joinmastodon.search.query:is:reply", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_IS_SENSITIVE("org.joinmastodon.search.query:is:sensitive", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_IN_LIBRARY("org.joinmastodon.search.query:in:library", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_IN_PUBLIC("org.joinmastodon.search.query:in:public", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_BY_DATE("org.joinmastodon.search.query:in:public", listOf(Version(major = 1))),
}

View File

@ -20,40 +20,42 @@ package app.pachli.core.network
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.Companion.PRIVATE import androidx.annotation.VisibleForTesting.Companion.PRIVATE
import app.pachli.core.common.PachliError import app.pachli.core.common.PachliError
import app.pachli.core.model.NodeInfo
import app.pachli.core.model.ServerKind
import app.pachli.core.model.ServerKind.AKKOMA
import app.pachli.core.model.ServerKind.FEDIBIRD
import app.pachli.core.model.ServerKind.FIREFISH
import app.pachli.core.model.ServerKind.FRIENDICA
import app.pachli.core.model.ServerKind.GLITCH
import app.pachli.core.model.ServerKind.GOTOSOCIAL
import app.pachli.core.model.ServerKind.HOMETOWN
import app.pachli.core.model.ServerKind.ICESHRIMP
import app.pachli.core.model.ServerKind.MASTODON
import app.pachli.core.model.ServerKind.PIXELFED
import app.pachli.core.model.ServerKind.PLEROMA
import app.pachli.core.model.ServerKind.SHARKEY
import app.pachli.core.model.ServerKind.UNKNOWN
import app.pachli.core.model.ServerOperation
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_FILTERS_CLIENT
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_FILTERS_SERVER
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_BY_DATE
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_FROM
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_AUDIO
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_EMBED
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_IMAGE
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_LINK
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_MEDIA
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_POLL
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_VIDEO
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_LIBRARY
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_PUBLIC
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_REPLY
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_SENSITIVE
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_LANGUAGE
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_STATUSES_TRANSLATE
import app.pachli.core.network.Server.Error.UnparseableVersion import app.pachli.core.network.Server.Error.UnparseableVersion
import app.pachli.core.network.ServerKind.AKKOMA
import app.pachli.core.network.ServerKind.FEDIBIRD
import app.pachli.core.network.ServerKind.FIREFISH
import app.pachli.core.network.ServerKind.FRIENDICA
import app.pachli.core.network.ServerKind.GLITCH
import app.pachli.core.network.ServerKind.GOTOSOCIAL
import app.pachli.core.network.ServerKind.HOMETOWN
import app.pachli.core.network.ServerKind.ICESHRIMP
import app.pachli.core.network.ServerKind.MASTODON
import app.pachli.core.network.ServerKind.PIXELFED
import app.pachli.core.network.ServerKind.PLEROMA
import app.pachli.core.network.ServerKind.SHARKEY
import app.pachli.core.network.ServerKind.UNKNOWN
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_FILTERS_CLIENT
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_FILTERS_SERVER
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_BY_DATE
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_FROM
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_AUDIO
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_EMBED
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_IMAGE
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_LINK
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_MEDIA
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_POLL
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_VIDEO
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_LIBRARY
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_PUBLIC
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_REPLY
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_SENSITIVE
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_LANGUAGE
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_STATUSES_TRANSLATE
import app.pachli.core.network.model.InstanceV1 import app.pachli.core.network.model.InstanceV1
import app.pachli.core.network.model.InstanceV2 import app.pachli.core.network.model.InstanceV2
import app.pachli.core.network.model.nodeinfo.NodeInfo
import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result import com.github.michaelbull.result.Result
import com.github.michaelbull.result.andThen import com.github.michaelbull.result.andThen
@ -69,66 +71,6 @@ import io.github.z4kn4fein.semver.toVersion
import java.text.ParseException import java.text.ParseException
import kotlin.collections.set import kotlin.collections.set
/**
* Identifiers for operations that the server may or may not support.
*/
enum class ServerOperation(id: String, versions: List<Version>) {
/** Client-side filters */
ORG_JOINMASTODON_FILTERS_CLIENT(
"org.joinmastodon.filters.client",
listOf(
// Initial introduction in Mastodon 2.4.3
Version(major = 1),
// "account" context available in filter views in Mastodon 3.1.0
Version(major = 1, minor = 1),
),
),
/** Server-side filters */
ORG_JOINMASTODON_FILTERS_SERVER(
"org.joinmastodon.filters.server",
listOf(
// Intitial introduction in Mastodon 4.0.0
Version(major = 1),
),
),
/** Translate a status */
ORG_JOINMASTODON_STATUSES_TRANSLATE(
"org.joinmastodon.statuses.translate",
listOf(
// Initial introduction in Mastodon 4.0.0
Version(major = 1),
// Spoiler warnings, polls, and media descriptions are also translated in Mastodon 4.2.0
Version(major = 1, minor = 1),
),
),
/** Search for posts from a particular account */
ORG_JOINMASTODON_SEARCH_QUERY_FROM(
"org.joinmastodon.search.query:from",
listOf(
// Initial introduction in Mastodon 3.5.0
Version(major = 1),
// Support for `from:me` in Mastodon 4.2.0
Version(major = 1, minor = 1),
),
),
ORG_JOINMASTODON_SEARCH_QUERY_LANGUAGE("org.joinmastodon.search.query:language", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_MEDIA("org.joinmastodon.search.query:has:media", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_IMAGE("org.joinmastodon.search.query:has:image", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_VIDEO("org.joinmastodon.search.query:has:video", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_AUDIO("org.joinmastodon.search.query:has:audio", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_POLL("org.joinmastodon.search.query:has:poll", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_LINK("org.joinmastodon.search.query:has:link", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_HAS_EMBED("org.joinmastodon.search.query:has:embed", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_IS_REPLY("org.joinmastodon.search.query:is:reply", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_IS_SENSITIVE("org.joinmastodon.search.query:is:sensitive", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_IN_LIBRARY("org.joinmastodon.search.query:in:library", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_IN_PUBLIC("org.joinmastodon.search.query:in:public", listOf(Version(major = 1))),
ORG_JOINMASTODON_SEARCH_QUERY_BY_DATE("org.joinmastodon.search.query:in:public", listOf(Version(major = 1))),
}
data class Server( data class Server(
val kind: ServerKind, val kind: ServerKind,
val version: Version, val version: Version,
@ -138,10 +80,10 @@ data class Server(
* @return true if the server supports the given operation at the given minimum version * @return true if the server supports the given operation at the given minimum version
* level, false otherwise. * level, false otherwise.
*/ */
fun can(operation: ServerOperation, constraint: Constraint) = capabilities[operation]?.let { fun can(operation: ServerOperation, constraint: Constraint) =
version -> capabilities[operation]?.let { version ->
version satisfies constraint version satisfies constraint
} ?: false } ?: false
companion object { companion object {
/** /**
@ -401,49 +343,3 @@ data class Server(
} }
} }
} }
/**
* Servers that are known to implement the Mastodon client API
*/
enum class ServerKind {
AKKOMA,
FEDIBIRD,
FIREFISH,
FRIENDICA,
GLITCH,
GOTOSOCIAL,
HOMETOWN,
ICESHRIMP,
MASTODON,
PLEROMA,
PIXELFED,
SHARKEY,
/**
* Catch-all for servers we don't recognise but that responded to either
* /api/v1/instance or /api/v2/instance
*/
UNKNOWN,
;
companion object {
fun from(s: NodeInfo.Software) = when (s.name.lowercase()) {
"akkoma" -> AKKOMA
"fedibird" -> FEDIBIRD
"firefish" -> FIREFISH
"friendica" -> FRIENDICA
"gotosocial" -> GOTOSOCIAL
"hometown" -> HOMETOWN
"iceshrimp" -> ICESHRIMP
"mastodon" -> {
// Glitch doesn't report a different software name it stuffs it
// in the version (https://github.com/glitch-soc/mastodon/issues/2582).
if (s.version.contains("+glitch")) GLITCH else MASTODON
}
"pixelfed" -> PIXELFED
"pleroma" -> PLEROMA
"sharkey" -> SHARKEY
else -> UNKNOWN
}
}
}

View File

@ -19,10 +19,9 @@ package app.pachli.core.network.model.nodeinfo
import androidx.annotation.StringRes import androidx.annotation.StringRes
import app.pachli.core.common.PachliError import app.pachli.core.common.PachliError
import app.pachli.core.model.NodeInfo
import app.pachli.core.model.NodeInfo.Software
import app.pachli.core.network.R import app.pachli.core.network.R
import app.pachli.core.network.model.nodeinfo.NodeInfo.Error.NoSoftwareBlock
import app.pachli.core.network.model.nodeinfo.NodeInfo.Error.NoSoftwareName
import app.pachli.core.network.model.nodeinfo.NodeInfo.Error.NoSoftwareVersion
import com.github.michaelbull.result.Err import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result import com.github.michaelbull.result.Result
@ -49,30 +48,20 @@ data class UnvalidatedJrd(val links: List<Link>) {
data class UnvalidatedNodeInfo(val software: Software?) { data class UnvalidatedNodeInfo(val software: Software?) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class Software(val name: String?, val version: String?) data class Software(val name: String?, val version: String?)
}
/** fun validate(): Result<NodeInfo, Error> {
* A validated NodeInfo. return when {
* software == null -> Err(Error.NoSoftwareBlock)
* See https://nodeinfo.diaspora.software/protocol.html and software.name.isNullOrBlank() -> Err(Error.NoSoftwareName)
* https://nodeinfo.diaspora.software/schema.html. software.version.isNullOrBlank() -> Err(Error.NoSoftwareVersion)
*/ else -> Ok(
data class NodeInfo(val software: Software) { NodeInfo(
data class Software( NodeInfo.Software(
/** Software name, won't be null, empty, or blank */ software.name,
val name: String, software.version,
/** Software version, won't be null, empty, or blank */ ),
val version: String, ),
) )
companion object {
fun from(nodeInfo: UnvalidatedNodeInfo): Result<NodeInfo, Error> {
return when {
nodeInfo.software == null -> Err(NoSoftwareBlock)
nodeInfo.software.name.isNullOrBlank() -> Err(NoSoftwareName)
nodeInfo.software.version.isNullOrBlank() -> Err(NoSoftwareVersion)
else -> Ok(NodeInfo(Software(nodeInfo.software.name, nodeInfo.software.version)))
}
} }
} }

View File

@ -17,29 +17,31 @@
package app.pachli.core.network package app.pachli.core.network
import app.pachli.core.network.ServerKind.AKKOMA import app.pachli.core.model.NodeInfo
import app.pachli.core.network.ServerKind.FIREFISH import app.pachli.core.model.ServerKind
import app.pachli.core.network.ServerKind.FRIENDICA import app.pachli.core.model.ServerKind.AKKOMA
import app.pachli.core.network.ServerKind.GOTOSOCIAL import app.pachli.core.model.ServerKind.FIREFISH
import app.pachli.core.network.ServerKind.MASTODON import app.pachli.core.model.ServerKind.FRIENDICA
import app.pachli.core.network.ServerKind.PLEROMA import app.pachli.core.model.ServerKind.GOTOSOCIAL
import app.pachli.core.network.ServerKind.UNKNOWN import app.pachli.core.model.ServerKind.MASTODON
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_FILTERS_CLIENT import app.pachli.core.model.ServerKind.PLEROMA
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_FILTERS_SERVER import app.pachli.core.model.ServerKind.UNKNOWN
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_BY_DATE import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_FILTERS_CLIENT
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_FROM import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_FILTERS_SERVER
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_AUDIO import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_BY_DATE
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_EMBED import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_FROM
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_IMAGE import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_AUDIO
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_LINK import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_EMBED
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_MEDIA import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_IMAGE
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_POLL import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_LINK
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_VIDEO import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_MEDIA
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_LIBRARY import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_POLL
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_REPLY import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_HAS_VIDEO
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_SENSITIVE import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IN_LIBRARY
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_LANGUAGE import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_REPLY
import app.pachli.core.network.ServerOperation.ORG_JOINMASTODON_STATUSES_TRANSLATE import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_IS_SENSITIVE
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_SEARCH_QUERY_LANGUAGE
import app.pachli.core.model.ServerOperation.ORG_JOINMASTODON_STATUSES_TRANSLATE
import app.pachli.core.network.model.Account import app.pachli.core.network.model.Account
import app.pachli.core.network.model.Configuration import app.pachli.core.network.model.Configuration
import app.pachli.core.network.model.Contact import app.pachli.core.network.model.Contact
@ -54,7 +56,6 @@ import app.pachli.core.network.model.Registrations
import app.pachli.core.network.model.Thumbnail import app.pachli.core.network.model.Thumbnail
import app.pachli.core.network.model.Usage import app.pachli.core.network.model.Usage
import app.pachli.core.network.model.Users import app.pachli.core.network.model.Users
import app.pachli.core.network.model.nodeinfo.NodeInfo
import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result import com.github.michaelbull.result.Result
import com.google.common.truth.Truth.assertWithMessage import com.google.common.truth.Truth.assertWithMessage