diff --git a/app/build.gradle b/app/build.gradle index 49546d3..efff452 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,7 +55,6 @@ dependencies { // afollestad implementation 'com.afollestad.material-dialogs:core:' + versions.materialDialogs - implementation 'com.afollestad:vvalidator:' + versions.vvalidator // Debugging implementation 'com.jakewharton.timber:timber:' + versions.timber diff --git a/app/src/main/java/com/afollestad/nocknock/ui/addsite/AddSiteActivity.kt b/app/src/main/java/com/afollestad/nocknock/ui/addsite/AddSiteActivity.kt index 26a21f9..1c38e97 100644 --- a/app/src/main/java/com/afollestad/nocknock/ui/addsite/AddSiteActivity.kt +++ b/app/src/main/java/com/afollestad/nocknock/ui/addsite/AddSiteActivity.kt @@ -31,11 +31,13 @@ import com.afollestad.nocknock.utilities.ext.onTextChanged import com.afollestad.nocknock.utilities.ext.setTextAndMaintainSelection import com.afollestad.nocknock.utilities.livedata.distinct import com.afollestad.nocknock.viewcomponents.ext.dimenFloat +import com.afollestad.nocknock.viewcomponents.ext.isVisibleCondition import com.afollestad.nocknock.viewcomponents.ext.onScroll import com.afollestad.nocknock.viewcomponents.livedata.attachLiveData -import com.afollestad.nocknock.viewcomponents.livedata.toViewError import com.afollestad.nocknock.viewcomponents.livedata.toViewText import com.afollestad.nocknock.viewcomponents.livedata.toViewVisibility +import com.afollestad.vvalidator.form +import com.afollestad.vvalidator.form.Form import kotlinx.android.synthetic.main.activity_addsite.checkIntervalLayout import kotlinx.android.synthetic.main.activity_addsite.headersLayout import kotlinx.android.synthetic.main.activity_addsite.inputName @@ -64,13 +66,15 @@ class AddSiteActivity : DarkModeSwitchActivity() { } private val viewModel by viewModel() + private lateinit var validationForm: Form @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_addsite) - setupUi() + setupUi() + setupValidation() lifecycle.addObserver(viewModel) // Populate view model with initial data @@ -82,23 +86,17 @@ class AddSiteActivity : DarkModeSwitchActivity() { // Name inputName.attachLiveData(this, viewModel.name) - viewModel.onNameError() - .toViewError(this, inputName) // Tags inputTags.attachLiveData(this, viewModel.tags) // Url inputUrl.attachLiveData(this, viewModel.url) - viewModel.onUrlError() - .toViewError(this, inputUrl) viewModel.onUrlWarningVisibility() .toViewVisibility(this, textUrlWarning) // Timeout responseTimeoutInput.attachLiveData(this, viewModel.timeout) - viewModel.onTimeoutError() - .toViewError(this, responseTimeoutInput) // Validation mode responseValidationMode.attachLiveData( @@ -107,8 +105,6 @@ class AddSiteActivity : DarkModeSwitchActivity() { outTransformer = { ValidationMode.fromIndex(it) }, inTransformer = { it.toIndex() } ) - viewModel.onValidationSearchTermError() - .toViewError(this, responseValidationSearchTerm) viewModel.onValidationModeDescription() .toViewText(this, validationModeDescription) @@ -124,15 +120,15 @@ class AddSiteActivity : DarkModeSwitchActivity() { // Validation script scriptInputLayout.attach( codeData = viewModel.validationScript, - errorData = viewModel.onValidationScriptError(), - visibility = viewModel.onValidationScriptVisibility() + visibility = viewModel.onValidationScriptVisibility(), + form = validationForm ) // Check interval checkIntervalLayout.attach( valueData = viewModel.checkIntervalValue, multiplierData = viewModel.checkIntervalUnit, - errorData = viewModel.onCheckIntervalError() + form = validationForm ) // Retry Policy @@ -145,8 +141,6 @@ class AddSiteActivity : DarkModeSwitchActivity() { sslCertificateInput.onTextChanged { viewModel.certificateUri.value = it } viewModel.certificateUri.distinct() .observe(this, Observer { sslCertificateInput.setTextAndMaintainSelection(it) }) - viewModel.onCertificateError() - .toViewError(this, sslCertificateInput) // Headers headersLayout.attach(viewModel.headers) @@ -156,15 +150,6 @@ class AddSiteActivity : DarkModeSwitchActivity() { toolbarTitle.setText(R.string.add_site) toolbar.run { inflateMenu(R.menu.menu_addsite) - setOnMenuItemClickListener { - if (it.itemId == R.id.commit) { - viewModel.commit { - setResult(RESULT_OK) - finish() - } - } - true - } setNavigationIcon(R.drawable.ic_action_close) setNavigationOnClickListener { finish() } } @@ -195,6 +180,38 @@ class AddSiteActivity : DarkModeSwitchActivity() { } } + private fun setupValidation() { + validationForm = form { + input(inputName, name = "Name") { + isNotEmpty().description(R.string.please_enter_name) + } + input(inputUrl, name = "URL") { + isNotEmpty().description(R.string.please_enter_url) + isUrl().description(R.string.please_enter_valid_url) + } + input(responseTimeoutInput, name = "Timeout", optional = true) { + isNumber().greaterThan(0) + .description(R.string.please_enter_networkTimeout) + } + input(responseValidationSearchTerm, name = "Search term") { + conditional(responseValidationSearchTerm.isVisibleCondition()) { + isNotEmpty().description(R.string.please_enter_search_term) + } + } + input(sslCertificateInput, name = "Certificate Path") { + isUri().hasScheme("file", "content") + .that { it.host != null } + .description(R.string.please_enter_validCertUri) + } + submitWith(toolbar.menu, R.id.commit) { + viewModel.commit { + setResult(RESULT_OK) + finish() + } + } + } + } + override fun onResume() { super.onResume() appToolbar.elevation = if (scrollView.scrollY > appToolbar.measuredHeight / 2) { diff --git a/app/src/main/java/com/afollestad/nocknock/ui/addsite/AddSiteViewModel.kt b/app/src/main/java/com/afollestad/nocknock/ui/addsite/AddSiteViewModel.kt index 87da65c..106f232 100644 --- a/app/src/main/java/com/afollestad/nocknock/ui/addsite/AddSiteViewModel.kt +++ b/app/src/main/java/com/afollestad/nocknock/ui/addsite/AddSiteViewModel.kt @@ -39,9 +39,7 @@ import com.afollestad.nocknock.data.putSite import com.afollestad.nocknock.engine.validation.ValidationExecutor import com.afollestad.nocknock.ui.ScopedViewModel import com.afollestad.nocknock.utilities.ext.MINUTE -import com.afollestad.nocknock.utilities.ext.toUri import com.afollestad.nocknock.utilities.livedata.map -import com.afollestad.nocknock.viewcomponents.ext.isNullOrLessThan import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -83,23 +81,10 @@ class AddSiteViewModel( headers.value = emptyList() } - // Private properties private val isLoading = MutableLiveData() - private val nameError = MutableLiveData() - private val urlError = MutableLiveData() - private val timeoutError = MutableLiveData() - private val validationSearchTermError = MutableLiveData() - private val validationScriptError = MutableLiveData() - private val checkIntervalValueError = MutableLiveData() - private val certificateError = MutableLiveData() - // Expose private properties or calculated properties @CheckResult fun onIsLoading(): LiveData = isLoading - @CheckResult fun onNameError(): LiveData = nameError - - @CheckResult fun onUrlError(): LiveData = urlError - @CheckResult fun onUrlWarningVisibility(): LiveData { return url.map { val parsed = HttpUrl.parse(it) @@ -107,8 +92,6 @@ class AddSiteViewModel( } } - @CheckResult fun onTimeoutError(): LiveData = timeoutError - @CheckResult fun onValidationModeDescription(): LiveData { return validationMode.map { when (it!!) { @@ -119,19 +102,9 @@ class AddSiteViewModel( } } - @CheckResult fun onValidationSearchTermError(): LiveData = validationSearchTermError + @CheckResult fun onValidationSearchTermVisibility() = validationMode.map { it == TERM_SEARCH } - @CheckResult fun onValidationSearchTermVisibility() = - validationMode.map { it == TERM_SEARCH } - - @CheckResult fun onValidationScriptError(): LiveData = validationScriptError - - @CheckResult fun onValidationScriptVisibility() = - validationMode.map { it == JAVASCRIPT } - - @CheckResult fun onCheckIntervalError(): LiveData = checkIntervalValueError - - @CheckResult fun onCertificateError(): LiveData = certificateError + @CheckResult fun onValidationScriptVisibility() = validationMode.map { it == JAVASCRIPT } // Actions fun commit(done: () -> Unit) { @@ -171,89 +144,7 @@ class AddSiteViewModel( } private fun generateDbModel(): Site? { - var errorCount = 0 - - // Validation name - if (name.value.isNullOrEmpty()) { - nameError.value = R.string.please_enter_name - errorCount++ - } else { - nameError.value = null - } - - // Validate URL - when { - url.value.isNullOrEmpty() -> { - urlError.value = R.string.please_enter_url - errorCount++ - } - HttpUrl.parse(url.value!!) == null -> { - urlError.value = R.string.please_enter_valid_url - errorCount++ - } - else -> { - urlError.value = null - } - } - - // Validate timeout val timeout = timeout.value ?: 10_000 - if (timeout < 0) { - timeoutError.value = R.string.please_enter_networkTimeout - errorCount++ - } else { - timeoutError.value = null - } - - // Validate check interval - if (checkIntervalValue.value.isNullOrLessThan(1)) { - checkIntervalValueError.value = R.string.please_enter_check_interval - errorCount++ - } else { - checkIntervalValueError.value = null - } - - // Validate arguments - if (validationMode.value == TERM_SEARCH && - validationSearchTerm.value.isNullOrEmpty() - ) { - errorCount++ - validationSearchTermError.value = R.string.please_enter_search_term - validationScriptError.value = null - } else if (validationMode.value == JAVASCRIPT && - validationScript.value.isNullOrEmpty() - ) { - errorCount++ - validationSearchTermError.value = null - validationScriptError.value = R.string.please_enter_javaScript - } else { - validationSearchTermError.value = null - validationScriptError.value = null - } - - // Validate SSL certificate - val certString = certificateUri.value - if (certString != null) { - val rawCertUri = certString.toUri() - val certUri = if (rawCertUri.scheme == null) { - rawCertUri.buildUpon() - .scheme("file") - .build() - } else { - rawCertUri - } - if (certUri.scheme != "content" && certUri.scheme != "file") { - errorCount++ - certificateError.value = R.string.please_enter_validCertUri - } else { - certificateError.value = null - } - } - - if (errorCount > 0) { - return null - } - val cleanedTags = tags.value?.split(',')?.joinToString { it.trim() } ?: "" val newSettings = SiteSettings( @@ -275,7 +166,8 @@ class AddSiteViewModel( val retryPolicyMinutes = retryPolicyMinutes.value ?: 0 val newRetryPolicy: RetryPolicy? = if (retryPolicyTimes > 0 && retryPolicyMinutes > 0) { RetryPolicy( - count = retryPolicyTimes, minutes = retryPolicyMinutes + count = retryPolicyTimes, + minutes = retryPolicyMinutes ) } else { null diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2b09271..2267417 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -29,9 +29,7 @@ Please enter a name! Please enter a URL. Please enter a valid URL. - Please input a validation interval. Please input a search term. - Please input a validation script. Please enter a network timeout greater than 0. Certificate should be a valid file or content URI. diff --git a/app/src/test/java/com/afollestad/nocknock/ui/addsite/AddSiteViewModelTest.kt b/app/src/test/java/com/afollestad/nocknock/ui/addsite/AddSiteViewModelTest.kt index eb86c29..b62f9a6 100644 --- a/app/src/test/java/com/afollestad/nocknock/ui/addsite/AddSiteViewModelTest.kt +++ b/app/src/test/java/com/afollestad/nocknock/ui/addsite/AddSiteViewModelTest.kt @@ -149,247 +149,9 @@ class AddSiteViewModelTest { assertThat(viewModel.getValidationArgs()).isEqualTo("Two") } - @Test fun commit_nameError() { - val onNameError = viewModel.onNameError() - .test() - val onUrlError = viewModel.onUrlError() - .test() - val onTimeoutError = viewModel.onTimeoutError() - .test() - val onCheckIntervalError = viewModel.onCheckIntervalError() - .test() - val onSearchTermError = viewModel.onValidationSearchTermError() - .test() - val onScriptError = viewModel.onValidationScriptError() - .test() - - fillInModel().apply { - name.value = "" - } - val onDone = mock<() -> Unit>() - viewModel.commit(onDone) - - verify(validationManager, never()) - .scheduleValidation(any(), any(), any(), any()) - onNameError.assertValues(R.string.please_enter_name) - onUrlError.assertNoValues() - onTimeoutError.assertNoValues() - onCheckIntervalError.assertNoValues() - onSearchTermError.assertNoValues() - onScriptError.assertNoValues() - - verify(onDone, never()).invoke() - } - - @Test fun commit_urlEmptyError() { - val onNameError = viewModel.onNameError() - .test() - val onUrlError = viewModel.onUrlError() - .test() - val onTimeoutError = viewModel.onTimeoutError() - .test() - val onCheckIntervalError = viewModel.onCheckIntervalError() - .test() - val onSearchTermError = viewModel.onValidationSearchTermError() - .test() - val onScriptError = viewModel.onValidationScriptError() - .test() - - fillInModel().apply { - url.value = "" - } - val onDone = mock<() -> Unit>() - viewModel.commit(onDone) - - verify(validationManager, never()) - .scheduleValidation(any(), any(), any(), any()) - onNameError.assertNoValues() - onUrlError.assertValues(R.string.please_enter_url) - onTimeoutError.assertNoValues() - onCheckIntervalError.assertNoValues() - onSearchTermError.assertNoValues() - onScriptError.assertNoValues() - - verify(onDone, never()).invoke() - } - - @Test fun commit_urlFormatError() { - val onNameError = viewModel.onNameError() - .test() - val onUrlError = viewModel.onUrlError() - .test() - val onTimeoutError = viewModel.onTimeoutError() - .test() - val onCheckIntervalError = viewModel.onCheckIntervalError() - .test() - val onSearchTermError = viewModel.onValidationSearchTermError() - .test() - val onScriptError = viewModel.onValidationScriptError() - .test() - - fillInModel().apply { - url.value = "ftp://www.idk.com" - } - val onDone = mock<() -> Unit>() - viewModel.commit(onDone) - - verify(validationManager, never()) - .scheduleValidation(any(), any(), any(), any()) - onNameError.assertNoValues() - onUrlError.assertValues(R.string.please_enter_valid_url) - onTimeoutError.assertNoValues() - onCheckIntervalError.assertNoValues() - onSearchTermError.assertNoValues() - onScriptError.assertNoValues() - - verify(onDone, never()).invoke() - } - - @Test fun commit_networkTimeout_error() { - val onNameError = viewModel.onNameError() - .test() - val onUrlError = viewModel.onUrlError() - .test() - val onTimeoutError = viewModel.onTimeoutError() - .test() - val onCheckIntervalError = viewModel.onCheckIntervalError() - .test() - val onSearchTermError = viewModel.onValidationSearchTermError() - .test() - val onScriptError = viewModel.onValidationScriptError() - .test() - - fillInModel().apply { - timeout.value = 0 - } - val onDone = mock<() -> Unit>() - viewModel.commit(onDone) - - verify(validationManager, never()) - .scheduleValidation(any(), any(), any(), any()) - onNameError.assertNoValues() - onUrlError.assertNoValues() - onTimeoutError.assertValues(R.string.please_enter_networkTimeout) - onCheckIntervalError.assertNoValues() - onSearchTermError.assertNoValues() - onScriptError.assertNoValues() - - verify(onDone, never()).invoke() - } - - @Test fun commit_checkIntervalError() { - val onNameError = viewModel.onNameError() - .test() - val onUrlError = viewModel.onUrlError() - .test() - val onTimeoutError = viewModel.onTimeoutError() - .test() - val onCheckIntervalError = viewModel.onCheckIntervalError() - .test() - val onSearchTermError = viewModel.onValidationSearchTermError() - .test() - val onScriptError = viewModel.onValidationScriptError() - .test() - - fillInModel().apply { - checkIntervalValue.value = 0 - } - val onDone = mock<() -> Unit>() - viewModel.commit(onDone) - - verify(validationManager, never()) - .scheduleValidation(any(), any(), any(), any()) - onNameError.assertNoValues() - onUrlError.assertNoValues() - onTimeoutError.assertNoValues() - onCheckIntervalError.assertValues(R.string.please_enter_check_interval) - onSearchTermError.assertNoValues() - onScriptError.assertNoValues() - - verify(onDone, never()).invoke() - } - - @Test fun commit_termSearchError() { - val onNameError = viewModel.onNameError() - .test() - val onUrlError = viewModel.onUrlError() - .test() - val onTimeoutError = viewModel.onTimeoutError() - .test() - val onCheckIntervalError = viewModel.onCheckIntervalError() - .test() - val onSearchTermError = viewModel.onValidationSearchTermError() - .test() - val onScriptError = viewModel.onValidationScriptError() - .test() - - fillInModel().apply { - validationMode.value = TERM_SEARCH - validationSearchTerm.value = "" - } - val onDone = mock<() -> Unit>() - viewModel.commit(onDone) - - verify(validationManager, never()) - .scheduleValidation(any(), any(), any(), any()) - onNameError.assertNoValues() - onUrlError.assertNoValues() - onTimeoutError.assertNoValues() - onCheckIntervalError.assertNoValues() - onSearchTermError.assertValues(R.string.please_enter_search_term) - onScriptError.assertNoValues() - - verify(onDone, never()).invoke() - } - - @Test fun commit_javaScript_error() { - val onNameError = viewModel.onNameError() - .test() - val onUrlError = viewModel.onUrlError() - .test() - val onTimeoutError = viewModel.onTimeoutError() - .test() - val onCheckIntervalError = viewModel.onCheckIntervalError() - .test() - val onSearchTermError = viewModel.onValidationSearchTermError() - .test() - val onScriptError = viewModel.onValidationScriptError() - .test() - - fillInModel().apply { - validationMode.value = JAVASCRIPT - validationScript.value = "" - } - val onDone = mock<() -> Unit>() - viewModel.commit(onDone) - - verify(validationManager, never()) - .scheduleValidation(any(), any(), any(), any()) - onNameError.assertNoValues() - onUrlError.assertNoValues() - onTimeoutError.assertNoValues() - onCheckIntervalError.assertNoValues() - onSearchTermError.assertNoValues() - onScriptError.assertValues(R.string.please_enter_javaScript) - - verify(onDone, never()).invoke() - } - @Test fun commit_success() = runBlocking { val isLoading = viewModel.onIsLoading() .test() - val onNameError = viewModel.onNameError() - .test() - val onUrlError = viewModel.onUrlError() - .test() - val onTimeoutError = viewModel.onTimeoutError() - .test() - val onSearchTermError = viewModel.onValidationSearchTermError() - .test() - val onScriptError = viewModel.onValidationScriptError() - .test() - val onCheckIntervalError = viewModel.onCheckIntervalError() - .test() fillInModel() val onDone = mock<() -> Unit>() @@ -416,12 +178,6 @@ class AddSiteViewModelTest { cancelPrevious = true, fromFinishingJob = false ) - onNameError.assertNoValues() - onUrlError.assertNoValues() - onTimeoutError.assertNoValues() - onCheckIntervalError.assertNoValues() - onSearchTermError.assertNoValues() - onScriptError.assertNoValues() verify(onDone).invoke() } diff --git a/viewcomponents/build.gradle b/viewcomponents/build.gradle index 8b7cc90..a3ae3dc 100644 --- a/viewcomponents/build.gradle +++ b/viewcomponents/build.gradle @@ -18,6 +18,8 @@ dependencies { implementation project(':common') implementation project(':data') + api 'com.afollestad:vvalidator:' + versions.vvalidator + implementation 'androidx.appcompat:appcompat:' + versions.androidxCore implementation 'com.google.android.material:material:' + versions.googleMaterial api 'androidx.lifecycle:lifecycle-extensions:' + versions.lifecycle diff --git a/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/ext/ViewExt.kt b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/ext/ViewExt.kt index feeef85..445c931 100644 --- a/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/ext/ViewExt.kt +++ b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/ext/ViewExt.kt @@ -17,19 +17,15 @@ package com.afollestad.nocknock.viewcomponents.ext import android.view.View import android.view.View.GONE -import android.view.View.INVISIBLE import android.view.View.VISIBLE import android.view.ViewTreeObserver import androidx.annotation.DimenRes +import com.afollestad.vvalidator.form.Condition fun View.show() { visibility = VISIBLE } -fun View.conceal() { - visibility = INVISIBLE -} - fun View.hide() { visibility = GONE } @@ -51,3 +47,7 @@ fun View.onLayout(cb: () -> Unit) { fun View.dimenFloat(@DimenRes res: Int) = resources.getDimension(res) fun View.dimenInt(@DimenRes res: Int) = resources.getDimensionPixelSize(res) + +fun View.isVisibleCondition(): Condition = { + visibility == VISIBLE +} diff --git a/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/interval/ValidationIntervalLayout.kt b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/interval/ValidationIntervalLayout.kt index 3436dd5..2db60fe 100644 --- a/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/interval/ValidationIntervalLayout.kt +++ b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/interval/ValidationIntervalLayout.kt @@ -19,7 +19,6 @@ import android.content.Context import android.util.AttributeSet import android.widget.ArrayAdapter import android.widget.LinearLayout -import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.afollestad.nocknock.utilities.ext.DAY import com.afollestad.nocknock.utilities.ext.HOUR @@ -28,7 +27,7 @@ import com.afollestad.nocknock.utilities.ext.WEEK import com.afollestad.nocknock.viewcomponents.R import com.afollestad.nocknock.viewcomponents.livedata.attachLiveData import com.afollestad.nocknock.viewcomponents.livedata.lifecycleOwner -import com.afollestad.nocknock.viewcomponents.livedata.toViewError +import com.afollestad.vvalidator.form.Form import kotlinx.android.synthetic.main.validation_interval_layout.view.input import kotlinx.android.synthetic.main.validation_interval_layout.view.spinner @@ -66,8 +65,14 @@ class ValidationIntervalLayout( fun attach( valueData: MutableLiveData, multiplierData: MutableLiveData, - errorData: LiveData + form: Form ) { + form.input(input, name = "Interval") { + isNotEmpty().description(R.string.please_enter_check_interval) + length().greaterThan(0) + .description(R.string.check_interval_must_be_greater_zero) + } + input.attachLiveData(lifecycleOwner(), valueData) spinner.attachLiveData( lifecycleOwner = lifecycleOwner(), @@ -91,10 +96,5 @@ class ValidationIntervalLayout( } } ) - errorData.toViewError(lifecycleOwner(), this, ::setError) - } - - private fun setError(error: String?) { - input.error = error } } diff --git a/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/js/JavaScriptInputLayout.kt b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/js/JavaScriptInputLayout.kt index 5d32e69..176f32d 100644 --- a/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/js/JavaScriptInputLayout.kt +++ b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/js/JavaScriptInputLayout.kt @@ -20,15 +20,17 @@ import android.util.AttributeSet import android.widget.HorizontalScrollView import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import com.afollestad.nocknock.viewcomponents.R import com.afollestad.nocknock.viewcomponents.R.dimen import com.afollestad.nocknock.viewcomponents.R.layout import com.afollestad.nocknock.viewcomponents.ext.dimenFloat import com.afollestad.nocknock.viewcomponents.ext.dimenInt +import com.afollestad.nocknock.viewcomponents.ext.isVisibleCondition import com.afollestad.nocknock.viewcomponents.ext.showOrHide import com.afollestad.nocknock.viewcomponents.livedata.attachLiveData import com.afollestad.nocknock.viewcomponents.livedata.lifecycleOwner -import com.afollestad.nocknock.viewcomponents.livedata.toViewError import com.afollestad.nocknock.viewcomponents.livedata.toViewVisibility +import com.afollestad.vvalidator.form.Form import kotlinx.android.synthetic.main.javascript_input_layout.view.error_text import kotlinx.android.synthetic.main.javascript_input_layout.view.userInput @@ -55,11 +57,20 @@ class JavaScriptInputLayout( fun attach( codeData: MutableLiveData, - errorData: LiveData, - visibility: LiveData + visibility: LiveData, + form: Form ) { + form.input(userInput, name = "Script") { + conditional(isVisibleCondition()) { + isNotEmpty().description(R.string.please_enter_javaScript) + } + onErrors { _, errors -> + val error = errors.firstOrNull() + setError(error.toString()) + } + } + userInput.attachLiveData(lifecycleOwner(), codeData) - errorData.toViewError(lifecycleOwner(), this, ::setError) visibility.toViewVisibility(lifecycleOwner(), this) } diff --git a/viewcomponents/src/main/res/values/strings.xml b/viewcomponents/src/main/res/values/strings.xml index b2462ec..a8f5d3f 100644 --- a/viewcomponents/src/main/res/values/strings.xml +++ b/viewcomponents/src/main/res/values/strings.xml @@ -23,4 +23,8 @@ Header Name Header Value + Please input a validation interval. + The validation interval must be greater than 0. + Please input a validation script. +