Skip to content

Commit

Permalink
contacts
Browse files Browse the repository at this point in the history
  • Loading branch information
polstianka committed Oct 17, 2024
1 parent 8bd3552 commit 07861d6
Show file tree
Hide file tree
Showing 59 changed files with 1,250 additions and 217 deletions.
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/wallet/data/contacts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
14 changes: 14 additions & 0 deletions apps/wallet/data/contacts/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
plugins {
id("com.tonapps.wallet.data")
id("kotlin-parcelize")
}

android {
namespace = Build.namespacePrefix("wallet.data.contacts")
}

dependencies {
implementation(project(Dependence.Wallet.Data.core))
implementation(project(Dependence.Lib.extensions))
implementation(project(Dependence.Lib.sqlite))
}
1 change: 1 addition & 0 deletions apps/wallet/data/contacts/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest />
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.tonapps.wallet.data.contacts

import android.content.Context
import com.tonapps.wallet.data.contacts.entities.ContactEntity
import com.tonapps.wallet.data.contacts.source.DatabaseSource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class ContactsRepository(context: Context, scope: CoroutineScope) {

private val database = DatabaseSource(context)

private val _contactsFlow = MutableStateFlow<List<ContactEntity>?>(null)
val contactsFlow = _contactsFlow.stateIn(scope, SharingStarted.Lazily, null).filterNotNull()

init {
scope.launch {
_contactsFlow.value = database.getContacts()
}
}

fun isHidden(accountId: String, testnet: Boolean) = database.isHidden(accountId, testnet)

fun hide(accountId: String, testnet: Boolean) = database.setHidden(accountId, testnet, true)

suspend fun add(name: String, address: String): ContactEntity = withContext(Dispatchers.IO) {
val contact = database.addContact(name, address)
_contactsFlow.value = _contactsFlow.value.orEmpty().toMutableList().apply {
add(contact)
}
contact
}

suspend fun deleteContact(id: Long) = withContext(Dispatchers.IO) {
database.deleteContact(id)
_contactsFlow.value = _contactsFlow.value.orEmpty().toMutableList().apply {
removeAll { it.id == id }
}
}

suspend fun editContact(id: Long, name: String) = withContext(Dispatchers.IO) {
database.editContact(id, name)
val oldContacts = _contactsFlow.value.orEmpty().toMutableList()
val index = oldContacts.indexOfFirst { it.id == id }
if (index == -1) return@withContext
oldContacts[index] = oldContacts[index].copy(name = name)
_contactsFlow.value = oldContacts
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.tonapps.wallet.data.contacts

import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module

val contactsModule = module {
singleOf(::ContactsRepository)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.tonapps.wallet.data.contacts.entities

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
data class ContactEntity(
val id: Long,
val name: String,
val address: String,
val date: Long
): Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.tonapps.wallet.data.contacts.source

import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import com.tonapps.extensions.currentTimeSeconds
import com.tonapps.sqlite.SQLiteHelper
import com.tonapps.wallet.data.contacts.entities.ContactEntity

internal class DatabaseSource(
context: Context
): SQLiteHelper(context, DATABASE_NAME, DATABASE_VERSION) {

private companion object {
private const val DATABASE_NAME = "contacts"
private const val DATABASE_VERSION = 1

private const val CONTACTS_TABLE = "contacts"
private const val CONTACTS_ID = "_id"
private const val CONTACTS_NAME = "name"
private const val CONTACTS_ADDRESS = "address"
private const val CONTACTS_DATE = "date"

private val contactsField = arrayOf(
CONTACTS_ID,
CONTACTS_NAME,
CONTACTS_ADDRESS,
CONTACTS_DATE
).joinToString(",")

private const val KEY_HIDDEN = "hidden"
}

private val prefs = context.getSharedPreferences("contacts", Context.MODE_PRIVATE)

override fun create(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE $CONTACTS_TABLE (" +
"$CONTACTS_ID INTEGER PRIMARY KEY AUTOINCREMENT," +
"$CONTACTS_NAME TEXT NOT NULL," +
"$CONTACTS_ADDRESS TEXT NOT NULL," +
"$CONTACTS_DATE INTEGER NOT NULL" +
")")
}

fun getContacts(): List<ContactEntity> {
val contacts = mutableListOf<ContactEntity>()
val query = "SELECT $contactsField FROM $CONTACTS_TABLE LIMIT 100"
val cursor = readableDatabase.rawQuery(query, null)
val idIndex = cursor.getColumnIndex(CONTACTS_ID)
val nameIndex = cursor.getColumnIndex(CONTACTS_NAME)
val addressIndex = cursor.getColumnIndex(CONTACTS_ADDRESS)
val dateIndex = cursor.getColumnIndex(CONTACTS_DATE)
while (cursor.moveToNext()) {
contacts.add(ContactEntity(
id = cursor.getLong(idIndex),
name = cursor.getString(nameIndex),
address = cursor.getString(addressIndex),
date = cursor.getLong(dateIndex)
))
}
cursor.close()
return contacts
}

fun addContact(name: String, address: String): ContactEntity {
val date = currentTimeSeconds()
val values = ContentValues().apply {
put(CONTACTS_NAME, name)
put(CONTACTS_ADDRESS, address)
put(CONTACTS_DATE, date)
}
val id = writableDatabase.insert(CONTACTS_TABLE, null, values)
return ContactEntity(id, name, address, date)
}

fun editContact(id: Long, name: String) {
val values = ContentValues().apply {
put(CONTACTS_NAME, name)
}
writableDatabase.update(CONTACTS_TABLE, values, "$CONTACTS_ID = ?", arrayOf(id.toString()))
}

fun deleteContact(id: Long) {
writableDatabase.delete(CONTACTS_TABLE, "$CONTACTS_ID = ?", arrayOf(id.toString()))
}

private fun prefixHidden(accountId: String, testnet: Boolean): String {
return "$KEY_HIDDEN:$accountId:${if (testnet) "testnet" else "mainnet"}"
}

fun isHidden(accountId: String, testnet: Boolean): Boolean {
return prefs.getBoolean(prefixHidden(accountId, testnet), false)
}

fun setHidden(accountId: String, testnet: Boolean, hidden: Boolean) {
prefs.edit().putBoolean(prefixHidden(accountId, testnet), hidden).apply()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class SettingsRepository(
private const val ENCRYPTED_COMMENT_MODAL_KEY = "encrypted_comment_modal"
private const val BATTERY_VIEWED_KEY = "battery_viewed"
private const val CHART_PERIOD_KEY = "chart_period"
private const val ONLY_VERIFY_NFTS_KEY = "only_verify_nfts"
private const val ONLY_VERIFY_TOKENS_KEY = "only_verify_tokens"
}

private val _currencyFlow = MutableEffectFlow<WalletCurrency>()
Expand All @@ -76,6 +78,10 @@ class SettingsRepository(
private val _walletPush = MutableEffectFlow<Unit>()
val walletPush = _walletPush.shareIn(scope, SharingStarted.Eagerly)

fun notifyWalletPush() {
_walletPush.tryEmit(Unit)
}

private val prefs = context.getSharedPreferences(NAME, Context.MODE_PRIVATE)
private val tokenPrefsFolder = TokenPrefsFolder(context, scope)
private val walletPrefsFolder = WalletPrefsFolder(context, scope)
Expand Down Expand Up @@ -215,6 +221,25 @@ class SettingsRepository(
}
}


var onlyVerifyNFTs: Boolean = prefs.getBoolean(ONLY_VERIFY_NFTS_KEY, false)
set(value) {
if (value != field) {
prefs.edit().putBoolean(ONLY_VERIFY_NFTS_KEY, value).apply()
field = value
walletPrefsFolder.notifyChanged()
}
}

var onlyVerifyTokens: Boolean = prefs.getBoolean(ONLY_VERIFY_TOKENS_KEY, false)
set(value) {
if (value != field) {
prefs.edit().putBoolean(ONLY_VERIFY_TOKENS_KEY, value).apply()
field = value
walletPrefsFolder.notifyChanged()
}
}

fun isUSDTW5(walletId: String) = walletPrefsFolder.isUSDTW5(walletId)

fun disableUSDTW5(walletId: String) = walletPrefsFolder.disableUSDTW5(walletId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ internal abstract class BaseSettingsFolder(
notifyChanged()
}

private fun notifyChanged() {
fun notifyChanged() {
_changedFlow.tryEmit(Unit)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ class TokenRepository(
return getRemote(currency, accountId, testnet)
}

suspend fun mustGet(
currency: WalletCurrency,
accountId: String,
testnet: Boolean,
refresh: Boolean = false,
): List<AccountTokenEntity> {
return get(currency, accountId, testnet, refresh) ?: emptyList()
}

private suspend fun getRemote(
currency: WalletCurrency,
accountId: String,
Expand Down
1 change: 1 addition & 0 deletions apps/wallet/instance/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ dependencies {
implementation(project(Dependence.Wallet.Data.purchase))
implementation(project(Dependence.Wallet.Data.battery))
implementation(project(Dependence.Wallet.Data.dapps))
implementation(project(Dependence.Wallet.Data.contacts))

implementation(project(Dependence.UIKit.core))

Expand Down
2 changes: 2 additions & 0 deletions apps/wallet/instance/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
android:enabled="true"
android:name="com.tonapps.tonkeeper.DefaultLauncherIcon"
android:targetActivity="com.tonapps.tonkeeper.ui.screen.root.RootActivity"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:exported="true">

<intent-filter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.tonapps.wallet.data.backup.backupModule
import com.tonapps.wallet.data.battery.batteryModule
import com.tonapps.wallet.data.browser.browserModule
import com.tonapps.wallet.data.collectibles.collectiblesModule
import com.tonapps.wallet.data.contacts.contactsModule
import com.tonapps.wallet.data.core.Theme
import com.tonapps.wallet.data.core.dataModule
import com.tonapps.wallet.data.dapps.dAppsModule
Expand Down Expand Up @@ -78,7 +79,7 @@ class App: Application(), CameraXConfig.Provider, KoinComponent {
instance = this
startKoin {
androidContext(this@App)
modules(koinModel, workerModule, dAppsModule, viewModelWalletModule, purchaseModule, batteryModule, stakingModule, passcodeModule, rnLegacyModule, backupModule, dataModule, browserModule, apiModule, accountModule, ratesModule, tokenModule, eventsModule, collectiblesModule)
modules(koinModel, contactsModule, workerModule, dAppsModule, viewModelWalletModule, purchaseModule, batteryModule, stakingModule, passcodeModule, rnLegacyModule, backupModule, dataModule, browserModule, apiModule, accountModule, ratesModule, tokenModule, eventsModule, collectiblesModule)
workManagerFactory()
}
setLocales(settingsRepository.localeList)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ sealed class AssetsEntity(
suspend fun List<AssetsEntity>.sort(
wallet: WalletEntity,
settingsRepository: SettingsRepository
) = map { asset ->
val pref = if (asset is Token) {
settingsRepository.getTokenPrefs(wallet.id, asset.token.address, asset.token.blacklist)
} else {
TokenPrefsEntity()
}
AssetsExtendedEntity(asset, pref, wallet.accountId)
}.filter { !it.hidden }.sortedWith(AssetsExtendedEntity.comparator).map { it.raw }
): List<AssetsEntity> {
return map { asset ->
val pref = if (asset is Token) {
settingsRepository.getTokenPrefs(wallet.id, asset.token.address, asset.token.blacklist)
} else {
TokenPrefsEntity()
}
AssetsExtendedEntity(asset, pref, wallet.accountId)
}.filter { !it.hidden }.sortedWith(AssetsExtendedEntity.comparator).map { it.raw }
}
}

data class Staked(val staked: StakedEntity): AssetsEntity(staked.fiatBalance) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.tonapps.tonkeeper.koin

import org.koin.androidx.viewmodel.dsl.viewModelOf
import androidx.lifecycle.viewmodel.viewModelFactory
import org.koin.dsl.module
import com.tonapps.tonkeeper.ui.screen.wallet.main.WalletViewModel
import com.tonapps.tonkeeper.ui.screen.settings.main.SettingsViewModel
Expand All @@ -20,14 +20,17 @@ import com.tonapps.tonkeeper.ui.screen.token.picker.TokenPickerViewModel
import com.tonapps.tonkeeper.ui.screen.battery.settings.BatterySettingsViewModel
import com.tonapps.tonkeeper.ui.screen.battery.refill.BatteryRefillViewModel
import com.tonapps.tonkeeper.ui.screen.battery.recharge.BatteryRechargeViewModel
import com.tonapps.tonkeeper.ui.screen.send.contacts.SendContactsViewModel
import com.tonapps.tonkeeper.ui.screen.send.contacts.main.SendContactsViewModel
import com.tonapps.tonkeeper.ui.screen.purchase.main.PurchaseViewModel
import com.tonapps.tonkeeper.ui.screen.nft.NftViewModel
import com.tonapps.tonkeeper.ui.screen.send.contacts.add.AddContactViewModel
import com.tonapps.tonkeeper.ui.screen.send.contacts.edit.EditContactViewModel
import com.tonapps.tonkeeper.ui.screen.staking.viewer.StakeViewerViewModel
import com.tonapps.tonkeeper.ui.screen.staking.unstake.UnStakeViewModel
import com.tonapps.tonkeeper.ui.screen.staking.stake.StakingViewModel
import com.tonapps.tonkeeper.ui.screen.send.transaction.SendTransactionViewModel
import com.tonapps.tonkeeper.ui.screen.staking.withdraw.StakeWithdrawViewModel
import org.koin.core.module.dsl.viewModelOf

val viewModelWalletModule = module {
viewModelOf(::WalletViewModel)
Expand Down Expand Up @@ -56,4 +59,6 @@ val viewModelWalletModule = module {
viewModelOf(::StakingViewModel)
viewModelOf(::SendTransactionViewModel)
viewModelOf(::StakeWithdrawViewModel)
viewModelOf(::AddContactViewModel)
viewModelOf(::EditContactViewModel)
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,13 @@ class AssetsManager(
currency: WalletCurrency = settingsRepository.currency,
refresh: Boolean,
): List<AssetsEntity.Token> {
val onlyVerifyTokens = settingsRepository.onlyVerifyTokens
val tokens = tokenRepository.get(currency, wallet.accountId, wallet.testnet, refresh) ?: return emptyList()
return tokens.map { AssetsEntity.Token(it) }
return if (onlyVerifyTokens) {
tokens.filter { it.verified }.map { AssetsEntity.Token(it) }
} else {
tokens.map { AssetsEntity.Token(it) }
}
}

private suspend fun getStaked(
Expand Down
Loading

0 comments on commit 07861d6

Please sign in to comment.