瀏覽代碼

add share person to another user

MrOzOn 5 年之前
父節點
當前提交
327f76afe2

+ 1 - 1
app/src/main/res/navigation/nav_graph.xml

@@ -77,7 +77,7 @@
     <fragment
         android:id="@+id/listPersonFragment"
         android:name="com.mrozon.feature_person.presentation.ListPersonFragment"
-        android:label="ListPersonFragment"
+        android:label="@string/list_persons"
         tools:layout="@layout/fragment_list_person">
         <action
             android:id="@+id/action_listPersonFragment_to_editPersonFragment"

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -1,3 +1,4 @@
 <resources>
     <string name="app_name">HealthDiary</string>
+    <string name="list_persons">Persons</string>
 </resources>

+ 3 - 0
core_api/src/main/java/com/mrozon/core_api/network/HealthDiaryService.kt

@@ -30,6 +30,9 @@ interface HealthDiaryService {
                            @Path("id") id: String,
                            @Body body: PersonRequest): Response<PersonResponse>
 
+    @POST("add-user-to-patient/")
+    suspend fun sharePerson(@Header("Authorization") token: String, @Body body: SharePersonRequest): Response<PersonResponse>
+
 //    @GET("lego/themes/")
 //    suspend fun getThemes(@Query("page") page: Int? = null,
 //                          @Query("page_size") pageSize: Int? = null,

+ 6 - 0
core_api/src/main/java/com/mrozon/core_api/network/model/SharePersonRequest.kt

@@ -0,0 +1,6 @@
+package com.mrozon.core_api.network.model
+
+data class SharePersonRequest(
+    val patient_id: Int,
+    val username: String
+)

+ 7 - 0
feature_person/src/main/java/com/mrozon/feature_person/data/PersonRemoteDataSource.kt

@@ -3,6 +3,7 @@ package com.mrozon.feature_person.data
 import com.mrozon.core_api.db.HealthDiaryDao
 import com.mrozon.core_api.network.HealthDiaryService
 import com.mrozon.core_api.network.model.PersonRequest
+import com.mrozon.core_api.network.model.SharePersonRequest
 import com.mrozon.utils.base.BaseDataSource
 import javax.inject.Inject
 
@@ -32,4 +33,10 @@ class PersonRemoteDataSource @Inject constructor(private val service: HealthDiar
         val token = "Token "+dao.getAccessToken()
         service.editPerson(token, id.toString(), personRequest)
     }
+
+    suspend fun sharePerson(sharePersonRequest: SharePersonRequest)
+            = getResult {
+        val token = "Token "+dao.getAccessToken()
+        service.sharePerson(token, sharePersonRequest)
+    }
 }

+ 14 - 0
feature_person/src/main/java/com/mrozon/feature_person/data/PersonRepository.kt

@@ -7,6 +7,7 @@ import com.mrozon.core_api.entity.Gender
 import com.mrozon.core_api.entity.Person
 import com.mrozon.core_api.mapper.PersonToPersonDbMapper
 import com.mrozon.core_api.network.model.PersonRequest
+import com.mrozon.core_api.network.model.SharePersonRequest
 import com.mrozon.core_api.network.model.toPerson
 import com.mrozon.core_api.resultLiveData
 import com.mrozon.utils.extension.toDateString
@@ -102,4 +103,17 @@ class PersonRepository @Inject constructor(private val dao: HealthDiaryDao,
         }
     }
 
+    fun sharePerson(id: Long, userName: String): Flow<Result<Unit>> {
+        return flow {
+            emit(loading())
+            val request = SharePersonRequest(patient_id = id.toInt(), username = userName)
+            val response = personRemoteDataSource.sharePerson(request)
+            if (response.status == Result.Status.SUCCESS) {
+                emit(success())
+            } else if (response.status == Result.Status.ERROR) {
+                emit(error(response.message!!))
+            }
+        }
+    }
+
 }

+ 49 - 4
feature_person/src/main/java/com/mrozon/feature_person/presentation/EditPersonFragment.kt

@@ -1,8 +1,11 @@
 package com.mrozon.feature_person.presentation
 
+import android.app.AlertDialog
 import android.content.Context
+import android.content.DialogInterface
 import android.os.Bundle
 import android.view.*
+import android.widget.EditText
 import androidx.core.app.ActivityCompat.invalidateOptionsMenu
 import androidx.core.view.get
 import androidx.fragment.app.viewModels
@@ -108,7 +111,7 @@ class EditPersonFragment : BaseFragment<FragmentEditPersonBinding>() {
                     }
                     Result.Status.ERROR -> {
                         binding?.progressBar?.visible(false)
-                        showError(result.message!!) {}
+                        showError(result.message!!)
                     }
                 }
             }
@@ -139,7 +142,7 @@ class EditPersonFragment : BaseFragment<FragmentEditPersonBinding>() {
                     }
                     Result.Status.ERROR -> {
                         binding?.progressBar?.visible(false)
-                        showError(result.message!!) {}
+                        showError(result.message!!)
                     }
                 }
             }
@@ -157,7 +160,25 @@ class EditPersonFragment : BaseFragment<FragmentEditPersonBinding>() {
                     }
                     Result.Status.ERROR -> {
                         binding?.progressBar?.visible(false)
-                        showError(result.message!!) {}
+                        showError(result.message!!)
+                    }
+                }
+            }
+        })
+
+        viewModel.sharePerson.observe(viewLifecycleOwner, Observer { result ->
+            if(result!=null){
+                when (result.status) {
+                    Result.Status.LOADING -> {
+                        binding?.progressBar?.visible(true)
+                    }
+                    Result.Status.SUCCESS -> {
+                        binding?.progressBar?.visible(false)
+                        show(getString(R.string.share_done))
+                    }
+                    Result.Status.ERROR -> {
+                        binding?.progressBar?.visible(false)
+                        showError(result.message!!)
                     }
                 }
             }
@@ -173,8 +194,10 @@ class EditPersonFragment : BaseFragment<FragmentEditPersonBinding>() {
             saveMenuItem.isVisible = it ?: false
         }
         val deleteMenuItem = menu.findItem(R.id.deletePerson)
+        val shareMenu = menu.findItem(R.id.sharePersonToUser)
         val current_id = arguments?.getLong("current_id",-1)?:-1
         deleteMenuItem.isVisible = current_id>0
+        shareMenu.isVisible = current_id>0
         return super.onCreateOptionsMenu(menu, inflater)
     }
 
@@ -182,7 +205,7 @@ class EditPersonFragment : BaseFragment<FragmentEditPersonBinding>() {
     override fun onOptionsItemSelected(item: MenuItem): Boolean {
         hideKeyboard()
         if (!isActiveNetwork()){
-            showError(getString(R.string.network_inactive)) {}
+            showError(getString(R.string.network_inactive))
             return false
         }
         val current_id = arguments?.getLong("current_id",-1)?:-1
@@ -198,9 +221,31 @@ class EditPersonFragment : BaseFragment<FragmentEditPersonBinding>() {
             R.id.deletePerson -> {
                 viewModel.deletePerson(current_id)
             }
+            R.id.sharePersonToUser -> {
+                showShareDialog(current_id)
+            }
         }
         return false
     }
 
+    private fun showShareDialog(id: Long) {
+        val dialogBuilder = AlertDialog.Builder(requireContext())
+        dialogBuilder.apply {
+            val view = layoutInflater.inflate(R.layout.dialog_share, null)
+            setView(view)
+            val etShareToUser = view.findViewById<EditText>(R.id.etShareToUser)
+            setCancelable(false)
+            setPositiveButton("Ok") { dialog, p1 ->
+                dialog.dismiss()
+                viewModel.sharePersonToUser(id, etShareToUser.text.toString())
+            }
+            setNegativeButton("Cancel") { dialog, _ ->
+                dialog.dismiss()
+            }
+        }
+        val alertDialog = dialogBuilder.create()
+        alertDialog.show()
+    }
+
 
 }

+ 28 - 1
feature_person/src/main/java/com/mrozon/feature_person/presentation/EditPersonFragmentViewModel.kt

@@ -1,10 +1,13 @@
 package com.mrozon.feature_person.presentation
 
+import android.content.Context
+import android.util.Patterns
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.viewModelScope
 import com.mrozon.core_api.entity.Gender
 import com.mrozon.core_api.entity.Person
+import com.mrozon.feature_person.R
 import com.mrozon.feature_person.data.PersonRepository
 import com.mrozon.utils.base.BaseViewModel
 import com.mrozon.utils.network.Result
@@ -16,7 +19,7 @@ import kotlinx.coroutines.flow.combine
 import java.util.*
 import javax.inject.Inject
 
-class EditPersonFragmentViewModel @Inject constructor(val repository: PersonRepository): BaseViewModel() {
+class EditPersonFragmentViewModel @Inject constructor(val context: Context, val repository: PersonRepository): BaseViewModel() {
 
     private val _male = MutableLiveData<Boolean>(true)
     val male: LiveData<Boolean>
@@ -34,6 +37,10 @@ class EditPersonFragmentViewModel @Inject constructor(val repository: PersonRepo
     val deletedPerson: LiveData<Result<Unit>?>
         get() = _deletedPerson
 
+    private var _sharePerson = MutableLiveData<Result<Unit>?>(null)
+    val sharePerson: LiveData<Result<Unit>?>
+        get() = _sharePerson
+
     @ExperimentalCoroutinesApi
     val personNameChannel = ConflatedBroadcastChannel<String>()
 
@@ -120,4 +127,24 @@ class EditPersonFragmentViewModel @Inject constructor(val repository: PersonRepo
         }
     }
 
+    fun sharePersonToUser(id: Long, userName: String) {
+        if(userName.isEmpty())
+        {
+            _sharePerson.value = Result.error(context.getString(R.string.error_empty_string))
+            return
+        }
+        if(!Patterns.EMAIL_ADDRESS.matcher(userName).matches())
+        {
+            _sharePerson.value = Result.error(context.getString(R.string.error_invalid_email))
+            return
+        }
+        viewModelScope.launch(Dispatchers.IO) {
+            repository.sharePerson(id, userName).collect {
+                withContext(Dispatchers.Main) {
+                    _sharePerson.value = it
+                }
+            }
+        }
+    }
+
 }

+ 7 - 6
feature_person/src/main/java/com/mrozon/feature_person/presentation/ListPersonFragment.kt

@@ -3,7 +3,6 @@ package com.mrozon.feature_person.presentation
 import android.content.Context
 import android.os.Bundle
 import android.view.View
-import androidx.core.os.bundleOf
 import androidx.fragment.app.viewModels
 import androidx.lifecycle.Observer
 import androidx.lifecycle.ViewModelProvider
@@ -11,11 +10,11 @@ import androidx.navigation.fragment.findNavController
 import androidx.recyclerview.widget.LinearLayoutManager
 import com.mrozon.core_api.entity.Person
 import com.mrozon.core_api.navigation.ListPersonNavigator
-import com.mrozon.core_api.navigation.LoginNavigator
 import com.mrozon.feature_person.R
 import com.mrozon.feature_person.databinding.FragmentListPersonBinding
 import com.mrozon.feature_person.di.ListPersonFragmentComponent
 import com.mrozon.utils.base.BaseFragment
+import com.mrozon.utils.extension.visible
 import com.mrozon.utils.network.Result
 import timber.log.Timber
 import javax.inject.Inject
@@ -44,6 +43,8 @@ class ListPersonFragment : BaseFragment<FragmentListPersonBinding>() {
         adapter = ListPersonAdapter(object : ListPersonAdapter.ListPersonClickListener {
             override fun onClick(person: Person) {
                 Timber.d("click to ${person.name}")
+                //TODO add logic for click item
+                show("will be soon))")
             }
 
             override fun onLongClick(person: Person) {
@@ -64,15 +65,15 @@ class ListPersonFragment : BaseFragment<FragmentListPersonBinding>() {
         viewModel.persons.observe(viewLifecycleOwner, Observer { result ->
             when (result.status) {
                 Result.Status.LOADING -> {
-                    binding?.srlPerson?.isRefreshing = true
+                    binding?.progressBar?.visible(true)
                 }
                 Result.Status.SUCCESS -> {
-                    binding?.srlPerson?.isRefreshing = false
+                    binding?.progressBar?.visible(false)
                     adapter.submitList(result.data)
                 }
                 Result.Status.ERROR -> {
-                    binding?.srlPerson?.isRefreshing = false
-                    showError(result.message!!) { }
+                    binding?.progressBar?.visible(false)
+                    showError(result.message!!)
                 }
             }
         })

+ 5 - 0
feature_person/src/main/res/drawable/ic_user_add_person_24.xml

@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#FFFFFF"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M15,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM6,10L6,7L4,7v3L1,10v2h3v3h2v-3h3v-2L6,10zM15,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
+</vector>

+ 24 - 0
feature_person/src/main/res/layout/dialog_share.xml

@@ -0,0 +1,24 @@
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:padding="16dp"
+    android:orientation="vertical">
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/dialog_share_txt"
+        android:textAppearance="?android:attr/textAppearanceLarge" />
+
+    <EditText
+        android:id="@+id/etShareToUser"
+        android:hint="@string/example_username"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:imeOptions="actionDone"
+        android:inputType="textEmailAddress">
+
+        <requestFocus />
+
+    </EditText>
+</LinearLayout>

+ 19 - 12
feature_person/src/main/res/layout/fragment_list_person.xml

@@ -25,23 +25,30 @@
             app:srcCompat="@drawable/ic_add_24"
             app:useCompatPadding="true" />
 
-        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
-            android:id="@+id/srlPerson"
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/rvPerson"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="59dp"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:listitem="@layout/item_person" />
+
+        <ProgressBar
+            android:id="@+id/progressBar"
+            style="?android:attr/progressBarStyle"
+            android:layout_width="128dp"
+            android:layout_height="128dp"
+            android:indeterminate="true"
+            android:visibility="invisible"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toTopOf="parent">
-
-            <androidx.recyclerview.widget.RecyclerView
-                android:id="@+id/rvPerson"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                tools:listitem="@layout/item_person" />
-
+            app:layout_constraintTop_toTopOf="parent"
+            tools:visibility="visible" />
 
-        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
 
     </androidx.constraintlayout.widget.ConstraintLayout>
 </layout>

+ 7 - 0
feature_person/src/main/res/menu/add_person_menu.xml

@@ -8,6 +8,13 @@
         android:visible="false"
         app:showAsAction="ifRoom" />
 
+    <item
+        android:id="@+id/sharePersonToUser"
+        android:icon="@drawable/ic_user_add_person_24"
+        android:title="@string/sharePersonToUser"
+        android:visible="false"
+        app:showAsAction="ifRoom" />
+
     <item
         android:id="@+id/addPerson"
         android:icon="@drawable/ic_check_white_24dp"

+ 6 - 0
feature_person/src/main/res/values/strings.xml

@@ -10,4 +10,10 @@
     <string name="tvDOB">Date of birth</string>
     <string name="edit_person">Edit person</string>
     <string name="delete_person">Delete person</string>
+    <string name="sharePersonToUser">Give access to a person to another user</string>
+    <string name="dialog_share_txt">Who do you want to give access to for the current person?</string>
+    <string name="example_username">bla-bla-bla@mail.ru</string>
+    <string name="error_empty_string">Empty field</string>
+    <string name="error_invalid_email">Invalid format username (e-mail)</string>
+    <string name="share_done">Done</string>
 </resources>

+ 11 - 4
utils/src/main/java/com/mrozon/utils/base/BaseDataSource.kt

@@ -3,6 +3,7 @@ package com.mrozon.utils.base
 import retrofit2.Response
 import timber.log.Timber
 import com.mrozon.utils.network.Result
+import org.json.JSONException
 import org.json.JSONObject
 
 abstract class BaseDataSource {
@@ -20,10 +21,16 @@ abstract class BaseDataSource {
             }
             var errMessage = response.message()
             val errorBody= response.errorBody()
-            val jObjError = JSONObject(errorBody?.string()?:"")
-            if(jObjError.length()>0){
-                val key = jObjError.names().get(0).toString()
-                errMessage = jObjError[key].toString().trim('[',']')
+            val textErrorBode = errorBody?.string()?:""
+            if(textErrorBode.startsWith("[")) {
+                errMessage = textErrorBode.trim('[',']').trim('"')
+            }
+            else {
+                val jObjError = JSONObject(textErrorBode)
+                if (jObjError.length() > 0) {
+                    val key = jObjError.names().get(0).toString()
+                    errMessage = jObjError[key].toString().trim('[', ']')
+                }
             }
 //            return error(" ${response.code()} $errMessage")
             return error(errMessage)

+ 10 - 3
utils/src/main/java/com/mrozon/utils/base/BaseFragment.kt

@@ -70,6 +70,16 @@ abstract class BaseFragment<T : ViewDataBinding>: Fragment()//,
         snackbar.show()
     }
 
+    fun showError(message: String) {
+        val snackbar = Snackbar.make(binding?.root!!,message,Snackbar.LENGTH_INDEFINITE)
+        snackbar.view.setBackgroundColor(getColor(requireContext(),R.color.color_snack_error))
+        snackbar.setActionTextColor(Color.WHITE)
+        snackbar.setAction(R.string.Ok) {
+            snackbar.dismiss()
+        }
+        snackbar.show()
+    }
+
     fun showInfo(message: String, action:()->Unit) {
         val snackbar = Snackbar.make(binding?.root!!,message,Snackbar.LENGTH_INDEFINITE)
         snackbar.view.setBackgroundColor(getColor(requireContext(),R.color.color_snack_info))
@@ -83,9 +93,6 @@ abstract class BaseFragment<T : ViewDataBinding>: Fragment()//,
 
     fun show(message: String) {
         val snackbar = Snackbar.make(binding?.root!!,message,Snackbar.LENGTH_LONG)
-        snackbar.setAction(R.string.Ok) {
-            snackbar.dismiss()
-        }
         snackbar.show()
     }
 }