Skip to content

Commit 38004f3

Browse files
Merge pull request #5444 from simpledotorg/master
2 parents 96788ce + 1c39a33 commit 38004f3

37 files changed

+1564
-952
lines changed

.codeclimate.yml

Lines changed: 0 additions & 49 deletions
This file was deleted.

.qlty/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
*
2+
!configs
3+
!configs/**
4+
!hooks
5+
!hooks/**
6+
!qlty.toml
7+
!.gitignore

.qlty/configs/.shellcheckrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
source-path=SCRIPTDIR

.qlty/configs/.yamllint.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
rules:
2+
document-start: disable
3+
quoted-strings:
4+
required: only-when-needed
5+
extra-allowed: ["{|}"]
6+
key-duplicates: {}
7+
octal-values:
8+
forbid-implicit-octal: true

.qlty/qlty.toml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# This file was automatically generated by `qlty init`.
2+
# You can modify it to suit your needs.
3+
# We recommend you to commit this file to your repository.
4+
#
5+
# This configuration is used by both Qlty CLI and Qlty Cloud.
6+
#
7+
# Qlty CLI -- Code quality toolkit for developers
8+
# Qlty Cloud -- Fully automated Code Health Platform
9+
#
10+
# Try Qlty Cloud: https://qlty.sh
11+
#
12+
# For a guide to configuration, visit https://qlty.sh/d/config
13+
# Or for a full reference, visit https://qlty.sh/d/qlty-toml
14+
config_version = "0"
15+
16+
exclude_patterns = [
17+
"*_min.*",
18+
"*-min.*",
19+
"*.min.*",
20+
"**/.yarn/**",
21+
"**/*.d.ts",
22+
"**/assets/**",
23+
"**/bower_components/**",
24+
"**/build/**",
25+
"**/cache/**",
26+
"**/config/**",
27+
"**/db/**",
28+
"**/deps/**",
29+
"**/dist/**",
30+
"**/extern/**",
31+
"**/external/**",
32+
"**/generated/**",
33+
"**/Godeps/**",
34+
"**/gradlew/**",
35+
"**/mvnw/**",
36+
"**/node_modules/**",
37+
"**/protos/**",
38+
"**/seed/**",
39+
"**/target/**",
40+
"**/templates/**",
41+
"**/testdata/**",
42+
"**/vendor/**", "**/androidTest/", "**/test/", "**/sharedTest/", "**/src/main/java/org/simple/clinic/storage/migrations/Migration_*.kt", "**/build/", "router/",
43+
]
44+
45+
test_patterns = [
46+
"**/test/**",
47+
"**/spec/**",
48+
"**/*.test.*",
49+
"**/*.spec.*",
50+
"**/*_test.*",
51+
"**/*_spec.*",
52+
"**/test_*.*",
53+
"**/spec_*.*",
54+
]
55+
56+
[smells]
57+
mode = "block"
58+
59+
[smells.boolean_logic]
60+
threshold = 1
61+
enabled = true
62+
63+
[smells.file_complexity]
64+
threshold = 55
65+
enabled = false
66+
67+
[smells.return_statements]
68+
threshold = 2
69+
enabled = true
70+
71+
[smells.nested_control_flow]
72+
threshold = 2
73+
enabled = true
74+
75+
[smells.function_parameters]
76+
threshold = 6
77+
enabled = true
78+
79+
[smells.function_complexity]
80+
threshold = 11
81+
enabled = true
82+
83+
[smells.duplication]
84+
enabled = false
85+
86+
[[source]]
87+
name = "default"
88+
default = true

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
- Handle window insets when displaying app content in edge-to-edge
88
- Enable edge-to-edge support on all versions of Android
99
- Handle nullable inputs when removing last chip in the `ChipInputAutoCompleteTextView`
10+
- Migrate `PatientSummaryScreen` toolbar to Jetpack Compose
11+
- Add Compose components for Overdue screen
12+
- Migrate medicines summary view to Jetpack Compose
13+
- Migrate codeclimate config to qlty.sh config
14+
- Migrate `OverdueScreen` patient list to Jetpack Compose
1015

1116
## 2025.05.20
1217

app/src/main/java/org/simple/clinic/home/overdue/OverdueScreen.kt

