Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check server version against app minimum api version #737

Merged
merged 4 commits into from
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions app/src/main/java/com/jerboa/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import androidx.activity.viewModels
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
Expand All @@ -22,6 +24,8 @@ import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import androidx.navigation.navDeepLink
import arrow.core.Either
import com.jerboa.api.ApiState
import com.jerboa.api.MINIMUM_API_VERSION
import com.jerboa.datatypes.types.GetCommunity
import com.jerboa.datatypes.types.GetPersonDetails
import com.jerboa.datatypes.types.GetPersonMentions
Expand All @@ -43,6 +47,7 @@ import com.jerboa.ui.components.comment.reply.CommentReplyActivity
import com.jerboa.ui.components.comment.reply.CommentReplyViewModel
import com.jerboa.ui.components.common.MarkdownHelper
import com.jerboa.ui.components.common.ShowChangelog
import com.jerboa.ui.components.common.ShowOutdatedServerDialog
import com.jerboa.ui.components.common.getCurrentAccount
import com.jerboa.ui.components.common.getCurrentAccountSync
import com.jerboa.ui.components.community.CommunityActivity
Expand Down Expand Up @@ -126,6 +131,7 @@ class MainActivity : ComponentActivity() {
) {
val navController = rememberNavController()
val ctx = LocalContext.current
var serverVersionOutdatedViewed = remember { mutableStateOf(false) }

MarkdownHelper.init(
navController,
Expand All @@ -135,6 +141,19 @@ class MainActivity : ComponentActivity() {

ShowChangelog(appSettingsViewModel = appSettingsViewModel)

when (val siteRes = siteViewModel.siteRes) {
is ApiState.Success -> {
val siteVersion = siteRes.data.version
if (compareVersions(siteVersion, MINIMUM_API_VERSION) < 0 && !serverVersionOutdatedViewed.value) {
ShowOutdatedServerDialog(
siteVersion = siteVersion,
onConfirm = { serverVersionOutdatedViewed.value = true },
)
}
}
else -> {}
}

NavHost(
navController = navController,
startDestination = "home",
Expand Down
19 changes: 19 additions & 0 deletions app/src/main/java/com/jerboa/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.lerp
import androidx.core.util.PatternsCompat
import androidx.navigation.NavController
import arrow.core.compareTo
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.jerboa.api.API
Expand Down Expand Up @@ -1229,3 +1230,21 @@ fun scrollToPreviousParentComment(
}
}
}

/**
* Compare two version strings.
*
* This attempts to do a natural comparison assuming it's a typical semver (e.g. x.y.z),
* but it ignores anything it doesn't understand. Since we're highly confident that these verisons
* will be properly formed, this is safe enough without overcomplicating it.
*/
fun compareVersions(a: String, b: String): Int {
val versionA: List<Int> = a.split('.').mapNotNull { it.toIntOrNull() }
val versionB: List<Int> = b.split('.').mapNotNull { it.toIntOrNull() }

val comparison = versionA.compareTo(versionB)
if (comparison == 0) {
return a.compareTo(b)
}
return comparison
}
1 change: 1 addition & 0 deletions app/src/main/java/com/jerboa/api/Http.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import java.io.InputStream

const val VERSION = "v3"
const val DEFAULT_INSTANCE = "lemmy.ml"
const val MINIMUM_API_VERSION: String = "0.18"

interface API {
@GET("site")
Expand Down
25 changes: 25 additions & 0 deletions app/src/main/java/com/jerboa/ui/components/common/Dialogs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import androidx.compose.ui.tooling.preview.Preview
import com.jerboa.PostViewMode
import com.jerboa.R
import com.jerboa.UnreadOrAll
import com.jerboa.api.MINIMUM_API_VERSION
import com.jerboa.datatypes.types.CommentSortType
import com.jerboa.datatypes.types.ListingType
import com.jerboa.datatypes.types.SortType
Expand Down Expand Up @@ -368,3 +369,27 @@ fun ShowChangelog(appSettingsViewModel: AppSettingsViewModel) {
}
}
}

@Composable
fun ShowOutdatedServerDialog(siteVersion: String, onConfirm: () -> Unit) {
AlertDialog(
text = {
Text(
stringResource(
R.string.dialogs_server_version_outdated,
siteVersion,
MINIMUM_API_VERSION,
),
)
},
onDismissRequest = { },
confirmButton = {
Button(
onClick = onConfirm,
content = {
Text(stringResource(id = R.string.input_fields_ok))
},
)
},
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import androidx.navigation.NavController
import com.jerboa.R
import com.jerboa.api.API
import com.jerboa.api.ApiState
import com.jerboa.api.MINIMUM_API_VERSION
import com.jerboa.api.apiWrapper
import com.jerboa.api.retrofitErrorHandler
import com.jerboa.compareVersions
import com.jerboa.datatypes.types.GetPosts
import com.jerboa.datatypes.types.GetSite
import com.jerboa.datatypes.types.Login
Expand Down Expand Up @@ -93,32 +95,49 @@ class LoginViewModel : ViewModel() {
).show()
}
is ApiState.Success -> {
val luv = siteRes.data.my_user!!.local_user_view
val account = Account(
id = luv.person.id,
name = luv.person.name,
current = true,
instance = instance,
jwt = jwt,
defaultListingType = luv.local_user.default_listing_type.ordinal,
defaultSortType = luv.local_user.default_sort_type.ordinal,
)
val siteVersion = siteRes.data.version
if (compareVersions(siteVersion, MINIMUM_API_VERSION) < 0) {
val message = ctx.resources.getString(
R.string.dialogs_server_version_outdated_short,
siteVersion,
)
Toast.makeText(ctx, message, Toast.LENGTH_SHORT).show()
}

homeViewModel.resetPage()
homeViewModel.getPosts(
GetPosts(
type_ = luv.local_user.default_listing_type,
sort = luv.local_user.default_sort_type,
page = homeViewModel.page,
auth = account.jwt,
),
)
try {
val luv = siteRes.data.my_user!!.local_user_view
val account = Account(
id = luv.person.id,
name = luv.person.name,
current = true,
instance = instance,
jwt = jwt,
defaultListingType = luv.local_user.default_listing_type.ordinal,
defaultSortType = luv.local_user.default_sort_type.ordinal,
)

homeViewModel.resetPage()
homeViewModel.getPosts(
GetPosts(
type_ = luv.local_user.default_listing_type,
sort = luv.local_user.default_sort_type,
page = homeViewModel.page,
auth = account.jwt,
),
)

// Remove the default account
accountViewModel.removeCurrent()
// Remove the default account
accountViewModel.removeCurrent()

// Save that info in the DB
accountViewModel.insert(account)
// Save that info in the DB
accountViewModel.insert(account)
} catch (e: Exception) {
loading = false
Log.e("login", e.toString())
API.changeLemmyInstance(originalInstance)
this.cancel()
return@launch
}

loading = false

Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@
<string name="dialogs_top_year">Top Year</string>
<string name="dialogs_old">Old</string>
<string name="dialogs_unread">Unread</string>
<string name="dialogs_server_version_outdated">Server version (%1$s) is under the minimum supported version (%2$s).\n\nPlease inform your administrator and login to another instance, or sign out and use the default instance.</string>
<string name="dialogs_server_version_outdated_short">Server version (%1$s) too low.</string>
<string name="floating_createPost">Create post</string>
<string name="form_submit">Submit</string>
<string name="homeHeader_filter">Select feed</string>
Expand Down
10 changes: 10 additions & 0 deletions app/src/test/java/com/jerboa/UtilsKtTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,14 @@ class UtilsKtTest {
val durationString = formatDuration(date, true)
assertEquals("1 day", durationString)
}

@Test
fun compareVersions() {
assertEquals(-1, compareVersions("0.0.1", "0.0.2"))
assertEquals(1, compareVersions("0.0.10", "0.0.2"))
assertEquals(1, compareVersions("0.1.10", "0.1.2"))
assertEquals(0, compareVersions("0.1.2", "0.1.2"))
assertEquals(-1, compareVersions("0.1.2-alpha1", "0.1.2-beta1"))
assertEquals(1, compareVersions("0.1.2-beta1", "0.1.2-alpha2"))
}
}