mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
Feature/list view (#42)
* contacts view, main classes * add contacts navigation * contacts ui + presentation * contacts domain + di * contact tags, adapter + view * add item decorator, drawable + ext func to core-ui * contacts, update ui + mappers * contacts. update domain + add extension date func * contacts, presentation + domain * contacts ui * update gradle settings * add filters logic * contacts, ui + presentation * filters deign * contacts di + ext * before pr fixes * show contacts on desktop screen * add gitignore * move adapters to class members * rename layout * pr fixes
This commit is contained in:
parent
8eef580be8
commit
f969388996
46 changed files with 1064 additions and 14 deletions
|
@ -92,6 +92,13 @@ class ComponentManager(private val main: MainComponent) {
|
|||
.build()
|
||||
}
|
||||
|
||||
val contactsComponent = Component {
|
||||
main
|
||||
.contactsComponentBuilder()
|
||||
.contactsModule(ContactsModule())
|
||||
.build()
|
||||
}
|
||||
|
||||
class Component<T>(private val builder: () -> T) {
|
||||
|
||||
private var instance: T? = null
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package com.agileburo.anytype.di.feature
|
||||
|
||||
import com.agileburo.anytype.core_utils.di.scope.PerScreen
|
||||
import com.agileburo.anytype.domain.contacts.GetContact
|
||||
import com.agileburo.anytype.domain.contacts.GetContacts
|
||||
import com.agileburo.anytype.domain.database.interactor.AddFilter
|
||||
import com.agileburo.anytype.domain.database.interactor.GetFilters
|
||||
import com.agileburo.anytype.presentation.contacts.ContactsViewModelFactory
|
||||
import com.agileburo.anytype.ui.contacts.ContactsFragment
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.Subcomponent
|
||||
|
||||
@Subcomponent(modules = [ContactsModule::class])
|
||||
@PerScreen
|
||||
interface ContactsSubComponent {
|
||||
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
fun build(): ContactsSubComponent
|
||||
fun contactsModule(module: ContactsModule): Builder
|
||||
}
|
||||
|
||||
fun inject(fragment: ContactsFragment)
|
||||
}
|
||||
|
||||
@Module
|
||||
class ContactsModule {
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideViewModelFactory(
|
||||
getContacts: GetContacts,
|
||||
getContact: GetContact,
|
||||
getFilters: GetFilters,
|
||||
addFilter: AddFilter
|
||||
): ContactsViewModelFactory =
|
||||
ContactsViewModelFactory(
|
||||
getContacts = getContacts,
|
||||
getContact = getContact,
|
||||
getFilters = getFilters,
|
||||
addFilter = AddFilter()
|
||||
)
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideGetContacts(): GetContacts = GetContacts()
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideGetContact(): GetContact = GetContact()
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideGetFilters(): GetFilters = GetFilters()
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideAddFilter(): AddFilter = AddFilter()
|
||||
}
|
|
@ -19,4 +19,5 @@ interface MainComponent {
|
|||
fun keychainPhraseComponentBuilder(): KeychainPhraseSubComponent.Builder
|
||||
fun homeDashboardComponentBuilder(): HomeDashboardSubComponent.Builder
|
||||
fun databaseViewComponentBuilder(): DatabaseViewSubComponent.Builder
|
||||
fun contactsComponentBuilder(): ContactsSubComponent.Builder
|
||||
}
|
|
@ -80,6 +80,10 @@ class Navigator : AppNavigation {
|
|||
*/
|
||||
}
|
||||
|
||||
override fun openContacts() {
|
||||
navController?.navigate(R.id.action_desktopScreen_to_contactsFragment)
|
||||
}
|
||||
|
||||
override fun exit() {
|
||||
navController?.popBackStack()
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ abstract class NavigationFragment(
|
|||
is Command.OpenProfile -> navigation.openProfile()
|
||||
is Command.OpenDocument -> navigation.openDocument(command.id)
|
||||
is Command.OpenKeychainScreen -> navigation.openKeychainScreen()
|
||||
is Command.OpenContactsScreen -> navigation.openContacts()
|
||||
is Command.Exit -> navigation.exit()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package com.agileburo.anytype.ui.contacts
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.agileburo.anytype.R
|
||||
import com.agileburo.anytype.presentation.contacts.model.ContactView
|
||||
import com.agileburo.anytype.ui.contacts.viewholders.ContactViewHolder
|
||||
import com.agileburo.anytype.ui.table.toView
|
||||
|
||||
class ContactsAdapter(
|
||||
private val click: (String) -> Unit
|
||||
) :
|
||||
RecyclerView.Adapter<ContactViewHolder>() {
|
||||
|
||||
private val data = mutableListOf<ContactView>()
|
||||
|
||||
fun updateData(contacts: List<ContactView>) {
|
||||
val size = data.size
|
||||
data.addAll(contacts)
|
||||
notifyItemRangeInserted(size, contacts.size)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactViewHolder =
|
||||
ContactViewHolder(
|
||||
LayoutInflater.from(parent.context).toView(
|
||||
id = R.layout.item_list_contact,
|
||||
parent = parent
|
||||
)
|
||||
)
|
||||
|
||||
override fun getItemCount(): Int = data.size
|
||||
|
||||
override fun onBindViewHolder(holder: ContactViewHolder, position: Int) {
|
||||
holder.bind(data[position], click)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package com.agileburo.anytype.ui.contacts
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.agileburo.anytype.R
|
||||
import com.agileburo.anytype.core_ui.layout.ListDividerItemDecoration
|
||||
import com.agileburo.anytype.core_ui.layout.SpacingItemDecoration
|
||||
import com.agileburo.anytype.di.common.componentManager
|
||||
import com.agileburo.anytype.presentation.contacts.ContactsViewModel
|
||||
import com.agileburo.anytype.presentation.contacts.ContactsViewModelFactory
|
||||
import com.agileburo.anytype.presentation.contacts.ContactsViewState
|
||||
import com.agileburo.anytype.ui.base.NavigationFragment
|
||||
import com.agileburo.anytype.ui.filters.FiltersAdapter
|
||||
import kotlinx.android.synthetic.main.fragment_contacts.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class ContactsFragment :
|
||||
NavigationFragment(R.layout.fragment_contacts), Observer<ContactsViewState> {
|
||||
|
||||
@Inject
|
||||
lateinit var factory: ContactsViewModelFactory
|
||||
|
||||
private lateinit var contactsAdapter: ContactsAdapter
|
||||
private lateinit var filtersAdapter: FiltersAdapter
|
||||
|
||||
private val vm by lazy {
|
||||
ViewModelProviders
|
||||
.of(this, factory)
|
||||
.get(ContactsViewModel::class.java)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
vm.state.observe(viewLifecycleOwner, this)
|
||||
vm.onViewCreated()
|
||||
}
|
||||
|
||||
override fun onChanged(state: ContactsViewState) {
|
||||
when (state) {
|
||||
ContactsViewState.UiEffect.Init -> {
|
||||
with(recyclerContacts) {
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
addItemDecoration(ListDividerItemDecoration(requireContext()))
|
||||
contactsAdapter = ContactsAdapter(vm::onContactClick)
|
||||
}
|
||||
|
||||
with(recyclerFilters) {
|
||||
layoutManager =
|
||||
LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
|
||||
addItemDecoration(
|
||||
SpacingItemDecoration(
|
||||
spacingStart = 20,
|
||||
firstItemSpacingStart = 0
|
||||
)
|
||||
)
|
||||
filtersAdapter = FiltersAdapter(vm::onFilterClick)
|
||||
}
|
||||
vm.getFilters()
|
||||
}
|
||||
is ContactsViewState.ScreenState.Contacts -> {
|
||||
contactsAdapter.updateData(state.contacts)
|
||||
}
|
||||
is ContactsViewState.ScreenState.Filters -> {
|
||||
filtersAdapter.updateData(state.filters)
|
||||
vm.getContacts()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
componentManager().contactsComponent.get().inject(this)
|
||||
}
|
||||
|
||||
override fun releaseDependencies() {
|
||||
componentManager().contactsComponent.release()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.agileburo.anytype.ui.contacts
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.agileburo.anytype.R
|
||||
import com.agileburo.anytype.presentation.contacts.model.TagView
|
||||
import com.agileburo.anytype.ui.contacts.viewholders.TagViewHolder
|
||||
import com.agileburo.anytype.ui.table.toView
|
||||
|
||||
class TagAdapter : RecyclerView.Adapter<TagViewHolder>() {
|
||||
|
||||
private val tags = mutableListOf<TagView>()
|
||||
|
||||
fun addData(tags: List<TagView>) {
|
||||
this.tags.addAll(tags)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
val size = this.tags.size
|
||||
this.tags.clear()
|
||||
notifyItemRangeRemoved(0, size)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TagViewHolder =
|
||||
TagViewHolder(LayoutInflater.from(parent.context).toView(R.layout.item_tag, parent))
|
||||
|
||||
override fun getItemCount(): Int = tags.size
|
||||
|
||||
override fun onBindViewHolder(holder: TagViewHolder, position: Int) {
|
||||
holder.bind(tags[position])
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package com.agileburo.anytype.ui.contacts.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.agileburo.anytype.core_ui.layout.SpacingItemDecoration
|
||||
import com.agileburo.anytype.core_utils.ext.DATE_FORMAT_MMMdYYYY
|
||||
import com.agileburo.anytype.core_utils.ext.formatToDateString
|
||||
import com.agileburo.anytype.presentation.contacts.model.ContactView
|
||||
import com.agileburo.anytype.presentation.contacts.model.TagView
|
||||
import com.agileburo.anytype.ui.contacts.TagAdapter
|
||||
import kotlinx.android.synthetic.main.item_list_contact.view.*
|
||||
import java.util.*
|
||||
|
||||
class ContactViewHolder(itemView: View) :
|
||||
RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
fun bind(contact: ContactView, click: (String) -> Unit) {
|
||||
with(itemView) {
|
||||
name.text = contact.name
|
||||
date.text = contact.date.formatToDateString(DATE_FORMAT_MMMdYYYY, Locale.getDefault())
|
||||
avatar.bind(contact.name)
|
||||
if (databaseViewTags.adapter == null) {
|
||||
contact.tags?.let {
|
||||
initRecyclerView(databaseViewTags, it)
|
||||
}
|
||||
} else {
|
||||
(databaseViewTags.adapter as? TagAdapter)?.clear()
|
||||
contact.tags?.let {
|
||||
(databaseViewTags.adapter as? TagAdapter)?.addData(it)
|
||||
}
|
||||
}
|
||||
this.setOnClickListener {
|
||||
click(contact.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initRecyclerView(rv: RecyclerView, tags: List<TagView>) = with(rv) {
|
||||
addItemDecoration(SpacingItemDecoration(spacingStart = 8, firstItemSpacingStart = 0))
|
||||
layoutManager =
|
||||
LinearLayoutManager(itemView.context, LinearLayoutManager.HORIZONTAL, false)
|
||||
adapter = TagAdapter().also {
|
||||
it.addData(tags)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.agileburo.anytype.ui.contacts.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.agileburo.anytype.presentation.contacts.model.TagView
|
||||
import kotlinx.android.synthetic.main.item_tag.view.*
|
||||
|
||||
class TagViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
fun bind(tag: TagView) {
|
||||
itemView.name.text = tag.name
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package com.agileburo.anytype.ui.filters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.agileburo.anytype.R
|
||||
import com.agileburo.anytype.presentation.filters.model.FilterView
|
||||
import com.agileburo.anytype.ui.filters.viewholders.FilterCheckedViewHolder
|
||||
import com.agileburo.anytype.ui.filters.viewholders.FilterViewHolder
|
||||
import com.agileburo.anytype.ui.table.toView
|
||||
import java.lang.RuntimeException
|
||||
|
||||
class FiltersAdapter(
|
||||
private val click: (String) -> Unit
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private var data = mutableListOf<FilterView>()
|
||||
|
||||
override fun getItemViewType(position: Int): Int = when (data[position].isChecked) {
|
||||
true -> TYPE_CHECKED
|
||||
false -> TYPE_UNCHECKED
|
||||
}
|
||||
|
||||
fun updateData(filters: List<FilterView>) {
|
||||
val size = data.size
|
||||
data.addAll(filters)
|
||||
notifyItemRangeInserted(size, filters.size)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
|
||||
when (viewType) {
|
||||
TYPE_CHECKED -> FilterCheckedViewHolder(
|
||||
LayoutInflater.from(parent.context).toView(
|
||||
R.layout.item_filter_checked,
|
||||
parent
|
||||
)
|
||||
)
|
||||
TYPE_UNCHECKED -> FilterViewHolder(
|
||||
LayoutInflater.from(parent.context).toView(
|
||||
R.layout.item_filter,
|
||||
parent
|
||||
)
|
||||
)
|
||||
else -> throw RuntimeException("Wrong filter ViewType")
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = data.size
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) = when (holder) {
|
||||
is FilterCheckedViewHolder -> {
|
||||
holder.bind(data[position], click)
|
||||
}
|
||||
is FilterViewHolder -> {
|
||||
holder.bind(data[position], click)
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE_CHECKED = 1
|
||||
const val TYPE_UNCHECKED = 2
|
||||
const val TYPE_PLUS = 3
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package com.agileburo.anytype.ui.filters.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.agileburo.anytype.presentation.filters.model.FilterView
|
||||
import kotlinx.android.synthetic.main.item_filter.view.*
|
||||
|
||||
class FilterCheckedViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
fun bind(filter: FilterView, click: (String) -> Unit) {
|
||||
itemView.setOnClickListener {
|
||||
click.invoke(filter.id)
|
||||
}
|
||||
itemView.name.text = filter.name
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package com.agileburo.anytype.ui.filters.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.agileburo.anytype.presentation.filters.model.FilterView
|
||||
import kotlinx.android.synthetic.main.item_filter.view.*
|
||||
|
||||
class FilterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
fun bind(filter: FilterView, click: (String) -> Unit) {
|
||||
itemView.setOnClickListener {
|
||||
click.invoke(filter.id)
|
||||
}
|
||||
itemView.name.text = filter.name
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ class DatabaseViewFragment : ViewStateFragment<ViewState<Table>>(R.layout.fragme
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
vm.state.observe(this, this)
|
||||
vm.state.observe(viewLifecycleOwner, this)
|
||||
vm.onViewCreated()
|
||||
}
|
||||
|
||||
|
|
12
app/src/main/res/drawable/ic_edit.xml
Normal file
12
app/src/main/res/drawable/ic_edit.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M4,20L5,15L13,7L17,11L9,19L4,20Z"
|
||||
android:fillColor="#ACA996"/>
|
||||
<path
|
||||
android:pathData="M15.6929,4.3071L14,6L18,10L19.6929,8.3071C20.0834,7.9166 20.0834,7.2834 19.6929,6.8929L17.1071,4.3071C16.7166,3.9166 16.0834,3.9166 15.6929,4.3071Z"
|
||||
android:fillColor="#ACA996"/>
|
||||
</vector>
|
|
@ -1,9 +1,22 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M4,6L20,6A1,1 0,0 1,21 7L21,7A1,1 0,0 1,20 8L4,8A1,1 0,0 1,3 7L3,7A1,1 0,0 1,4 6z"
|
||||
android:fillColor="#ACA996"/>
|
||||
<path
|
||||
android:pathData="M17.25,7C17.25,8.1046 16.3546,9 15.25,9C14.1454,9 13.25,8.1046 13.25,7C13.25,5.8954 14.1454,5 15.25,5C16.3546,5 17.25,5.8954 17.25,7Z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#ACA996"/>
|
||||
<path
|
||||
android:pathData="M4,16L20,16A1,1 0,0 1,21 17L21,17A1,1 0,0 1,20 18L4,18A1,1 0,0 1,3 17L3,17A1,1 0,0 1,4 16z"
|
||||
android:fillColor="#ACA996"/>
|
||||
<path
|
||||
android:pathData="M9.25,17m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#ACA996"/>
|
||||
</vector>
|
||||
|
|
19
app/src/main/res/drawable/plus.xml
Normal file
19
app/src/main/res/drawable/plus.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="2dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="2"
|
||||
android:viewportHeight="16">
|
||||
|
||||
<group>
|
||||
|
||||
<clip-path android:pathData="M1 0C1.55228 0 2 0.447715 2 1V15C2 15.5523 1.55228 16 1 16C0.447715 16 0 15.5523 0 15V1C0 0.447715 0.447715 0 1 0Z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:pathData="M0 0V16H2V0" />
|
||||
|
||||
</group>
|
||||
|
||||
</vector>
|
||||
|
100
app/src/main/res/layout/fragment_contacts.xml
Normal file
100
app/src/main/res/layout/fragment_contacts.xml
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/relativeLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/settings"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_block_more"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/settings" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pageTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="34sp"
|
||||
android:textStyle="bold"
|
||||
android:text="Contacts"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/avatar"
|
||||
tools:text="Contacts" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerFilters"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/pageTitle"
|
||||
tools:text="FilterList" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icSettings"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:src="@drawable/ic_settings"
|
||||
app:layout_constraintEnd_toStartOf="@+id/icEdit"
|
||||
app:layout_constraintTop_toBottomOf="@+id/recyclerFilters" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerContacts"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="1dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="1dp"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/icSettings" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnNew"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:drawableStart="@drawable/ic_plus"
|
||||
android:drawablePadding="6dp"
|
||||
android:textColor="#ACA996"
|
||||
android:textSize="16sp"
|
||||
android:text="New"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/recyclerFilters"
|
||||
tools:text="New" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icEdit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/recyclerFilters"
|
||||
app:srcCompat="@drawable/ic_edit" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
19
app/src/main/res/layout/item_filter.xml
Normal file
19
app/src/main/res/layout/item_filter.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#ACA996"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Team" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
19
app/src/main/res/layout/item_filter_checked.xml
Normal file
19
app/src/main/res/layout/item_filter_checked.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Team" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
63
app/src/main/res/layout/item_list_contact.xml
Normal file
63
app/src/main/res/layout/item_list_contact.xml
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.agileburo.anytype.core_ui.widgets.AvatarWidget
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@drawable/circle_solid_default"
|
||||
android:contentDescription="@string/description_profile_picture"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:text_size="@dimen/default_avatar_text_size" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:typeface="sans"
|
||||
android:textColor="#2C2B27"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/avatar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Kuzma Sergeevich Petrov-Vodkin" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/databaseViewTags"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/avatar"
|
||||
app:layout_constraintTop_toBottomOf="@+id/name"
|
||||
tools:listitem="@android:layout/simple_gallery_item"
|
||||
tools:orientation="horizontal" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:textColor="#6C6A5F"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/avatar"
|
||||
app:layout_constraintTop_toBottomOf="@+id/databaseViewTags"
|
||||
tools:text="Aug 3, 2019" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
14
app/src/main/res/layout/item_tag.xml
Normal file
14
app/src/main/res/layout/item_tag.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#E89D00"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:background="@drawable/rounded_corner">
|
||||
|
||||
</TextView>
|
|
@ -26,6 +26,9 @@
|
|||
<action
|
||||
android:id="@+id/action_desktopScreen_to_databaseViewFragment"
|
||||
app:destination="@id/databaseViewFragment" />
|
||||
<action
|
||||
android:id="@+id/action_desktopScreen_to_contactsFragment"
|
||||
app:destination="@id/contactsFragment" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -256,5 +259,9 @@
|
|||
android:id="@+id/databaseViewFragment"
|
||||
android:name="com.agileburo.anytype.ui.table.DatabaseViewFragment"
|
||||
android:label="DatabaseViewFragment" />
|
||||
<fragment
|
||||
android:id="@+id/contactsFragment"
|
||||
android:name="com.agileburo.anytype.ui.contacts.ContactsFragment"
|
||||
android:label="ContactsFragment" />
|
||||
|
||||
</navigation>
|
|
@ -21,4 +21,6 @@
|
|||
<dimen name="table_column_header_height">16dp</dimen>
|
||||
<dimen name="table_row_header_width">16dp</dimen>
|
||||
|
||||
<dimen name="contacts_margin_start">20dp</dimen>
|
||||
|
||||
</resources>
|
|
@ -5,8 +5,8 @@ buildscript {
|
|||
ext.gradle_tools = '3.1.3'
|
||||
ext.build_tools = '29.0.0'
|
||||
|
||||
ext.compile_sdk = 28
|
||||
ext.target_sdk = 28
|
||||
ext.compile_sdk = 29
|
||||
ext.target_sdk = 29
|
||||
ext.min_sdk = 26
|
||||
|
||||
ext.application_id = 'com.agileburo.anytype'
|
||||
|
@ -25,7 +25,7 @@ buildscript {
|
|||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.google.gms:google-services:4.3.2'
|
||||
classpath 'com.google.gms:google-services:4.3.3'
|
||||
classpath 'io.fabric.tools:gradle:1.31.1'
|
||||
classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.8"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package com.agileburo.anytype.core_ui.layout
|
||||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import com.agileburo.anytype.core_ui.R
|
||||
|
||||
class ListDividerItemDecoration(context: Context) :
|
||||
DividerItemDecoration(context, VERTICAL) {
|
||||
|
||||
init {
|
||||
context.getDrawable(R.drawable.divider)?.let {
|
||||
setDrawable(it)
|
||||
}
|
||||
}
|
||||
}
|
7
core-ui/src/main/res/drawable/divider.xml
Normal file
7
core-ui/src/main/res/drawable/divider.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<size
|
||||
android:width="1dp"
|
||||
android:height="1dp" />
|
||||
<solid android:color="#DFDDD0" />
|
||||
</shape>
|
16
core-ui/src/main/res/drawable/rounded_corner.xml
Normal file
16
core-ui/src/main/res/drawable/rounded_corner.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#FEF3C5" />
|
||||
|
||||
<solid android:color="#FEF3C5" />
|
||||
|
||||
<padding
|
||||
android:left="1dp"
|
||||
android:right="1dp"
|
||||
android:bottom="1dp"
|
||||
android:top="1dp" />
|
||||
|
||||
<corners android:radius="5dp" />
|
||||
</shape>
|
|
@ -4,6 +4,8 @@ import android.content.Context
|
|||
import android.net.Uri
|
||||
import android.provider.MediaStore
|
||||
import timber.log.Timber
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
fun Context.dimen(res: Int): Float {
|
||||
return resources
|
||||
|
@ -33,4 +35,11 @@ fun Uri.parsePath(context: Context): String {
|
|||
return result ?: throw IllegalStateException("Cold not get real path")
|
||||
}
|
||||
|
||||
fun Throwable.timber() = Timber.e("Get error : ${this.message}")
|
||||
fun Throwable.timber() = Timber.e("Get error : ${this.message}")
|
||||
|
||||
const val DATE_FORMAT_MMMdYYYY = "MMM d, yyyy"
|
||||
|
||||
fun Long.formatToDateString(pattern: String, locale: Locale): String {
|
||||
val formatter = SimpleDateFormat(pattern, locale)
|
||||
return formatter.format(Date(this))
|
||||
}
|
|
@ -15,7 +15,7 @@ ext {
|
|||
constraintLayout_version = '2.0.0-beta1'
|
||||
recyclerview_version = '1.0.0'
|
||||
cardview_version = '1.0.0'
|
||||
material_version = '1.0.0-rc01'
|
||||
material_version = '1.1.0-beta02'
|
||||
|
||||
// Architecture Components
|
||||
lifecycle_version = '2.1.0'
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package com.agileburo.anytype.domain.contacts
|
||||
|
||||
data class Contact(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val date: Long,
|
||||
val icon: String?,
|
||||
val tags: List<Tag>?
|
||||
)
|
||||
|
||||
data class Tag(
|
||||
val name: String
|
||||
)
|
|
@ -0,0 +1,49 @@
|
|||
package com.agileburo.anytype.domain.contacts
|
||||
|
||||
object ContactsMock {
|
||||
|
||||
val CONTACTS = listOf(
|
||||
Contact(
|
||||
id = "bjcbdjdfb",
|
||||
name = "Anton Pronkin",
|
||||
date = 1492387200000,
|
||||
tags = listOf(Tag("Product")),
|
||||
icon = ""
|
||||
),
|
||||
Contact(
|
||||
id = "sadfsdas",
|
||||
name = "Anton Barulenkov",
|
||||
date = 1574351513000,
|
||||
tags = listOf(Tag("Design")),
|
||||
icon = ""
|
||||
),
|
||||
Contact(
|
||||
id = "hfjhfgjkshjdh",
|
||||
name = "Zhanna Sharipova",
|
||||
date = 1571328513000,
|
||||
tags = listOf(Tag("Product")),
|
||||
icon = ""
|
||||
),
|
||||
Contact(
|
||||
id = "dshjfhskdh",
|
||||
name = "Konstantin Ivanov",
|
||||
date = 1571328513000,
|
||||
tags = listOf(Tag("Programming"), Tag("Android")),
|
||||
icon = ""
|
||||
),
|
||||
Contact(
|
||||
id = "sadwcdweew",
|
||||
name = "Evgeny Kozlov",
|
||||
date = 1571328513000,
|
||||
tags = listOf(Tag("Programming"), Tag("Android")),
|
||||
icon = ""
|
||||
),
|
||||
Contact(
|
||||
id = "kfgonk;lmkmvkld8",
|
||||
name = "Denis Batvinkin",
|
||||
date = 1571328513000,
|
||||
tags = listOf(Tag("Programming"), Tag("iOS")),
|
||||
icon = ""
|
||||
)
|
||||
)
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package com.agileburo.anytype.domain.contacts
|
||||
|
||||
import com.agileburo.anytype.domain.base.BaseUseCase
|
||||
import com.agileburo.anytype.domain.base.Either
|
||||
|
||||
class GetContact : BaseUseCase<Contact, GetContact.Params>() {
|
||||
|
||||
override suspend fun run(params: Params): Either<Throwable, Contact> = try {
|
||||
ContactsMock.CONTACTS.find {
|
||||
it.id == params.contactId
|
||||
}.let {
|
||||
if (it == null) {
|
||||
Either.Left(Throwable("Error getting contact by id=${params.contactId}"))
|
||||
} else {
|
||||
Either.Right(it)
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Either.Left(e)
|
||||
}
|
||||
|
||||
class Params(
|
||||
val contactId: String
|
||||
)
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.agileburo.anytype.domain.contacts
|
||||
|
||||
import com.agileburo.anytype.domain.base.BaseUseCase
|
||||
import com.agileburo.anytype.domain.base.Either
|
||||
|
||||
class GetContacts : BaseUseCase<List<Contact>, BaseUseCase.None>() {
|
||||
|
||||
override suspend fun run(params: None): Either<Throwable, List<Contact>> = try {
|
||||
Either.Right(ContactsMock.CONTACTS)
|
||||
} catch (e: Throwable) {
|
||||
Either.Left(e)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package com.agileburo.anytype.domain.database
|
||||
|
||||
import com.agileburo.anytype.domain.database.model.Filter
|
||||
import com.agileburo.anytype.domain.database.model.FilterTypeCondition
|
||||
import com.agileburo.anytype.domain.database.model.FilterTypeEquality
|
||||
|
||||
const val ALL_ID = "-2"
|
||||
const val PLUS_ID = "-1"
|
||||
|
||||
object FilterMock {
|
||||
|
||||
val FILTER_ALL = Filter(
|
||||
propertyId = ALL_ID,
|
||||
condition = FilterTypeCondition.NONE,
|
||||
equality = FilterTypeEquality.EQUAL,
|
||||
value = "All"
|
||||
)
|
||||
|
||||
// val FILTER_PLUS = Filter(
|
||||
// propertyId = PLUS_ID,
|
||||
// condition = FilterTypeCondition.NONE,
|
||||
// equality = FilterTypeEquality.EQUAL,
|
||||
// value = "Plus"
|
||||
// )
|
||||
|
||||
var filters = mutableListOf(
|
||||
Filter(
|
||||
propertyId = "122345",
|
||||
condition = FilterTypeCondition.NONE,
|
||||
equality = FilterTypeEquality.EQUAL,
|
||||
value = "Team"
|
||||
),
|
||||
Filter(
|
||||
propertyId = "987655",
|
||||
condition = FilterTypeCondition.NONE,
|
||||
equality = FilterTypeEquality.EQUAL,
|
||||
value = "Friends"
|
||||
)
|
||||
)
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.agileburo.anytype.domain.database.interactor
|
||||
|
||||
import com.agileburo.anytype.domain.base.BaseUseCase
|
||||
import com.agileburo.anytype.domain.base.Either
|
||||
import com.agileburo.anytype.domain.database.FilterMock
|
||||
import com.agileburo.anytype.domain.database.model.Filter
|
||||
|
||||
class AddFilter : BaseUseCase<Unit, AddFilter.Params>() {
|
||||
|
||||
override suspend fun run(params: Params): Either<Throwable, Unit> = try {
|
||||
FilterMock.filters.add(params.filter)
|
||||
Either.Right(Unit)
|
||||
} catch (e: Throwable) {
|
||||
Either.Left(e)
|
||||
}
|
||||
|
||||
data class Params(val filter: Filter)
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.agileburo.anytype.domain.database.interactor
|
||||
|
||||
import com.agileburo.anytype.domain.base.BaseUseCase
|
||||
import com.agileburo.anytype.domain.base.Either
|
||||
import com.agileburo.anytype.domain.database.FilterMock
|
||||
import com.agileburo.anytype.domain.database.model.Filter
|
||||
|
||||
class GetFilters : BaseUseCase<List<Filter>, BaseUseCase.None>() {
|
||||
|
||||
override suspend fun run(params: None): Either<Throwable, List<Filter>> = try {
|
||||
Either.Right(addDefaultFilters(FilterMock.filters.toMutableList()))
|
||||
} catch (e: Throwable) {
|
||||
Either.Left(e)
|
||||
}
|
||||
|
||||
private fun addDefaultFilters(filters: MutableList<Filter>): List<Filter> {
|
||||
filters.add(0, FilterMock.FILTER_ALL)
|
||||
return filters
|
||||
}
|
||||
}
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -4,4 +4,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
|
||||
|
|
1
lib-middleware/.gitignore
vendored
Normal file
1
lib-middleware/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
|
@ -0,0 +1,112 @@
|
|||
package com.agileburo.anytype.presentation.contacts
|
||||
|
||||
import androidx.lifecycle.*
|
||||
import com.agileburo.anytype.domain.base.BaseUseCase
|
||||
import com.agileburo.anytype.domain.contacts.Contact
|
||||
import com.agileburo.anytype.domain.contacts.GetContact
|
||||
import com.agileburo.anytype.domain.contacts.GetContacts
|
||||
import com.agileburo.anytype.domain.database.ALL_ID
|
||||
import com.agileburo.anytype.domain.database.interactor.AddFilter
|
||||
import com.agileburo.anytype.domain.database.interactor.GetFilters
|
||||
import com.agileburo.anytype.presentation.contacts.model.FilterState
|
||||
import com.agileburo.anytype.presentation.databaseview.mapper.toPresentation
|
||||
import com.agileburo.anytype.presentation.filters.model.FilterView
|
||||
import timber.log.Timber
|
||||
|
||||
class ContactsViewModel(
|
||||
private val getContacts: GetContacts,
|
||||
private val getContact: GetContact,
|
||||
private val getFilters: GetFilters,
|
||||
private val addFilter: AddFilter
|
||||
) : ViewModel() {
|
||||
|
||||
private val stateData = MutableLiveData<ContactsViewState>()
|
||||
val state: LiveData<ContactsViewState> = stateData
|
||||
|
||||
private var filterState = FilterState()
|
||||
|
||||
fun onViewCreated() {
|
||||
stateData.postValue(ContactsViewState.UiEffect.Init)
|
||||
}
|
||||
|
||||
fun getFilters() {
|
||||
getFilters.invoke(viewModelScope, BaseUseCase.None) { results ->
|
||||
results.either(
|
||||
fnL = {
|
||||
Timber.e(it, "Error while getting filters")
|
||||
},
|
||||
fnR = { filters ->
|
||||
filters.map {
|
||||
it.toPresentation()
|
||||
}.let {
|
||||
it.forEach { filter ->
|
||||
if (filter.id == ALL_ID) {
|
||||
filter.isChecked = true
|
||||
}
|
||||
}
|
||||
stateData.postValue(ContactsViewState.ScreenState.Filters(it))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun getContacts() {
|
||||
getContacts.invoke(viewModelScope, BaseUseCase.None) { results ->
|
||||
results.either(
|
||||
fnL = {
|
||||
Timber.e(it, "Error while getting contacts")
|
||||
},
|
||||
fnR = { contacts ->
|
||||
contacts.map { contact ->
|
||||
contact.toPresentation()
|
||||
}.let {
|
||||
stateData.postValue(ContactsViewState.ScreenState.Contacts(it))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onAddFilterClick() {
|
||||
}
|
||||
|
||||
fun onFilterClick(filterId: String) {
|
||||
Timber.d("on filter click : $filterId")
|
||||
}
|
||||
|
||||
fun onContactClick(contactId: String) {
|
||||
getContact.invoke(viewModelScope, GetContact.Params(contactId = contactId)) { result ->
|
||||
result.either(
|
||||
fnL = {
|
||||
Timber.e(it, "Error while getting contact by id:$contactId")
|
||||
},
|
||||
fnR = { contact: Contact ->
|
||||
Timber.d("Get contact : $contact")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addFilterToState(filter: FilterView) {
|
||||
//todo
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class ContactsViewModelFactory(
|
||||
private val getContacts: GetContacts,
|
||||
private val getContact: GetContact,
|
||||
private val getFilters: GetFilters,
|
||||
private val addFilter: AddFilter
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return ContactsViewModel(
|
||||
getContacts = getContacts,
|
||||
getContact = getContact,
|
||||
addFilter = addFilter,
|
||||
getFilters = getFilters
|
||||
) as T
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.agileburo.anytype.presentation.contacts
|
||||
|
||||
import com.agileburo.anytype.presentation.contacts.model.ContactView
|
||||
import com.agileburo.anytype.presentation.filters.model.FilterView
|
||||
|
||||
sealed class ContactsViewState {
|
||||
|
||||
sealed class ScreenState: ContactsViewState(){
|
||||
|
||||
object Loading: ScreenState()
|
||||
data class Contacts(val contacts: List<ContactView>) : ScreenState()
|
||||
data class Filters(val filters: List<FilterView>): ScreenState()
|
||||
object Error: ScreenState()
|
||||
}
|
||||
|
||||
sealed class UiEffect: ContactsViewState() {
|
||||
object Init: UiEffect()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.agileburo.anytype.presentation.contacts.model
|
||||
|
||||
data class ContactView(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val date: Long,
|
||||
val icon: String?,
|
||||
val tags: List<TagView>?
|
||||
)
|
||||
|
||||
data class TagView(
|
||||
val name: String
|
||||
)
|
|
@ -0,0 +1,7 @@
|
|||
package com.agileburo.anytype.presentation.contacts.model
|
||||
|
||||
import com.agileburo.anytype.presentation.filters.model.FilterView
|
||||
|
||||
data class FilterState(
|
||||
var filters: MutableSet<FilterView> = mutableSetOf()
|
||||
)
|
|
@ -1,10 +1,16 @@
|
|||
package com.agileburo.anytype.presentation.databaseview.mapper
|
||||
|
||||
import com.agileburo.anytype.domain.contacts.Contact
|
||||
import com.agileburo.anytype.domain.contacts.Tag
|
||||
import com.agileburo.anytype.domain.database.model.*
|
||||
import com.agileburo.anytype.presentation.contacts.model.ContactView
|
||||
import com.agileburo.anytype.presentation.contacts.model.TagView
|
||||
import com.agileburo.anytype.domain.database.model.DatabaseView
|
||||
import com.agileburo.anytype.domain.database.model.DisplayView
|
||||
import com.agileburo.anytype.domain.database.model.Property
|
||||
import com.agileburo.anytype.domain.database.model.ViewType
|
||||
import com.agileburo.anytype.presentation.databaseview.models.*
|
||||
import com.agileburo.anytype.presentation.filters.model.FilterView
|
||||
|
||||
fun Property.toPresentation(): ColumnView =
|
||||
when (this) {
|
||||
|
@ -89,3 +95,16 @@ fun findTypeOfData(map: HashMap<String, Any>, properties: List<Property>): List<
|
|||
}
|
||||
return cells
|
||||
}
|
||||
|
||||
fun Contact.toPresentation(): ContactView =
|
||||
ContactView(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
date = this.date,
|
||||
icon = this.icon,
|
||||
tags = this.tags?.map { it.toPresentation() })
|
||||
|
||||
fun Tag.toPresentation(): TagView = TagView(name = this.name)
|
||||
|
||||
fun Filter.toPresentation(): FilterView =
|
||||
FilterView(id = this.propertyId, name = this.value as String)
|
|
@ -0,0 +1,3 @@
|
|||
package com.agileburo.anytype.presentation.filters.model
|
||||
|
||||
data class FilterView(val id: String, val name: String, var isChecked: Boolean = false)
|
|
@ -18,6 +18,7 @@ interface AppNavigation {
|
|||
fun startDesktopFromLogin()
|
||||
fun startSplashFromDesktop()
|
||||
fun openKeychainScreen()
|
||||
fun openContacts()
|
||||
fun exit()
|
||||
|
||||
sealed class Command {
|
||||
|
@ -41,6 +42,7 @@ interface AppNavigation {
|
|||
object StartDesktopFromSplash : Command()
|
||||
object StartDesktopFromLogin : Command()
|
||||
object StartSplashFromDesktop : Command()
|
||||
object OpenContactsScreen : Command()
|
||||
}
|
||||
|
||||
interface Provider {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue