show rules on the login screen (#2746)

* show rules on the login screen

* fix code formatting

* fix code formatting

* fix tests
This commit is contained in:
Konrad Pozniak 2022-11-05 17:37:20 +01:00 committed by GitHub
parent 3fb1173d3f
commit 8e9b356074
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 152 additions and 8 deletions

View File

@ -216,7 +216,7 @@ class LoginActivity : BaseActivity(), Injectable {
.addQueryParameter("response_type", "code")
.addQueryParameter("scope", OAUTH_SCOPES)
.build()
doWebViewAuth.launch(LoginData(url.toString().toUri(), oauthRedirectUri.toUri()))
doWebViewAuth.launch(LoginData(domain, url.toString().toUri(), oauthRedirectUri.toUri()))
}
override fun onStart() {

View File

@ -1,3 +1,18 @@
/* Copyright 2022 Tusky Contributors
*
* This file is a part of Tusky.
*
* 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.
*
* Tusky 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 Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.components.login
import android.annotation.SuppressLint
@ -16,16 +31,23 @@ import android.webkit.WebStorage
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.core.net.toUri
import androidx.lifecycle.lifecycleScope
import com.keylesspalace.tusky.BaseActivity
import com.keylesspalace.tusky.BuildConfig
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.databinding.ActivityLoginWebviewBinding
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.parcelableExtra
import com.keylesspalace.tusky.util.viewBinding
import com.keylesspalace.tusky.util.visible
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
import javax.inject.Inject
/** Contract for starting [LoginWebViewActivity]. */
class OauthLogin : ActivityResultContract<LoginData, LoginResult>() {
@ -62,6 +84,7 @@ class OauthLogin : ActivityResultContract<LoginData, LoginResult>() {
@Parcelize
data class LoginData(
val domain: String,
val url: Uri,
val oauthRedirectUrl: Uri,
) : Parcelable
@ -81,6 +104,11 @@ sealed class LoginResult : Parcelable {
class LoginWebViewActivity : BaseActivity(), Injectable {
private val binding by viewBinding(ActivityLoginWebviewBinding::inflate)
@Inject
lateinit var viewModelFactory: ViewModelFactory
private val viewModel: LoginWebViewViewModel by viewModels { viewModelFactory }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -162,6 +190,25 @@ class LoginWebViewActivity : BaseActivity(), Injectable {
} else {
webView.restoreState(savedInstanceState)
}
binding.loginRules.text = getString(R.string.instance_rule_info, data.domain)
viewModel.init(data.domain)
lifecycleScope.launch {
viewModel.instanceRules.collect { instanceRules ->
binding.loginRules.visible(instanceRules.isNotEmpty())
binding.loginRules.setOnClickListener {
AlertDialog.Builder(this@LoginWebViewActivity)
.setTitle(getString(R.string.instance_rule_title, data.domain))
.setMessage(
instanceRules.joinToString(separator = "\n\n") { "$it" }
)
.setPositiveButton(android.R.string.ok, null)
.show()
}
}
}
}
override fun onSaveInstanceState(outState: Bundle) {

View File

@ -0,0 +1,47 @@
/* Copyright 2022 Tusky Contributors
*
* This file is a part of Tusky.
*
* 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.
*
* Tusky 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 Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.components.login
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import at.connyduck.calladapter.networkresult.fold
import com.keylesspalace.tusky.network.MastodonApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
class LoginWebViewViewModel @Inject constructor(
private val api: MastodonApi
) : ViewModel() {
val instanceRules: MutableStateFlow<List<String>> = MutableStateFlow(emptyList())
private var domain: String? = null
fun init(domain: String) {
if (this.domain == null) {
this.domain = domain
viewModelScope.launch {
api.getInstance(domain).fold({ instance ->
instanceRules.value = instance.rules?.map { rule -> rule.text }.orEmpty()
}, { throwable ->
Log.w("LoginWebViewViewModel", "failed to load instance info", throwable)
})
}
}
}
}

View File

@ -10,6 +10,7 @@ import com.keylesspalace.tusky.components.announcements.AnnouncementsViewModel
import com.keylesspalace.tusky.components.compose.ComposeViewModel
import com.keylesspalace.tusky.components.conversation.ConversationsViewModel
import com.keylesspalace.tusky.components.drafts.DraftsViewModel
import com.keylesspalace.tusky.components.login.LoginWebViewViewModel
import com.keylesspalace.tusky.components.report.ReportViewModel
import com.keylesspalace.tusky.components.scheduled.ScheduledStatusViewModel
import com.keylesspalace.tusky.components.search.SearchViewModel
@ -119,5 +120,11 @@ abstract class ViewModelModule {
@IntoMap
@ViewModelKey(AccountMediaViewModel::class)
internal abstract fun accountMediaViewModel(viewModel: AccountMediaViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(LoginWebViewViewModel::class)
internal abstract fun loginWebViewViewModel(viewModel: LoginWebViewViewModel): ViewModel
// Add more ViewModels here
}

View File

@ -33,7 +33,8 @@ data class Instance(
val configuration: InstanceConfiguration?,
@SerializedName("max_media_attachments") val maxMediaAttachments: Int?,
val pleroma: PleromaConfiguration?,
@SerializedName("upload_limit") val uploadLimit: Int?
@SerializedName("upload_limit") val uploadLimit: Int?,
val rules: List<InstanceRules>?
) {
override fun hashCode(): Int {
return uri.hashCode()
@ -90,3 +91,8 @@ data class PleromaFieldLimits(
@SerializedName("name_length") val nameLength: Int?,
@SerializedName("value_length") val valueLength: Int?
)
data class InstanceRules(
val id: String,
val text: String
)

View File

@ -79,7 +79,7 @@ interface MastodonApi {
suspend fun getCustomEmojis(): NetworkResult<List<Emoji>>
@GET("api/v1/instance")
suspend fun getInstance(): NetworkResult<Instance>
suspend fun getInstance(@Header(DOMAIN_HEADER) domain: String? = null): NetworkResult<Instance>
@GET("api/v1/filters")
fun getFilters(): Single<List<Filter>>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="48"
android:viewportHeight="48"
android:tint="?attr/colorPrimary">
<path
android:fillColor="@android:color/white"
android:pathData="M22.65,34H25.65V22H22.65ZM24,18.3Q24.7,18.3 25.175,17.85Q25.65,17.4 25.65,16.7Q25.65,16 25.175,15.5Q24.7,15 24,15Q23.3,15 22.825,15.5Q22.35,16 22.35,16.7Q22.35,17.4 22.825,17.85Q23.3,18.3 24,18.3ZM24,44Q19.9,44 16.25,42.425Q12.6,40.85 9.875,38.125Q7.15,35.4 5.575,31.75Q4,28.1 4,23.95Q4,19.85 5.575,16.2Q7.15,12.55 9.875,9.85Q12.6,7.15 16.25,5.575Q19.9,4 24.05,4Q28.15,4 31.8,5.575Q35.45,7.15 38.15,9.85Q40.85,12.55 42.425,16.2Q44,19.85 44,24Q44,28.1 42.425,31.75Q40.85,35.4 38.15,38.125Q35.45,40.85 31.8,42.425Q28.15,44 24,44ZM24.05,41Q31.1,41 36.05,36.025Q41,31.05 41,23.95Q41,16.9 36.05,11.95Q31.1,7 24,7Q16.95,7 11.975,11.95Q7,16.9 7,24Q7,31.05 11.975,36.025Q16.95,41 24.05,41ZM24,24Q24,24 24,24Q24,24 24,24Q24,24 24,24Q24,24 24,24Q24,24 24,24Q24,24 24,24Q24,24 24,24Q24,24 24,24Z"/>
</vector>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -19,12 +20,33 @@
android:id="@+id/loginProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
android:layout_gravity="center" />
<WebView
android:id="@+id/loginWebView"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
android:orientation="vertical"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<WebView
android:id="@+id/loginWebView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<TextView
android:id="@+id/loginRules"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/colorBackground"
android:drawablePadding="8dp"
android:foreground="?attr/selectableItemBackgroundBorderless"
android:lineSpacingMultiplier="1.1"
android:paddingHorizontal="24dp"
android:paddingVertical="12dp"
app:drawableStartCompat="@drawable/info_24dp"
tools:text="By logging in you agree to the rules of instance.example" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -670,4 +670,7 @@
<string name="delete_scheduled_post_warning">Delete this scheduled post?</string>
<string name="instance_rule_info">By logging in you agree to the rules of %s.</string>
<string name="instance_rule_title">%s rules</string>
</resources>

View File

@ -480,7 +480,8 @@ class ComposeActivityTest {
configuration = configuration,
maxMediaAttachments = null,
pleroma = null,
uploadLimit = null
uploadLimit = null,
rules = emptyList()
)
}