Lines changed: 50 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import android.view.LayoutInflater
77
import android.view.View
88
import android.view.ViewGroup
99
import androidx.appcompat.app.AppCompatActivity
10-
import androidx.recyclerview.widget.LinearLayoutManager
10+
import androidx.compose.runtime.getValue
11+
import androidx.compose.runtime.mutableStateOf
12+
import androidx.compose.runtime.setValue
13+
import androidx.compose.ui.platform.ViewCompositionStrategy
1114
import com.f2prateek.rx.preferences2.Preference
1215
import com.google.android.material.dialog.MaterialAlertDialogBuilder
1316
import com.jakewharton.rxbinding3.view.clicks
@@ -17,25 +20,23 @@ import io.reactivex.Observable
1720
import io.reactivex.disposables.CompositeDisposable
1821
import io.reactivex.rxkotlin.cast
1922
import io.reactivex.rxkotlin.ofType
23+
import io.reactivex.subjects.PublishSubject
2024
import kotlinx.parcelize.Parcelize
2125
import org.simple.clinic.R
2226
import org.simple.clinic.ReportAnalyticsEvents
2327
import org.simple.clinic.activity.permissions.RequestPermissions
2428
import org.simple.clinic.activity.permissions.RuntimePermissions
2529
import org.simple.clinic.appconfig.Country
2630
import org.simple.clinic.contactpatient.ContactPatientBottomSheet
27-
import org.simple.clinic.databinding.ListItemDividerBinding
28-
import org.simple.clinic.databinding.ListItemNoPendingPatientsBinding
29-
import org.simple.clinic.databinding.ListItemOverdueListSectionHeaderBinding
30-
import org.simple.clinic.databinding.ListItemOverduePatientBinding
31-
import org.simple.clinic.databinding.ListItemOverduePendingListFooterBinding
32-
import org.simple.clinic.databinding.ListItemSearchOverduePatientButtonBinding
3331
import org.simple.clinic.databinding.ScreenOverdueBinding
3432
import org.simple.clinic.di.injector
3533
import org.simple.clinic.feature.Feature.OverdueInstantSearch
3634
import org.simple.clinic.feature.Feature.PatientReassignment
3735
import org.simple.clinic.feature.Features
3836
import org.simple.clinic.home.HomeScreen
37+
import org.simple.clinic.home.overdue.compose.OverdueAppointmentListItem
38+
import org.simple.clinic.home.overdue.compose.OverdueUiModel
39+
import org.simple.clinic.home.overdue.compose.OverdueUiModelMapper
3940
import org.simple.clinic.home.overdue.search.OverdueSearchScreen
4041
import org.simple.clinic.navigation.v2.Router
4142
import org.simple.clinic.navigation.v2.ScreenKey
@@ -54,7 +55,6 @@ import org.simple.clinic.util.UserClock
5455
import org.simple.clinic.util.UtcClock
5556
import org.simple.clinic.util.applyInsetsBottomPadding
5657
import org.simple.clinic.util.unsafeLazy
57-
import org.simple.clinic.widgets.ItemAdapter
5858
import org.simple.clinic.widgets.UiEvent
5959
import java.time.Instant
6060
import java.time.LocalDate
@@ -115,37 +115,13 @@ class OverdueScreen : BaseScreen<
115115
@Inject
116116
lateinit var locale: Locale
117117

118-
private val overdueListAdapter = ItemAdapter(
119-
diffCallback = OverdueAppointmentListItem.DiffCallback(),
120-
bindings = mapOf(
121-
R.layout.list_item_overdue_patient to { layoutInflater, parent ->
122-
ListItemOverduePatientBinding.inflate(layoutInflater, parent, false)
123-
},
124-
R.layout.list_item_overdue_list_section_header to { layoutInflater, parent ->
125-
ListItemOverdueListSectionHeaderBinding.inflate(layoutInflater, parent, false)
126-
},
127-
R.layout.list_item_overdue_pending_list_footer to { layoutInflater, parent ->
128-
ListItemOverduePendingListFooterBinding.inflate(layoutInflater, parent, false)
129-
},
130-
R.layout.list_item_no_pending_patients to { layoutInflater, parent ->
131-
ListItemNoPendingPatientsBinding.inflate(layoutInflater, parent, false)
132-
},
133-
R.layout.list_item_divider to { layoutInflater, parent ->
134-
ListItemDividerBinding.inflate(layoutInflater, parent, false)
135-
},
136-
R.layout.list_item_search_overdue_patient_button to { layoutInflater, parent ->
137-
ListItemSearchOverduePatientButtonBinding.inflate(layoutInflater, parent, false)
138-
}
139-
)
140-
)
141-
142118
private val disposable = CompositeDisposable()
143119

144120
private val viewForEmptyList
145121
get() = binding.viewForEmptyList
146122

