diff --git a/app/src/main/java/net/helcel/cowspent/android/main/BillsListScreen.kt b/app/src/main/java/net/helcel/cowspent/android/main/BillsListScreen.kt index 283f037..f1a9e70 100644 --- a/app/src/main/java/net/helcel/cowspent/android/main/BillsListScreen.kt +++ b/app/src/main/java/net/helcel/cowspent/android/main/BillsListScreen.kt @@ -430,6 +430,7 @@ fun BillsListScreen( } }, drawerContent = { + val selectedProject = viewModel.projects.find { it.id == viewModel.selectedProjectId } Drawer( projects = viewModel.projects, members = viewModel.members, @@ -437,6 +438,7 @@ fun BillsListScreen( selectedProjectId = viewModel.selectedProjectId, selectedMemberId = viewModel.selectedMemberId, lastSyncText = viewModel.lastSyncText, + mainCurrency = selectedProject?.currencyName, showArchived = showArchived, onProjectClick = { viewModel.selectedMemberId = null diff --git a/app/src/main/java/net/helcel/cowspent/android/project/create/NewProjectActivity.kt b/app/src/main/java/net/helcel/cowspent/android/project/create/NewProjectActivity.kt index 7bdf5d5..d348563 100644 --- a/app/src/main/java/net/helcel/cowspent/android/project/create/NewProjectActivity.kt +++ b/app/src/main/java/net/helcel/cowspent/android/project/create/NewProjectActivity.kt @@ -21,6 +21,7 @@ import net.helcel.cowspent.model.* import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper import net.helcel.cowspent.theme.ThemeUtils import net.helcel.cowspent.util.* +import java.util.Locale import androidx.core.net.toUri import androidx.core.content.edit import net.helcel.cowspent.model.ProjectType @@ -60,7 +61,8 @@ class NewProjectActivity : AppCompatActivity() { chooseFromNextcloud() }, onOkPressed = { onPressOk() }, - onBack = { finish() } + onBack = { finish() }, + onFieldsChanged = { updateAuthStatus() } ) } } @@ -85,6 +87,8 @@ class NewProjectActivity : AppCompatActivity() { viewModel.projectUrl = defaultNcUrl } } + + updateAuthStatus() val defaultProjectId = intent.getStringExtra(PARAM_DEFAULT_PROJECT_ID) if (defaultProjectId != null) { @@ -139,6 +143,17 @@ class NewProjectActivity : AppCompatActivity() { } } + private fun updateAuthStatus() { + val url = getFormattedUrl() + val fakeProj = DBProject( + 0, "", "", "", url, + "", 0L, viewModel.projectType, 0L, + null, false, DBProject.ACCESS_LEVEL_UNKNOWN, + "" + ) + viewModel.isAuthenticatedAccount = db.cowspentServerSyncHelper.canCreateAuthenticatedProject(fakeProj) + } + private fun onPressOk() { val type = viewModel.projectType val todoCreate = viewModel.whatTodoIsCreate @@ -186,7 +201,16 @@ class NewProjectActivity : AppCompatActivity() { } private fun createProject() { - val isCospendScheme = isCospendSchemeLink(getFormattedUrl()) + val url = getFormattedUrl() + val isCospendScheme = isCospendSchemeLink(url) + + if (viewModel.whatTodoIsCreate && viewModel.projectId.isEmpty() && viewModel.projectName.isNotEmpty()) { + viewModel.projectId = viewModel.projectName.lowercase(Locale.ROOT) + .replace("[^a-z0-9]".toRegex(), "-") + .replace("-+".toRegex(), "-") + .trim('-') + } + val rid = viewModel.projectId if (!isCospendScheme && (rid == "" || rid.contains(",") || rid.contains("/"))) { showToast(getString(R.string.error_invalid_project_remote_id), Toast.LENGTH_LONG) @@ -194,7 +218,7 @@ class NewProjectActivity : AppCompatActivity() { } if (viewModel.projectType != ProjectType.LOCAL && !isCospendScheme) { - if (!isValidUrl(getFormattedUrl())) { + if (!isValidUrl(url)) { showToast("Invalid URL", Toast.LENGTH_SHORT) return } @@ -217,7 +241,16 @@ class NewProjectActivity : AppCompatActivity() { showToast("Invalid project name", Toast.LENGTH_SHORT) return } - if (!SupportUtil.isValidEmail(viewModel.projectEmail)) { + + val fakeProj = DBProject( + 0, rid, "", viewModel.projectName, url, + viewModel.projectEmail, 0L, viewModel.projectType, 0L, + null, false, DBProject.ACCESS_LEVEL_UNKNOWN, + "" + ) + val isAuthenticated = db.cowspentServerSyncHelper.canCreateAuthenticatedProject(fakeProj) + + if (!isAuthenticated && !SupportUtil.isValidEmail(viewModel.projectEmail)) { showToast("Invalid email", Toast.LENGTH_SHORT) return } @@ -226,7 +259,7 @@ class NewProjectActivity : AppCompatActivity() { if (!db.cowspentServerSyncHelper.createRemoteProject( viewModel.projectId, viewModel.projectName, - viewModel.projectEmail, viewModel.projectPassword, getFormattedUrl(), viewModel.projectType, createRemoteCallBack + viewModel.projectEmail, viewModel.projectPassword, url, viewModel.projectType, createRemoteCallBack ) ) { viewModel.isCreatingRemoteProject = false diff --git a/app/src/main/java/net/helcel/cowspent/android/project/create/NewProjectScreen.kt b/app/src/main/java/net/helcel/cowspent/android/project/create/NewProjectScreen.kt index bceb66b..fa12f85 100644 --- a/app/src/main/java/net/helcel/cowspent/android/project/create/NewProjectScreen.kt +++ b/app/src/main/java/net/helcel/cowspent/android/project/create/NewProjectScreen.kt @@ -32,8 +32,13 @@ fun NewProjectScreen( onImportFile: () -> Unit, onChooseFromNextcloud: () -> Unit, onOkPressed: () -> Unit, - onBack: () -> Unit + onBack: () -> Unit, + onFieldsChanged: () -> Unit ) { + LaunchedEffect(viewModel.projectUrl, viewModel.projectType) { + onFieldsChanged() + } + Scaffold( topBar = { TopAppBar( @@ -162,16 +167,18 @@ fun NewProjectScreen( Spacer(modifier = Modifier.height(8.dp)) } - OutlinedTextField( - value = viewModel.projectId, - onValueChange = { viewModel.projectId = it }, - label = { Text(stringResource(R.string.setting_project_id)) }, - modifier = Modifier.fillMaxWidth(), - leadingIcon = { Icon(Icons.AutoMirrored.Filled.LibraryBooks, contentDescription = null) } - ) + if (!viewModel.whatTodoIsCreate || !viewModel.isAuthenticatedAccount || viewModel.projectType != ProjectType.COSPEND) { + OutlinedTextField( + value = viewModel.projectId, + onValueChange = { viewModel.projectId = it }, + label = { Text(stringResource(R.string.setting_project_id)) }, + modifier = Modifier.fillMaxWidth(), + leadingIcon = { Icon(Icons.AutoMirrored.Filled.LibraryBooks, contentDescription = null) } + ) + Spacer(modifier = Modifier.height(8.dp)) + } if (viewModel.projectType != ProjectType.LOCAL && (!viewModel.whatTodoIsCreate || viewModel.projectType == ProjectType.IHATEMONEY)) { - Spacer(modifier = Modifier.height(8.dp)) OutlinedTextField( value = viewModel.projectPassword, onValueChange = { viewModel.projectPassword = it }, @@ -179,10 +186,10 @@ fun NewProjectScreen( modifier = Modifier.fillMaxWidth(), leadingIcon = { Icon(Icons.Default.Lock, contentDescription = null) } ) + Spacer(modifier = Modifier.height(8.dp)) } if (viewModel.whatTodoIsCreate && viewModel.projectType != ProjectType.LOCAL) { - Spacer(modifier = Modifier.height(8.dp)) OutlinedTextField( value = viewModel.projectName, onValueChange = { viewModel.projectName = it }, @@ -190,14 +197,17 @@ fun NewProjectScreen( modifier = Modifier.fillMaxWidth(), leadingIcon = { Icon(Icons.Default.Title, contentDescription = null) } ) - Spacer(modifier = Modifier.height(8.dp)) - OutlinedTextField( - value = viewModel.projectEmail, - onValueChange = { viewModel.projectEmail = it }, - label = { Text(stringResource(R.string.setting_new_project_email)) }, - modifier = Modifier.fillMaxWidth(), - leadingIcon = { Icon(Icons.Default.Email, contentDescription = null) } - ) + + if (!viewModel.isAuthenticatedAccount || viewModel.projectType == ProjectType.IHATEMONEY) { + Spacer(modifier = Modifier.height(8.dp)) + OutlinedTextField( + value = viewModel.projectEmail, + onValueChange = { viewModel.projectEmail = it }, + label = { Text(stringResource(R.string.setting_new_project_email)) }, + modifier = Modifier.fillMaxWidth(), + leadingIcon = { Icon(Icons.Default.Email, contentDescription = null) } + ) + } } } } @@ -334,6 +344,7 @@ fun NewProjectScreenPreview() { onImportFile = {}, onChooseFromNextcloud = {}, onOkPressed = {}, - onBack = {} + onBack = {}, + onFieldsChanged = {} ) } diff --git a/app/src/main/java/net/helcel/cowspent/android/project/create/NewProjectViewModel.kt b/app/src/main/java/net/helcel/cowspent/android/project/create/NewProjectViewModel.kt index b0060c0..16a8b82 100644 --- a/app/src/main/java/net/helcel/cowspent/android/project/create/NewProjectViewModel.kt +++ b/app/src/main/java/net/helcel/cowspent/android/project/create/NewProjectViewModel.kt @@ -30,6 +30,8 @@ class NewProjectViewModel : ViewModel() { var projectName by mutableStateOf("") var projectEmail by mutableStateOf("") + var isAuthenticatedAccount by mutableStateOf(false) + var showAuthWarningDialog by mutableStateOf(false) var showNextcloudProjectDialog by mutableStateOf(false) var nextcloudProjects by mutableStateOf>(emptyList()) @@ -39,12 +41,23 @@ class NewProjectViewModel : ViewModel() { fun isFormValid(): Boolean { if (whatTodoIsCreate) { - if (projectId.isEmpty()) return false - if (projectType != ProjectType.LOCAL) { + if (projectType == ProjectType.LOCAL) { + return projectId.isNotEmpty() + } else { if (projectUrl.isEmpty()) return false if (projectName.isEmpty()) return false - if (projectEmail.isEmpty()) return false - if (projectType == ProjectType.IHATEMONEY && projectPassword.isEmpty()) return false + + if (projectType == ProjectType.COSPEND && isAuthenticatedAccount) { + return true + } + + if (projectId.isEmpty()) return false + if (projectType == ProjectType.IHATEMONEY) { + if (projectEmail.isEmpty()) return false + if (projectPassword.isEmpty()) return false + } else { + if (projectEmail.isEmpty()) return false + } } } else { // Join diff --git a/app/src/main/java/net/helcel/cowspent/persistence/CowspentServerSyncHelper.kt b/app/src/main/java/net/helcel/cowspent/persistence/CowspentServerSyncHelper.kt index fab690b..457b6a6 100644 --- a/app/src/main/java/net/helcel/cowspent/persistence/CowspentServerSyncHelper.kt +++ b/app/src/main/java/net/helcel/cowspent/persistence/CowspentServerSyncHelper.kt @@ -320,48 +320,69 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen } } - val currenciesToDelete = dbHelper.getCurrenciesOfProjectWithState(project.id, DBBill.STATE_DELETED) - for (cToDel in currenciesToDelete) { - try { - val deleteRemoteCurrencyResponse = client!!.deleteRemoteCurrency(project, cToDel.remoteId) - if (deleteRemoteCurrencyResponse.stringContent == "OK") { - Log.d(TAG, "successfully deleted currency on remote project : delete it locally") - dbHelper.deleteCurrency(cToDel.id) - } - } catch (e: IOException) { - if (e.message == "\"Not Found\"") { - Log.d(TAG, "failed to delete currency on remote project : delete it locally anyway") - dbHelper.deleteCurrency(cToDel.id) - } else { - throw e + if (project.type == ProjectType.COSPEND) { + val currenciesToDelete = dbHelper.getCurrenciesOfProjectWithState(project.id, DBBill.STATE_DELETED) + for (cToDel in currenciesToDelete) { + try { + val deleteRemoteCurrencyResponse = client!!.deleteRemoteCurrency(project, cToDel.remoteId) + if (deleteRemoteCurrencyResponse.stringContent == "OK") { + Log.d(TAG, "successfully deleted currency on remote project : delete it locally") + dbHelper.deleteCurrency(cToDel.id) + } + } catch (e: IOException) { + if (e.message == "\"Not Found\"") { + Log.d(TAG, "failed to delete currency on remote project : delete it locally anyway") + dbHelper.deleteCurrency(cToDel.id) + } else { + throw e + } } } + } else { + val currenciesToDelete = dbHelper.getCurrenciesOfProjectWithState(project.id, DBBill.STATE_DELETED) + for (cToDel in currenciesToDelete) { + dbHelper.deleteCurrency(cToDel.id) + } } - val currenciesToEdit = dbHelper.getCurrenciesOfProjectWithState(project.id, DBBill.STATE_EDITED) - for (cToEdit in currenciesToEdit) { - try { - val editRemoteCurrencyResponse = client!!.editRemoteCurrency(project, cToEdit) - if (editRemoteCurrencyResponse.stringContent == cToEdit.remoteId.toString()) { - dbHelper.setCurrencyState(cToEdit.id, DBBill.STATE_OK) - Log.d(TAG, "SUCCESSFUL remote currency edition (${editRemoteCurrencyResponse.stringContent})") - } else { - Log.d(TAG, "FAILED to edit remote currency (${editRemoteCurrencyResponse.stringContent})") - } - } catch (e: IOException) { - if (e.message == "{\"message\": \"Internal Server Error\"}") { - Log.d(TAG, "FAILED to edit remote currency : it does not exist remotely") - } else { - throw e + if (project.type == ProjectType.COSPEND) { + val currenciesToEdit = dbHelper.getCurrenciesOfProjectWithState(project.id, DBBill.STATE_EDITED) + for (cToEdit in currenciesToEdit) { + try { + val editRemoteCurrencyResponse = client!!.editRemoteCurrency(project, cToEdit) + if (editRemoteCurrencyResponse.stringContent == cToEdit.remoteId.toString()) { + dbHelper.setCurrencyState(cToEdit.id, DBBill.STATE_OK) + Log.d(TAG, "SUCCESSFUL remote currency edition (${editRemoteCurrencyResponse.stringContent})") + } else { + Log.d(TAG, "FAILED to edit remote currency (${editRemoteCurrencyResponse.stringContent})") + } + } catch (e: IOException) { + if (e.message == "{\"message\": \"Internal Server Error\"}") { + Log.d(TAG, "FAILED to edit remote currency : it does not exist remotely") + } else { + throw e + } } } + } else { + val currenciesToEdit = dbHelper.getCurrenciesOfProjectWithState(project.id, DBBill.STATE_EDITED) + for (cToEdit in currenciesToEdit) { + dbHelper.setCurrencyState(cToEdit.id, DBBill.STATE_OK) + } } - val currencyToAdd = dbHelper.getCurrenciesOfProjectWithState(project.id, DBBill.STATE_ADDED) - for (cToAdd in currencyToAdd) { - val createRemoteCurrencyResponse = client!!.createRemoteCurrency(project, cToAdd) - val newRemoteId = createRemoteCurrencyResponse.stringContent.toLong() - if (newRemoteId > 0) { + if (project.type == ProjectType.COSPEND) { + val currencyToAdd = dbHelper.getCurrenciesOfProjectWithState(project.id, DBBill.STATE_ADDED) + for (cToAdd in currencyToAdd) { + val createRemoteCurrencyResponse = client!!.createRemoteCurrency(project, cToAdd) + val newRemoteId = createRemoteCurrencyResponse.stringContent.toLong() + if (newRemoteId > 0) { + dbHelper.setCurrencyState(cToAdd.id, DBBill.STATE_OK) + } + } + } else { + val currencyToAdd = dbHelper.getCurrenciesOfProjectWithState(project.id, DBBill.STATE_ADDED) + for (cToAdd in currencyToAdd) { dbHelper.setCurrencyState(cToAdd.id, DBBill.STATE_OK) } }