Use string identifiers instead of ints
This commit is contained in:
parent
a56406caa9
commit
c0ca8c8b29
34 changed files with 217 additions and 179 deletions
|
@ -57,7 +57,7 @@ dependencies {
|
|||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.core:core:1.3.2'
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.media:media:1.2.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'com.google.android.material:material:1.2.1'
|
||||
|
|
|
@ -4,12 +4,15 @@ import android.content.SharedPreferences
|
|||
import androidx.core.content.edit
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.wbrawner.budget.*
|
||||
import com.wbrawner.budget.AsyncState
|
||||
import com.wbrawner.budget.AsyncViewModel
|
||||
import com.wbrawner.budget.common.budget.BudgetRepository
|
||||
import com.wbrawner.budget.common.user.UserRepository
|
||||
import com.wbrawner.budget.launch
|
||||
import com.wbrawner.budget.lib.network.BaseUrlHelper
|
||||
import com.wbrawner.budget.lib.network.DEFAULT_URL
|
||||
import com.wbrawner.budget.lib.network.PREF_KEY_BASE_URL
|
||||
import com.wbrawner.budget.postValue
|
||||
import javax.inject.Inject
|
||||
|
||||
class SplashViewModel : ViewModel(), AsyncViewModel<AuthenticationState> {
|
||||
|
@ -28,7 +31,7 @@ class SplashViewModel : ViewModel(), AsyncViewModel<AuthenticationState> {
|
|||
|
||||
suspend fun checkForExistingCredentials() {
|
||||
if (baseUrlHelper.url == DEFAULT_URL) {
|
||||
state.postValue(AuthenticationState.Unauthenticated)
|
||||
state.postValue(AuthenticationState.Unauthenticated())
|
||||
return
|
||||
}
|
||||
state.postValue(AsyncState.Success(AuthenticationState.Splash))
|
||||
|
@ -36,7 +39,7 @@ class SplashViewModel : ViewModel(), AsyncViewModel<AuthenticationState> {
|
|||
userRepository.getProfile()
|
||||
AuthenticationState.Authenticated
|
||||
} catch (ignored: Exception) {
|
||||
AuthenticationState.Unauthenticated
|
||||
AuthenticationState.Unauthenticated()
|
||||
}
|
||||
state.postValue(authState)
|
||||
}
|
||||
|
@ -53,9 +56,9 @@ class SplashViewModel : ViewModel(), AsyncViewModel<AuthenticationState> {
|
|||
putString(PREF_KEY_BASE_URL, correctServer)
|
||||
}
|
||||
AuthenticationState.Authenticated
|
||||
} catch (ignored: Exception) {
|
||||
} catch (e: Exception) {
|
||||
// TODO: Return error message here
|
||||
AuthenticationState.Unauthenticated
|
||||
AuthenticationState.Unauthenticated(server, username, password, e.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,6 +69,12 @@ class SplashViewModel : ViewModel(), AsyncViewModel<AuthenticationState> {
|
|||
|
||||
sealed class AuthenticationState {
|
||||
object Splash : AuthenticationState()
|
||||
object Unauthenticated : AuthenticationState()
|
||||
class Unauthenticated(
|
||||
val server: String? = null,
|
||||
val username: String? = null,
|
||||
val password: String? = null,
|
||||
val errorMessage: String? = null
|
||||
) : AuthenticationState()
|
||||
|
||||
object Authenticated : AuthenticationState()
|
||||
}
|
|
@ -1,15 +1,18 @@
|
|||
package com.wbrawner.budget.ui.auth
|
||||
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import com.wbrawner.budget.AsyncState
|
||||
import com.wbrawner.budget.R
|
||||
import com.wbrawner.budget.ui.AuthenticationState
|
||||
import com.wbrawner.budget.ui.SplashViewModel
|
||||
import com.wbrawner.budget.ui.ensureNotEmpty
|
||||
import com.wbrawner.budget.ui.show
|
||||
|
@ -40,6 +43,20 @@ class LoginFragment : Fragment() {
|
|||
password.error = "Invalid username/password"
|
||||
state.exception.printStackTrace()
|
||||
}
|
||||
is AsyncState.Success -> {
|
||||
if (state.data is AuthenticationState.Unauthenticated) {
|
||||
server.setText(state.data.server ?: "")
|
||||
username.setText(state.data.username ?: "")
|
||||
password.setText(state.data.password ?: "")
|
||||
state.data.errorMessage?.let {
|
||||
AlertDialog.Builder(view.context)
|
||||
.setTitle("Login Failed")
|
||||
.setMessage(it)
|
||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> }
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
password.setOnEditorActionListener { _, _, _ ->
|
||||
|
|
|
@ -19,7 +19,7 @@ import kotlinx.android.synthetic.main.fragment_add_edit_budget.*
|
|||
class AddEditBudgetFragment : Fragment() {
|
||||
private val viewModel: BudgetFormViewModel by viewModels()
|
||||
|
||||
var id: Long? = null
|
||||
var id: String? = null
|
||||
var menu: Menu? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -32,7 +32,7 @@ class AddEditBudgetFragment : Fragment() {
|
|||
.appComponent
|
||||
.inject(viewModel)
|
||||
super.onAttach(context)
|
||||
viewModel.getBudget(arguments?.getLong(EXTRA_BUDGET_ID))
|
||||
viewModel.getBudget(arguments?.getString(EXTRA_BUDGET_ID))
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
|
|
|
@ -9,8 +9,8 @@ import com.wbrawner.budget.common.budget.Budget
|
|||
import com.wbrawner.budget.common.budget.BudgetRepository
|
||||
import com.wbrawner.budget.common.user.User
|
||||
import com.wbrawner.budget.common.user.UserRepository
|
||||
import com.wbrawner.budget.common.util.randomId
|
||||
import kotlinx.coroutines.launch
|
||||
import java.lang.Exception
|
||||
import javax.inject.Inject
|
||||
|
||||
class BudgetFormViewModel : ViewModel() {
|
||||
|
@ -25,13 +25,13 @@ class BudgetFormViewModel : ViewModel() {
|
|||
@Inject
|
||||
lateinit var userRepository: UserRepository
|
||||
|
||||
fun getBudget(id: Long? = null) {
|
||||
fun getBudget(id: String? = null) {
|
||||
viewModelScope.launch {
|
||||
state.postValue(BudgetFormState.Loading)
|
||||
try {
|
||||
val budget = id?.let {
|
||||
budgetRepository.findById(it)
|
||||
}?: Budget(name = "")
|
||||
} ?: Budget(name = "")
|
||||
state.postValue(BudgetFormState.Success(budget))
|
||||
} catch (e: Exception) {
|
||||
state.postValue(BudgetFormState.Failed(e))
|
||||
|
@ -46,7 +46,7 @@ class BudgetFormViewModel : ViewModel() {
|
|||
if (budget.id != null) {
|
||||
budgetRepository.update(budget)
|
||||
} else {
|
||||
budgetRepository.create(budget)
|
||||
budgetRepository.create(budget.copy(id = randomId()))
|
||||
}
|
||||
state.postValue(BudgetFormState.Exit)
|
||||
} catch (e: Exception) {
|
||||
|
@ -55,11 +55,11 @@ class BudgetFormViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
fun deleteBudget(accountId: Long) {
|
||||
fun deleteBudget(budgetId: String) {
|
||||
viewModelScope.launch {
|
||||
state.postValue(BudgetFormState.Loading)
|
||||
try {
|
||||
budgetRepository.delete(accountId)
|
||||
budgetRepository.delete(budgetId)
|
||||
state.postValue(BudgetFormState.Exit)
|
||||
} catch (e: Exception) {
|
||||
state.postValue(BudgetFormState.Failed(e))
|
||||
|
|
|
@ -58,7 +58,7 @@ class BudgetViewHolder(itemView: View, val navController: NavController) : Binda
|
|||
}
|
||||
itemView.setOnClickListener {
|
||||
val bundle = Bundle().apply {
|
||||
putLong(EXTRA_BUDGET_ID, budget.id!!)
|
||||
putString(EXTRA_BUDGET_ID, budget.id)
|
||||
}
|
||||
navController.navigate(R.id.categoryListFragment, bundle)
|
||||
}
|
||||
|
|
|
@ -81,8 +81,8 @@ class CategoryDetailsFragment : Fragment() {
|
|||
} ?: run {
|
||||
val transactionsFragment = TransactionListFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putLong(EXTRA_BUDGET_ID, category.budgetId)
|
||||
putLong(EXTRA_CATEGORY_ID, category.id!!)
|
||||
putString(EXTRA_BUDGET_ID, category.budgetId)
|
||||
putString(EXTRA_CATEGORY_ID, category.id)
|
||||
}
|
||||
}
|
||||
childFragmentManager.beginTransaction()
|
||||
|
@ -100,7 +100,7 @@ class CategoryDetailsFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
})
|
||||
viewModel.getCategory(arguments?.getLong(EXTRA_CATEGORY_ID))
|
||||
viewModel.getCategory(arguments?.getString(EXTRA_CATEGORY_ID))
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
|
|
@ -15,7 +15,7 @@ class CategoryDetailsViewModel : ViewModel(), AsyncViewModel<CategoryDetails> {
|
|||
@Inject
|
||||
lateinit var categoryRepo: CategoryRepository
|
||||
|
||||
fun getCategory(id: Long? = null) {
|
||||
fun getCategory(id: String? = null) {
|
||||
if (id == null) {
|
||||
state.postValue(AsyncState.Error("Invalid category ID"))
|
||||
return
|
||||
|
@ -23,7 +23,7 @@ class CategoryDetailsViewModel : ViewModel(), AsyncViewModel<CategoryDetails> {
|
|||
launch {
|
||||
val category = categoryRepo.findById(id)
|
||||
val multiplier = if (category.expense) -1 else 1
|
||||
val balance = categoryRepo.getBalance(category.id!!) * multiplier
|
||||
val balance = categoryRepo.getBalance(id) * multiplier
|
||||
CategoryDetails(
|
||||
category,
|
||||
balance,
|
||||
|
|
|
@ -23,7 +23,7 @@ import kotlinx.android.synthetic.main.activity_add_edit_category.*
|
|||
|
||||
class CategoryFormActivity : AppCompatActivity() {
|
||||
val viewModel: CategoryFormViewModel by viewModels()
|
||||
var id: Long? = null
|
||||
var id: String? = null
|
||||
var menu: Menu? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -65,7 +65,7 @@ class CategoryFormActivity : AppCompatActivity() {
|
|||
is AsyncState.Exit -> finish()
|
||||
}
|
||||
})
|
||||
viewModel.loadCategory(intent?.extras?.getLong(EXTRA_CATEGORY_ID))
|
||||
viewModel.loadCategory(intent?.extras?.getString(EXTRA_CATEGORY_ID))
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
|
|
|
@ -11,24 +11,24 @@ import com.wbrawner.budget.common.budget.Budget
|
|||
import com.wbrawner.budget.common.budget.BudgetRepository
|
||||
import com.wbrawner.budget.common.category.Category
|
||||
import com.wbrawner.budget.common.category.CategoryRepository
|
||||
import com.wbrawner.budget.common.util.randomId
|
||||
import com.wbrawner.budget.launch
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class CategoryFormViewModel : ViewModel(), AsyncViewModel<CategoryFormState> {
|
||||
override val state: MutableLiveData<AsyncState<CategoryFormState>> = MutableLiveData(AsyncState.Loading)
|
||||
|
||||
@Inject
|
||||
lateinit var categoryRepository: CategoryRepository
|
||||
|
||||
@Inject
|
||||
lateinit var budgetRepository: BudgetRepository
|
||||
|
||||
fun loadCategory(categoryId: Long? = null) {
|
||||
fun loadCategory(categoryId: String? = null) {
|
||||
launch {
|
||||
val category = categoryId?.let {
|
||||
categoryRepository.findById(it)
|
||||
} ?: Category(-1, title = "", amount = 0)
|
||||
} ?: Category("", title = "", amount = 0)
|
||||
CategoryFormState(
|
||||
category,
|
||||
budgetRepository.findAll().toList()
|
||||
|
@ -41,7 +41,7 @@ class CategoryFormViewModel : ViewModel(), AsyncViewModel<CategoryFormState> {
|
|||
state.postValue(AsyncState.Loading)
|
||||
try {
|
||||
if (category.id == null)
|
||||
categoryRepository.create(category)
|
||||
categoryRepository.create(category.copy(id = randomId()))
|
||||
else
|
||||
categoryRepository.update(category)
|
||||
state.postValue(AsyncState.Exit)
|
||||
|
@ -51,7 +51,7 @@ class CategoryFormViewModel : ViewModel(), AsyncViewModel<CategoryFormState> {
|
|||
}
|
||||
}
|
||||
|
||||
fun deleteCategoryById(id: Long) {
|
||||
fun deleteCategoryById(id: String) {
|
||||
viewModelScope.launch {
|
||||
state.postValue(AsyncState.Loading)
|
||||
try {
|
||||
|
|
|
@ -82,7 +82,7 @@ class CategoryViewHolder(
|
|||
}
|
||||
itemView.setOnClickListener {
|
||||
val bundle = Bundle().apply {
|
||||
putLong(EXTRA_CATEGORY_ID, category.id ?: -1)
|
||||
putString(EXTRA_CATEGORY_ID, category.id)
|
||||
putString(EXTRA_CATEGORY_NAME, category.title)
|
||||
}
|
||||
navController.navigate(R.id.categoryFragment, bundle)
|
||||
|
|
|
@ -34,7 +34,7 @@ class TransactionFormActivity : AppCompatActivity(), CoroutineScope {
|
|||
override val coroutineContext: CoroutineContext = Dispatchers.Main
|
||||
|
||||
private val viewModel: TransactionFormViewModel by viewModels()
|
||||
var id: Long? = null
|
||||
var id: String? = null
|
||||
var menu: Menu? = null
|
||||
var transaction: Transaction? = null
|
||||
|
||||
|
@ -112,7 +112,7 @@ class TransactionFormActivity : AppCompatActivity(), CoroutineScope {
|
|||
|
||||
private suspend fun loadTransaction() {
|
||||
transaction = try {
|
||||
viewModel.getTransaction(intent!!.extras!!.getLong(EXTRA_TRANSACTION_ID))
|
||||
viewModel.getTransaction(intent!!.extras!!.getString(EXTRA_TRANSACTION_ID)!!)
|
||||
} catch (e: Exception) {
|
||||
menu?.findItem(R.id.action_delete)?.isVisible = false
|
||||
val date = Date()
|
||||
|
@ -148,8 +148,8 @@ class TransactionFormActivity : AppCompatActivity(), CoroutineScope {
|
|||
this@TransactionFormActivity,
|
||||
android.R.layout.simple_list_item_1
|
||||
)
|
||||
adapter.add(Category(id = 0, title = getString(R.string.uncategorized),
|
||||
amount = 0, budgetId = 0))
|
||||
adapter.add(Category(title = getString(R.string.uncategorized),
|
||||
amount = 0, budgetId = ""))
|
||||
adapter.addAll(categories)
|
||||
edit_transaction_category.adapter = adapter
|
||||
transaction?.categoryId?.let {
|
||||
|
@ -190,10 +190,6 @@ class TransactionFormActivity : AppCompatActivity(), CoroutineScope {
|
|||
}
|
||||
}
|
||||
val categoryId = (edit_transaction_category.selectedItem as? Category)?.id
|
||||
?.let {
|
||||
if (it > 0) it
|
||||
else null
|
||||
}
|
||||
launch {
|
||||
viewModel.saveTransaction(Transaction(
|
||||
id = id,
|
||||
|
|
|
@ -21,7 +21,7 @@ class TransactionFormViewModel : ViewModel() {
|
|||
@Inject
|
||||
lateinit var userRepository: UserRepository
|
||||
|
||||
var currentUserId: Long? = null
|
||||
var currentUserId: String? = null
|
||||
private set
|
||||
|
||||
//TODO: Find a better way to handle this
|
||||
|
@ -31,18 +31,18 @@ class TransactionFormViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getCategories(budgetId: Long, expense: Boolean) = categoryRepository.findAll(arrayOf(budgetId)).filter {
|
||||
suspend fun getCategories(budgetId: String, expense: Boolean) = categoryRepository.findAll(arrayOf(budgetId)).filter {
|
||||
it.expense == expense
|
||||
}
|
||||
|
||||
suspend fun getTransaction(id: Long) = transactionRepository.findById(id)
|
||||
suspend fun getTransaction(id: String) = transactionRepository.findById(id)
|
||||
|
||||
suspend fun saveTransaction(transaction: Transaction) = if (transaction.id == null)
|
||||
transactionRepository.create(transaction)
|
||||
else
|
||||
transactionRepository.update(transaction)
|
||||
|
||||
suspend fun deleteTransaction(id: Long) = transactionRepository.delete(id)
|
||||
suspend fun deleteTransaction(id: String) = transactionRepository.delete(id)
|
||||
|
||||
suspend fun getAccounts() = budgetRepository.findAll()
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ class TransactionListFragment : ListWithAddButtonFragment<Transaction, Transacti
|
|||
get() = mapOf(TRANSACTION_VIEW to { v -> TransactionViewHolder(v, findNavController()) })
|
||||
|
||||
override fun reloadItems() {
|
||||
viewModel.getTransactions(categoryId = arguments?.getLong(EXTRA_CATEGORY_ID))
|
||||
viewModel.getTransactions(categoryId = arguments?.getString(EXTRA_CATEGORY_ID))
|
||||
}
|
||||
|
||||
override fun bindData(data: Transaction): BindableData<Transaction> = BindableData(data, TRANSACTION_VIEW)
|
||||
|
@ -96,8 +96,8 @@ class TransactionViewHolder(itemView: View, val navController: NavController) :
|
|||
amount.setTextColor(ContextCompat.getColor(context, color))
|
||||
itemView.setOnClickListener {
|
||||
val bundle = Bundle().apply {
|
||||
putLong(EXTRA_BUDGET_ID, transaction.budgetId)
|
||||
putLong(EXTRA_TRANSACTION_ID, transaction.id ?: -1)
|
||||
putString(EXTRA_BUDGET_ID, transaction.budgetId)
|
||||
putString(EXTRA_TRANSACTION_ID, transaction.id)
|
||||
}
|
||||
navController.navigate(R.id.addEditTransactionActivity, bundle)
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ class TransactionListViewModel : ViewModel(), AsyncViewModel<List<Transaction>>
|
|||
override val state: MutableLiveData<AsyncState<List<Transaction>>> = MutableLiveData(AsyncState.Loading)
|
||||
|
||||
fun getTransactions(
|
||||
categoryId: Long? = null,
|
||||
categoryId: String? = null,
|
||||
start: Calendar? = null,
|
||||
end: Calendar? = null
|
||||
) {
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
package com.wbrawner.budget.lib.network
|
||||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
|
||||
const val DEFAULT_URL = "http://localhost"
|
||||
|
||||
class BaseUrlHelper(var url: String = DEFAULT_URL) {
|
||||
val interceptor = object: Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
val newRequest = request.newBuilder()
|
||||
val url = request.url.toString().replace(DEFAULT_URL, url)
|
||||
return chain.proceed(newRequest.url(url).build())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.wbrawner.budget.lib.network
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import com.wbrawner.budget.common.PREF_KEY_TOKEN
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import javax.inject.Inject
|
||||
|
||||
const val DEFAULT_URL = "http://localhost"
|
||||
|
||||
class BaseUrlHelper(var url: String = DEFAULT_URL) {
|
||||
val interceptor = object : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
val newRequest = request.newBuilder()
|
||||
val url = request.url.toString().replace(DEFAULT_URL, url)
|
||||
return chain.proceed(newRequest.url(url).build())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AuthHelper @Inject constructor(val sharedPreferences: SharedPreferences) {
|
||||
val interceptor = object : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val token = sharedPreferences.getString(PREF_KEY_TOKEN, null)
|
||||
?: return chain.proceed(chain.request())
|
||||
val newRequest = chain.request()
|
||||
.newBuilder()
|
||||
.addHeader("Authorization", "Bearer $token")
|
||||
.build()
|
||||
return chain.proceed(newRequest)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package com.wbrawner.budget.lib.network
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Base64
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
|
||||
import com.wbrawner.budget.common.budget.BudgetRepository
|
||||
|
@ -15,16 +14,11 @@ import com.wbrawner.budget.lib.repository.NetworkUserRepository
|
|||
import com.wbrawner.budgetlib.BuildConfig
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import java.nio.charset.Charset
|
||||
import java.util.*
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
|
||||
const val PREF_KEY_BASE_URL = "baseUrl"
|
||||
|
@ -36,36 +30,6 @@ class NetworkModule {
|
|||
.add(Date::class.java, Rfc3339DateJsonAdapter())
|
||||
.build()
|
||||
|
||||
@Provides
|
||||
fun provideCookieJar(sharedPreferences: SharedPreferences): CookieJar {
|
||||
return object : CookieJar {
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||
sharedPreferences.edit()
|
||||
.putString(
|
||||
url.host,
|
||||
cookies.joinToString(separator = ",") {
|
||||
Base64.encode(it.toString().toByteArray(), 0)
|
||||
.toString(charset = Charset.forName("UTF-8"))
|
||||
}
|
||||
)
|
||||
.apply()
|
||||
}
|
||||
|
||||
override fun loadForRequest(url: HttpUrl): MutableList<Cookie> {
|
||||
return sharedPreferences.getString(url.host, "")
|
||||
?.split(",")
|
||||
?.mapNotNull {
|
||||
Cookie.parse(
|
||||
url,
|
||||
Base64.decode(it, 0).toString(Charset.forName("UTF-8"))
|
||||
)
|
||||
}
|
||||
?.toMutableList()
|
||||
?: mutableListOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideBaseUrlHelper(sharedPreferences: SharedPreferences) = BaseUrlHelper().apply {
|
||||
|
@ -76,9 +40,9 @@ class NetworkModule {
|
|||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideOkHttpClient(baseUrlHelper: BaseUrlHelper, cookieJar: CookieJar): OkHttpClient = OkHttpClient.Builder()
|
||||
.cookieJar(cookieJar)
|
||||
fun provideOkHttpClient(baseUrlHelper: BaseUrlHelper, authHelper: AuthHelper): OkHttpClient = OkHttpClient.Builder()
|
||||
.addInterceptor(baseUrlHelper.interceptor)
|
||||
.addInterceptor(authHelper.interceptor)
|
||||
.apply {
|
||||
if (BuildConfig.DEBUG)
|
||||
this.addInterceptor(
|
||||
|
@ -100,26 +64,26 @@ class NetworkModule {
|
|||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideApiService(retrofit: Retrofit): BudgetApiService =
|
||||
retrofit.create(BudgetApiService::class.java)
|
||||
fun provideApiService(retrofit: Retrofit): TwigsApiService =
|
||||
retrofit.create(TwigsApiService::class.java)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideBudgetRepository(apiService: BudgetApiService, sharedPreferences: SharedPreferences): BudgetRepository =
|
||||
fun provideBudgetRepository(apiService: TwigsApiService, sharedPreferences: SharedPreferences): BudgetRepository =
|
||||
NetworkBudgetRepository(apiService, sharedPreferences)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideCategoryRepository(apiService: BudgetApiService): CategoryRepository =
|
||||
fun provideCategoryRepository(apiService: TwigsApiService): CategoryRepository =
|
||||
NetworkCategoryRepository(apiService)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideTransactionRepository(apiService: BudgetApiService): TransactionRepository =
|
||||
fun provideTransactionRepository(apiService: TwigsApiService): TransactionRepository =
|
||||
NetworkTransactionRepository(apiService)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideUserRepository(apiService: BudgetApiService): UserRepository =
|
||||
NetworkUserRepository(apiService)
|
||||
fun provideUserRepository(apiService: TwigsApiService, sharedPreferences: SharedPreferences): UserRepository =
|
||||
NetworkUserRepository(apiService, sharedPreferences)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.wbrawner.budget.lib.network
|
||||
|
||||
import com.wbrawner.budget.common.Session
|
||||
import com.wbrawner.budget.common.budget.Budget
|
||||
import com.wbrawner.budget.common.category.Category
|
||||
import com.wbrawner.budget.common.transaction.Transaction
|
||||
|
@ -8,7 +9,7 @@ import com.wbrawner.budget.common.user.User
|
|||
import com.wbrawner.budget.lib.repository.NewBudgetRequest
|
||||
import retrofit2.http.*
|
||||
|
||||
interface BudgetApiService {
|
||||
interface TwigsApiService {
|
||||
// Budgets
|
||||
@GET("budgets")
|
||||
suspend fun getBudgets(
|
||||
|
@ -17,54 +18,54 @@ interface BudgetApiService {
|
|||
): List<Budget>
|
||||
|
||||
@GET("budgets/{id}")
|
||||
suspend fun getBudget(@Path("id") id: Long): Budget
|
||||
suspend fun getBudget(@Path("id") id: String): Budget
|
||||
|
||||
@GET("budgets/{id}/balance")
|
||||
suspend fun getBudgetBalance(@Path("id") id: Long): BudgetBalanceResponse
|
||||
suspend fun getBudgetBalance(@Path("id") id: String): BudgetBalanceResponse
|
||||
|
||||
@POST("budgets")
|
||||
suspend fun newBudget(@Body budget: NewBudgetRequest): Budget
|
||||
|
||||
@PUT("budgets/{id}")
|
||||
suspend fun updateBudget(
|
||||
@Path("id") id: Long,
|
||||
@Path("id") id: String,
|
||||
@Body budget: Budget
|
||||
): Budget
|
||||
|
||||
@DELETE("budgets/{id}")
|
||||
suspend fun deleteBudget(@Path("id") id: Long)
|
||||
suspend fun deleteBudget(@Path("id") id: String)
|
||||
|
||||
// Categories
|
||||
@GET("categories")
|
||||
suspend fun getCategories(
|
||||
@Query("budgetIds") budgetIds: Array<Long>? = null,
|
||||
@Query("budgetIds") budgetIds: Array<String>? = null,
|
||||
@Query("count") count: Int? = null,
|
||||
@Query("page") page: Int? = null
|
||||
): List<Category>
|
||||
|
||||
@GET("categories/{id}")
|
||||
suspend fun getCategory(@Path("id") id: Long): Category
|
||||
suspend fun getCategory(@Path("id") id: String): Category
|
||||
|
||||
@GET("categories/{id}/balance")
|
||||
suspend fun getCategoryBalance(@Path("id") id: Long): CategoryBalanceResponse
|
||||
suspend fun getCategoryBalance(@Path("id") id: String): CategoryBalanceResponse
|
||||
|
||||
@POST("categories")
|
||||
suspend fun newCategory(@Body category: Category): Category
|
||||
|
||||
@PUT("categories/{id}")
|
||||
suspend fun updateCategory(
|
||||
@Path("id") id: Long,
|
||||
@Path("id") id: String,
|
||||
@Body category: Category
|
||||
): Category
|
||||
|
||||
@DELETE("categories/{id}")
|
||||
suspend fun deleteCategory(@Path("id") id: Long)
|
||||
suspend fun deleteCategory(@Path("id") id: String)
|
||||
|
||||
// Transactions
|
||||
@GET("transactions")
|
||||
suspend fun getTransactions(
|
||||
@Query("budgetIds") budgetIds: List<Long>? = null,
|
||||
@Query("categoryIds") categoryIds: List<Long>? = null,
|
||||
@Query("budgetIds") budgetIds: List<String>? = null,
|
||||
@Query("categoryIds") categoryIds: List<String>? = null,
|
||||
@Query("from") from: String? = null,
|
||||
@Query("to") to: String? = null,
|
||||
@Query("count") count: Int? = null,
|
||||
|
@ -72,30 +73,30 @@ interface BudgetApiService {
|
|||
): List<Transaction>
|
||||
|
||||
@GET("transactions/{id}")
|
||||
suspend fun getTransaction(@Path("id") id: Long): Transaction
|
||||
suspend fun getTransaction(@Path("id") id: String): Transaction
|
||||
|
||||
@POST("transactions")
|
||||
suspend fun newTransaction(@Body transaction: Transaction): Transaction
|
||||
|
||||
@PUT("transactions/{id}")
|
||||
suspend fun updateTransaction(
|
||||
@Path("id") id: Long,
|
||||
@Path("id") id: String,
|
||||
@Body transaction: Transaction
|
||||
): Transaction
|
||||
|
||||
@DELETE("transactions/{id}")
|
||||
suspend fun deleteTransaction(@Path("id") id: Long)
|
||||
suspend fun deleteTransaction(@Path("id") id: String)
|
||||
|
||||
// Users
|
||||
@GET("users")
|
||||
suspend fun getUsers(
|
||||
@Query("budgetId") budgetId: Long? = null,
|
||||
@Query("budgetId") budgetid: String? = null,
|
||||
@Query("count") count: Int? = null,
|
||||
@Query("page") page: Int? = null
|
||||
): List<User>
|
||||
|
||||
@POST("users/login")
|
||||
suspend fun login(@Body request: LoginRequest): User
|
||||
suspend fun login(@Body request: LoginRequest): Session
|
||||
|
||||
@GET("users/me")
|
||||
suspend fun getProfile(): User
|
||||
|
@ -104,17 +105,17 @@ interface BudgetApiService {
|
|||
suspend fun searchUsers(@Query("query") query: String): List<User>
|
||||
|
||||
@GET("users/{id}")
|
||||
suspend fun getUser(@Path("id") id: Long): User
|
||||
suspend fun getUser(@Path("id") id: String): User
|
||||
|
||||
@POST("users")
|
||||
suspend fun newUser(@Body user: User): User
|
||||
|
||||
@PUT("users/{id}")
|
||||
suspend fun updateUser(
|
||||
@Path("id") id: Long,
|
||||
@Path("id") id: String,
|
||||
@Body user: User
|
||||
): User
|
||||
|
||||
@DELETE("users/{id}")
|
||||
suspend fun deleteUser(@Path("id") id: Long)
|
||||
suspend fun deleteUser(@Path("id") id: String)
|
||||
}
|
|
@ -5,7 +5,7 @@ import androidx.lifecycle.LiveData
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import com.wbrawner.budget.common.budget.Budget
|
||||
import com.wbrawner.budget.common.budget.BudgetRepository
|
||||
import com.wbrawner.budget.lib.network.BudgetApiService
|
||||
import com.wbrawner.budget.lib.network.TwigsApiService
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
|
@ -14,7 +14,7 @@ import kotlinx.coroutines.sync.withLock
|
|||
const val KEY_DEFAULT_BUDGET = "defaultBudget"
|
||||
|
||||
class NetworkBudgetRepository(
|
||||
private val apiService: BudgetApiService,
|
||||
private val apiService: TwigsApiService,
|
||||
private val sharedPreferences: SharedPreferences
|
||||
) : BudgetRepository {
|
||||
private val mutex = Mutex()
|
||||
|
@ -25,7 +25,7 @@ class NetworkBudgetRepository(
|
|||
currentBudget.observeForever { budget ->
|
||||
sharedPreferences.edit().apply {
|
||||
budget?.id?.let {
|
||||
putLong(KEY_DEFAULT_BUDGET, it)
|
||||
putString(KEY_DEFAULT_BUDGET, it)
|
||||
} ?: remove(KEY_DEFAULT_BUDGET)
|
||||
apply()
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class NetworkBudgetRepository(
|
|||
emptyList<Budget>()
|
||||
}
|
||||
if (budgets.isEmpty()) return
|
||||
val budgetId = sharedPreferences.getLong(KEY_DEFAULT_BUDGET, budgets.first().id!!)
|
||||
val budgetId = sharedPreferences.getString(KEY_DEFAULT_BUDGET, budgets.first().id)!!
|
||||
val budget = try {
|
||||
findById(budgetId)
|
||||
} catch (e: Exception) {
|
||||
|
@ -67,7 +67,7 @@ class NetworkBudgetRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun findById(id: Long, setCurrent: Boolean): Budget = (mutex.withLock {
|
||||
override suspend fun findById(id: String, setCurrent: Boolean): Budget = (mutex.withLock {
|
||||
budgets.firstOrNull { it.id == id }
|
||||
} ?: apiService.getBudget(id).apply {
|
||||
mutex.withLock {
|
||||
|
@ -87,19 +87,19 @@ class NetworkBudgetRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Long) = apiService.deleteBudget(id).apply {
|
||||
override suspend fun delete(id: String) = apiService.deleteBudget(id).apply {
|
||||
mutex.withLock {
|
||||
budgets.removeAll { it.id == id }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getBalance(id: Long): Long = apiService.getBudgetBalance(id).balance
|
||||
override suspend fun getBalance(id: String): Long = apiService.getBudgetBalance(id).balance
|
||||
}
|
||||
|
||||
data class NewBudgetRequest(
|
||||
val name: String,
|
||||
val description: String? = null,
|
||||
val userIds: List<Long>
|
||||
val userIds: List<String>
|
||||
) {
|
||||
constructor(budget: Budget) : this(budget.name, budget.description, budget.users.map { it.id!! })
|
||||
}
|
|
@ -2,23 +2,24 @@ package com.wbrawner.budget.lib.repository
|
|||
|
||||
import com.wbrawner.budget.common.category.Category
|
||||
import com.wbrawner.budget.common.category.CategoryRepository
|
||||
import com.wbrawner.budget.lib.network.BudgetApiService
|
||||
import com.wbrawner.budget.lib.network.TwigsApiService
|
||||
import javax.inject.Inject
|
||||
class NetworkCategoryRepository @Inject constructor(private val apiService: BudgetApiService) : CategoryRepository {
|
||||
|
||||
class NetworkCategoryRepository @Inject constructor(private val apiService: TwigsApiService) : CategoryRepository {
|
||||
override suspend fun create(newItem: Category): Category = apiService.newCategory(newItem)
|
||||
|
||||
override suspend fun findAll(budgetIds: Array<Long>?): List<Category> = apiService.getCategories(budgetIds).sortedBy { it.title }
|
||||
override suspend fun findAll(budgetIds: Array<String>?): List<Category> = apiService.getCategories(budgetIds).sortedBy { it.title }
|
||||
|
||||
override suspend fun findAll(): List<Category> = findAll(null)
|
||||
|
||||
override suspend fun findById(id: Long): Category = apiService.getCategory(id)
|
||||
override suspend fun findById(id: String): Category = apiService.getCategory(id)
|
||||
|
||||
override suspend fun update(updatedItem: Category): Category =
|
||||
apiService.updateCategory(updatedItem.id!!, updatedItem)
|
||||
|
||||
override suspend fun delete(id: Long) = apiService.deleteCategory(id)
|
||||
override suspend fun delete(id: String) = apiService.deleteCategory(id)
|
||||
|
||||
// TODO: Implement this method server-side and then here
|
||||
override suspend fun getBalance(id: Long): Long = apiService.getCategoryBalance(id).balance
|
||||
override suspend fun getBalance(id: String): Long = apiService.getCategoryBalance(id).balance
|
||||
}
|
||||
|
||||
|
|
|
@ -2,19 +2,19 @@ package com.wbrawner.budget.lib.repository
|
|||
|
||||
import com.wbrawner.budget.common.transaction.Transaction
|
||||
import com.wbrawner.budget.common.transaction.TransactionRepository
|
||||
import com.wbrawner.budget.lib.network.BudgetApiService
|
||||
import com.wbrawner.budget.lib.network.TwigsApiService
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class NetworkTransactionRepository @Inject constructor(private val apiService: BudgetApiService) : TransactionRepository {
|
||||
class NetworkTransactionRepository @Inject constructor(private val apiService: TwigsApiService) : TransactionRepository {
|
||||
private val dateFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH)
|
||||
|
||||
override suspend fun create(newItem: Transaction): Transaction = apiService.newTransaction(newItem)
|
||||
|
||||
override suspend fun findAll(
|
||||
budgetIds: List<Long>?,
|
||||
categoryIds: List<Long>?,
|
||||
budgetIds: List<String>?,
|
||||
categoryIds: List<String>?,
|
||||
start: Calendar?,
|
||||
end: Calendar?
|
||||
): List<Transaction> = apiService.getTransactions(
|
||||
|
@ -30,10 +30,10 @@ class NetworkTransactionRepository @Inject constructor(private val apiService: B
|
|||
|
||||
override suspend fun findAll(): List<Transaction> = findAll(null)
|
||||
|
||||
override suspend fun findById(id: Long): Transaction = apiService.getTransaction(id)
|
||||
override suspend fun findById(id: String): Transaction = apiService.getTransaction(id)
|
||||
|
||||
override suspend fun update(updatedItem: Transaction): Transaction =
|
||||
apiService.updateTransaction(updatedItem.id!!, updatedItem)
|
||||
|
||||
override suspend fun delete(id: Long) = apiService.deleteTransaction(id)
|
||||
override suspend fun delete(id: String) = apiService.deleteTransaction(id)
|
||||
}
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
package com.wbrawner.budget.lib.repository
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.wbrawner.budget.common.PREF_KEY_TOKEN
|
||||
import com.wbrawner.budget.common.user.LoginRequest
|
||||
import com.wbrawner.budget.common.user.User
|
||||
import com.wbrawner.budget.common.user.UserRepository
|
||||
import com.wbrawner.budget.lib.network.BudgetApiService
|
||||
import com.wbrawner.budget.lib.network.TwigsApiService
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class NetworkUserRepository @Inject constructor(private val apiService: BudgetApiService) : UserRepository {
|
||||
class NetworkUserRepository @Inject constructor(
|
||||
private val apiService: TwigsApiService,
|
||||
private val sharedPreferences: SharedPreferences
|
||||
) : UserRepository {
|
||||
|
||||
override val currentUser: LiveData<User?> = MutableLiveData()
|
||||
|
||||
|
@ -23,10 +28,14 @@ class NetworkUserRepository @Inject constructor(private val apiService: BudgetAp
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun login(username: String, password: String): User =
|
||||
apiService.login(LoginRequest(username, password)).also {
|
||||
(currentUser as MutableLiveData).postValue(it)
|
||||
}
|
||||
override suspend fun login(username: String, password: String): User {
|
||||
apiService.login(LoginRequest(username, password)).also {
|
||||
sharedPreferences.edit()
|
||||
.putString(PREF_KEY_TOKEN, it.token)
|
||||
.apply()
|
||||
}
|
||||
return getProfile()
|
||||
}
|
||||
|
||||
override suspend fun getProfile(): User = apiService.getProfile().also {
|
||||
(currentUser as MutableLiveData).postValue(it)
|
||||
|
@ -34,11 +43,11 @@ class NetworkUserRepository @Inject constructor(private val apiService: BudgetAp
|
|||
|
||||
override suspend fun create(newItem: User): User = apiService.newUser(newItem)
|
||||
|
||||
override suspend fun findAll(accountId: Long?): List<User> = apiService.getUsers(accountId)
|
||||
override suspend fun findAll(budgetId: String?): List<User> = apiService.getUsers(budgetId)
|
||||
|
||||
override suspend fun findAll(): List<User> = findAll(null)
|
||||
|
||||
override suspend fun findById(id: Long): User = apiService.getUser(id)
|
||||
override suspend fun findById(id: String): User = apiService.getUser(id)
|
||||
|
||||
override suspend fun findAllByNameLike(query: String): List<User> =
|
||||
apiService.searchUsers(query)
|
||||
|
@ -46,5 +55,5 @@ class NetworkUserRepository @Inject constructor(private val apiService: BudgetAp
|
|||
override suspend fun update(updatedItem: User): User =
|
||||
apiService.updateUser(updatedItem.id!!, updatedItem)
|
||||
|
||||
override suspend fun delete(id: Long) = apiService.deleteUser(id)
|
||||
}
|
||||
override suspend fun delete(id: String) = apiService.deleteUser(id)
|
||||
}
|
||||
|
|
|
@ -15,5 +15,5 @@ interface Repository<T, K> {
|
|||
}
|
||||
|
||||
interface Identifiable {
|
||||
val id: Long?
|
||||
val id: String?
|
||||
}
|
||||
|
|
11
common/src/main/java/com/wbrawner/budget/common/Session.kt
Normal file
11
common/src/main/java/com/wbrawner/budget/common/Session.kt
Normal file
|
@ -0,0 +1,11 @@
|
|||
package com.wbrawner.budget.common
|
||||
|
||||
import java.util.*
|
||||
|
||||
const val PREF_KEY_TOKEN = "sessionToken"
|
||||
|
||||
data class Session(
|
||||
val id: String,
|
||||
val token: String,
|
||||
val expiration: Date
|
||||
)
|
|
@ -4,7 +4,7 @@ import com.wbrawner.budget.common.Identifiable
|
|||
import com.wbrawner.budget.common.user.User
|
||||
|
||||
data class Budget(
|
||||
override val id: Long? = null,
|
||||
override val id: String? = null,
|
||||
val name: String,
|
||||
val description: String? = null,
|
||||
val users: List<User> = emptyList()
|
||||
|
|
|
@ -3,10 +3,10 @@ package com.wbrawner.budget.common.budget
|
|||
import androidx.lifecycle.LiveData
|
||||
import com.wbrawner.budget.common.Repository
|
||||
|
||||
interface BudgetRepository : Repository<Budget, Long> {
|
||||
interface BudgetRepository : Repository<Budget, String> {
|
||||
val currentBudget: LiveData<Budget?>
|
||||
override suspend fun findById(id: Long): Budget = findById(id, false)
|
||||
suspend fun findById(id: Long, setCurrent: Boolean = false): Budget
|
||||
override suspend fun findById(id: String): Budget = findById(id, false)
|
||||
suspend fun findById(id: String, setCurrent: Boolean = false): Budget
|
||||
suspend fun prefetchData()
|
||||
suspend fun getBalance(id: Long): Long
|
||||
suspend fun getBalance(id: String): Long
|
||||
}
|
|
@ -3,8 +3,8 @@ package com.wbrawner.budget.common.category
|
|||
import com.wbrawner.budget.common.Identifiable
|
||||
|
||||
data class Category(
|
||||
val budgetId: Long,
|
||||
override val id: Long? = null,
|
||||
val budgetId: String,
|
||||
override val id: String? = null,
|
||||
val title: String,
|
||||
val description: String? = null,
|
||||
val amount: Long,
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.wbrawner.budget.common.category
|
|||
|
||||
import com.wbrawner.budget.common.Repository
|
||||
|
||||
interface CategoryRepository : Repository<Category, Long> {
|
||||
suspend fun findAll(budgetIds: Array<Long>? = null): List<Category>
|
||||
suspend fun getBalance(id: Long): Long
|
||||
interface CategoryRepository : Repository<Category, String> {
|
||||
suspend fun findAll(budgetIds: Array<String>? = null): List<Category>
|
||||
suspend fun getBalance(id: String): Long
|
||||
}
|
|
@ -4,13 +4,13 @@ import com.wbrawner.budget.common.Identifiable
|
|||
import java.util.*
|
||||
|
||||
data class Transaction(
|
||||
override val id: Long? = null,
|
||||
override val id: String? = null,
|
||||
val title: String,
|
||||
val date: Date,
|
||||
val description: String,
|
||||
val amount: Long,
|
||||
val categoryId: Long? = null,
|
||||
val budgetId: Long,
|
||||
val categoryId: String? = null,
|
||||
val budgetId: String,
|
||||
val expense: Boolean,
|
||||
val createdBy: Long
|
||||
val createdBy: String
|
||||
): Identifiable
|
|
@ -3,10 +3,10 @@ package com.wbrawner.budget.common.transaction
|
|||
import com.wbrawner.budget.common.Repository
|
||||
import java.util.*
|
||||
|
||||
interface TransactionRepository : Repository<Transaction, Long> {
|
||||
interface TransactionRepository : Repository<Transaction, String> {
|
||||
suspend fun findAll(
|
||||
budgetIds: List<Long>? = null,
|
||||
categoryIds: List<Long>? = null,
|
||||
budgetIds: List<String>? = null,
|
||||
categoryIds: List<String>? = null,
|
||||
start: Calendar? = GregorianCalendar.getInstance().apply {
|
||||
set(Calendar.DAY_OF_MONTH, 1)
|
||||
set(Calendar.HOUR_OF_DAY, 0)
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package com.wbrawner.budget.common.user
|
||||
|
||||
import com.wbrawner.budget.common.Identifiable
|
||||
|
||||
data class User(
|
||||
val id: Long? = null,
|
||||
override val id: String? = null,
|
||||
val username: String,
|
||||
val email: String? = null,
|
||||
val avatar: String? = null
|
||||
) {
|
||||
) : Identifiable {
|
||||
override fun toString(): String = username
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@ package com.wbrawner.budget.common.user
|
|||
import androidx.lifecycle.LiveData
|
||||
import com.wbrawner.budget.common.Repository
|
||||
|
||||
interface UserRepository : Repository<User, Long> {
|
||||
interface UserRepository : Repository<User, String> {
|
||||
val currentUser: LiveData<User?>
|
||||
suspend fun login(username: String, password: String): User
|
||||
suspend fun getProfile(): User
|
||||
suspend fun findAll(accountId: Long? = null): List<User>
|
||||
suspend fun findAll(budgetId: String? = null): List<User>
|
||||
suspend fun findAllByNameLike(query: String): List<User>
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.wbrawner.budget.common.util
|
||||
|
||||
private const val CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
|
||||
fun randomId(): String {
|
||||
val id = StringBuilder()
|
||||
repeat(32) {
|
||||
id.append(CHARACTERS.random())
|
||||
}
|
||||
return id.toString()
|
||||
}
|
Loading…
Reference in a new issue