147-
private val overdueRecyclerView
148-
get() = binding.overdueRecyclerView
123+
private val composeView
124+
get() = binding.composeView
149125

150126
private val overdueProgressBar
151127
get() = binding.overdueProgressBar
@@ -172,18 +148,22 @@ class OverdueScreen : BaseScreen<
172148
country.isoCountryCode == Country.INDIA
173149
}
174150

151+
private var uiModelsState by mutableStateOf<List<OverdueUiModel>>(emptyList())
152+
175153
override fun defaultModel() = OverdueModel.create()
176154

177155
override fun bindView(layoutInflater: LayoutInflater, container: ViewGroup?) =
178156
ScreenOverdueBinding.inflate(layoutInflater, container, false)
179157

180158
override fun uiRenderer() = OverdueUiRenderer(ui = this)
181159

160+
private val composeUiEvents = PublishSubject.create<OverdueEvent>()
161+
182162
override fun events() = Observable.mergeArray(
183-
overdueListAdapter.itemEvents,
184163
downloadOverdueListClicks(),
185164
shareOverdueListClicks(),
186-
clearSelectedOverdueAppointmentClicks()
165+
clearSelectedOverdueAppointmentClicks(),
166+
composeUiEvents,
187167
)
188168
.compose(RequestPermissions(runtimePermissions, screenResults.streamResults().ofType()))
189169
.compose(runtimeNetworkStatus::apply)
@@ -218,12 +198,38 @@ class OverdueScreen : BaseScreen<
218198

219199
buttonsFrame.applyInsetsBottomPadding()
220200

221-
overdueRecyclerView.adapter = overdueListAdapter
222-
overdueRecyclerView.layoutManager = LinearLayoutManager(context)
201+
composeView.apply {
202+
setViewCompositionStrategy(
203+
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
204+
)
205+
setContent {
206+
OverdueAppointmentListItem(
207+
uiModels = uiModelsState,
208+
onCallClicked = { patientId ->
209+
composeUiEvents.onNext(CallPatientClicked(patientId))
210+
},
211+
onRowClicked = { patientId ->
212+
composeUiEvents.onNext(OverduePatientClicked(patientId))
213+
},
214+
onCheckboxClicked = { appointmentUuid ->
215+
composeUiEvents.onNext(OverdueAppointmentCheckBoxClicked(appointmentUuid))
216+
},
217+
onSearch = {
218+
composeUiEvents.onNext(OverdueSearchButtonClicked)
219+
},
220+
onSectionHeaderClick = { overdueAppointmentSectionTitle ->
221+
composeUiEvents.onNext(ChevronClicked(overdueAppointmentSectionTitle))
222+
},
223+
onSectionFooterClick = {
224+
composeUiEvents.onNext(PendingListFooterClicked)
225+
}
226+
)
227+
}
228+
}
223229
}
224230

231+
225232
override fun onDestroyView() {
226-
overdueRecyclerView.adapter = null
227233
disposable.clear()
228234
super.onDestroyView()
229235
}
@@ -267,7 +273,7 @@ class OverdueScreen : BaseScreen<
267273
selectedOverdueAppointments: Set<UUID>,
268274
overdueListSectionStates: OverdueListSectionStates
269275
) {
270-
overdueListAdapter.submitList(OverdueAppointmentListItem.from(
276+
uiModelsState = OverdueUiModelMapper.from(
271277
overdueAppointmentSections = overdueAppointmentSections,
272278
clock = userClock,
273279
pendingListDefaultStateSize = pendingAppointmentsConfig.pendingListDefaultStateSize,
@@ -277,7 +283,8 @@ class OverdueScreen : BaseScreen<
277283
selectedOverdueAppointments = selectedOverdueAppointments,
278284
isPatientReassignmentFeatureEnabled = features.isEnabled(PatientReassignment),
279285
locale = locale,
280-
))
286+
)
287+
281288
if (isOverdueListDownloadAndShareEnabled) {
282289
buttonsFrame.visibility = View.VISIBLE
283290
}
@@ -316,11 +323,11 @@ class OverdueScreen : BaseScreen<
316323
}
317324

318325
override fun showOverdueRecyclerView() {
319-
overdueRecyclerView.visibility = View.VISIBLE
326+
composeView.visibility = View.VISIBLE
320327
}
321328

322329
override fun hideOverdueRecyclerView() {
323-
overdueRecyclerView.visibility = View.GONE
330+
composeView.visibility = View.GONE
324331
}
325332

326333
override fun openOverdueSearch() {

0 commit comments

Comments
 (0)