Skip to content

Commit 9bfc243

Browse files
authored
Merge pull request #353 from ryanw-mobile/feature/351-account-screen-account-owner-block-improvement
(#351) Account Screen UI update
2 parents 7f27820 + 333fd62 commit 9bfc243

File tree

19 files changed

+254
-130
lines changed

19 files changed

+254
-130
lines changed

composeApp/composeApp.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |spec|
22
spec.name = 'composeApp'
3-
spec.version = '2.2.0'
3+
spec.version = '2.3.0'
44
spec.homepage = 'https://github.com/ryanw-mobile/OctoMeter/'
55
spec.source = { :http=> ''}
66
spec.authors = ''
@@ -51,4 +51,4 @@ Pod::Spec.new do |spec|
5151
}
5252
]
5353
spec.resources = ['build/compose/cocoapods/compose-resources']
54-
end
54+
end

composeApp/src/commonMain/graphql/com/rwmobi/kunigami/queries/PropertiesQuery.graphql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,9 @@ query PropertiesQuery($accountNumber: String!) {
8787
}
8888
}
8989
}
90+
account(accountNumber: $accountNumber) {
91+
users {
92+
preferredName
93+
}
94+
}
9095
}

composeApp/src/commonMain/kotlin/com/rwmobi/kunigami/data/repository/OctopusGraphQLRepository.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -479,9 +479,14 @@ class OctopusGraphQLRepository(
479479
accountNumber = accountNumber,
480480
)
481481

482-
response.properties?.firstOrNull()?.toAccount(accountNumber = accountNumber)?.also {
483-
inMemoryCacheDataSource.cacheProfile(account = it, createdAt = Clock.System.now())
484-
}
482+
val preferredName = response.account?.users?.firstOrNull()?.preferredName
483+
response.properties?.firstOrNull()?.toAccount(
484+
accountNumber = accountNumber,
485+
preferredName = preferredName,
486+
)
487+
?.also {
488+
inMemoryCacheDataSource.cacheProfile(account = it, createdAt = Clock.System.now())
489+
}
485490
}
486491
}.except<CancellationException, _>()
487492
}

composeApp/src/commonMain/kotlin/com/rwmobi/kunigami/data/repository/mapper/AccountMapper.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import kotlinx.datetime.Instant
2525
/***
2626
* Throws: IllegalArgumentException - if the timestamp return from API is not parsable
2727
*/
28-
fun PropertiesQuery.Property.toAccount(accountNumber: String): Account {
28+
fun PropertiesQuery.Property.toAccount(accountNumber: String, preferredName: String?): Account {
2929
// We decided to take the latest occupancy. This has no other side-effects other than a date showing on the screen.
3030
val occupancyPeriod = occupancyPeriods?.maxByOrNull {
3131
it?.effectiveFrom?.let { effectiveFrom ->
@@ -35,6 +35,7 @@ fun PropertiesQuery.Property.toAccount(accountNumber: String): Account {
3535

3636
return Account(
3737
accountNumber = accountNumber,
38+
preferredName = preferredName,
3839
movedInAt = occupancyPeriod?.effectiveFrom?.let { Instant.parse(it.toString()) },
3940
movedOutAt = occupancyPeriod?.effectiveTo?.let { Instant.parse(it.toString()) },
4041
fullAddress = address,

composeApp/src/commonMain/kotlin/com/rwmobi/kunigami/domain/model/account/Account.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import kotlinx.datetime.Instant
2222
@Immutable
2323
data class Account(
2424
val accountNumber: String,
25+
val preferredName: String?,
2526
val fullAddress: String?,
2627
val postcode: String?,
2728
val movedInAt: Instant?,

composeApp/src/commonMain/kotlin/com/rwmobi/kunigami/ui/destinations/account/AccountInformationScreen.kt

Lines changed: 64 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,12 @@ package com.rwmobi.kunigami.ui.destinations.account
2020
import androidx.compose.desktop.ui.tooling.preview.Preview
2121
import androidx.compose.foundation.layout.Arrangement
2222
import androidx.compose.foundation.layout.Column
23-
import androidx.compose.foundation.layout.Row
2423
import androidx.compose.foundation.layout.Spacer
2524
import androidx.compose.foundation.layout.fillMaxSize
2625
import androidx.compose.foundation.layout.fillMaxWidth
2726
import androidx.compose.foundation.layout.height
2827
import androidx.compose.foundation.layout.padding
29-
import androidx.compose.foundation.layout.width
30-
import androidx.compose.foundation.layout.wrapContentHeight
31-
import androidx.compose.material3.ButtonDefaults
28+
import androidx.compose.material3.Card
3229
import androidx.compose.material3.MaterialTheme
3330
import androidx.compose.material3.Surface
3431
import androidx.compose.material3.Text
@@ -37,37 +34,30 @@ import androidx.compose.runtime.getValue
3734
import androidx.compose.runtime.mutableStateOf
3835
import androidx.compose.runtime.saveable.rememberSaveable
3936
import androidx.compose.runtime.setValue
40-
import androidx.compose.ui.Alignment
4137
import androidx.compose.ui.Modifier
38+
import androidx.compose.ui.draw.alpha
4239
import androidx.compose.ui.text.font.FontWeight
4340
import androidx.compose.ui.unit.dp
4441
import com.rwmobi.kunigami.domain.extensions.getLocalDateString
4542
import com.rwmobi.kunigami.domain.model.account.UserProfile
4643
import com.rwmobi.kunigami.ui.components.CommonPreviewSetup
4744
import com.rwmobi.kunigami.ui.components.MessageActionScreen
48-
import com.rwmobi.kunigami.ui.components.SquareButton
4945
import com.rwmobi.kunigami.ui.composehelper.getScreenSizeInfo
46+
import com.rwmobi.kunigami.ui.destinations.account.components.AccountOperationButtonBar
5047
import com.rwmobi.kunigami.ui.destinations.account.components.AppInfoFooter
51-
import com.rwmobi.kunigami.ui.destinations.account.components.DemoModeConfirmDialog
5248
import com.rwmobi.kunigami.ui.destinations.account.components.ElectricityMeterPointCard
5349
import com.rwmobi.kunigami.ui.destinations.account.components.UpdateApiKeyDialog
5450
import com.rwmobi.kunigami.ui.model.SpecialErrorScreen
5551
import com.rwmobi.kunigami.ui.previewsampledata.AccountSamples
5652
import com.rwmobi.kunigami.ui.theme.AppTheme
5753
import com.rwmobi.kunigami.ui.theme.getDimension
5854
import kunigami.composeapp.generated.resources.Res
59-
import kunigami.composeapp.generated.resources.account_clear_cache
6055
import kunigami.composeapp.generated.resources.account_clear_credential_title
61-
import kunigami.composeapp.generated.resources.account_demo_mode
6256
import kunigami.composeapp.generated.resources.account_error_account_empty
6357
import kunigami.composeapp.generated.resources.account_moved_in
6458
import kunigami.composeapp.generated.resources.account_moved_out
6559
import kunigami.composeapp.generated.resources.account_number
6660
import kunigami.composeapp.generated.resources.account_unknown_installation_address
67-
import kunigami.composeapp.generated.resources.account_update_api_key
68-
import kunigami.composeapp.generated.resources.database_remove_outline
69-
import kunigami.composeapp.generated.resources.eraser
70-
import kunigami.composeapp.generated.resources.key
7161
import kunigami.composeapp.generated.resources.retry
7262
import kunigami.composeapp.generated.resources.unlink
7363
import org.jetbrains.compose.resources.ExperimentalResourceApi
@@ -98,39 +88,62 @@ internal fun AccountInformationScreen(
9888
onSecondaryButtonClicked = uiEvent.onClearCredentialButtonClicked,
9989
)
10090
} else {
101-
Spacer(modifier = Modifier.height(height = dimension.grid_2))
102-
103-
Text(
104-
style = MaterialTheme.typography.headlineMedium,
105-
fontWeight = FontWeight.Bold,
106-
maxLines = 1,
107-
text = stringResource(resource = Res.string.account_number, uiState.userProfile.account.accountNumber),
108-
)
109-
110-
uiState.userProfile.account.fullAddress?.let {
111-
Text(
112-
style = MaterialTheme.typography.bodyLarge,
113-
text = it,
114-
)
115-
} ?: run {
116-
Text(
117-
style = MaterialTheme.typography.bodyLarge,
118-
text = stringResource(resource = Res.string.account_unknown_installation_address),
119-
)
120-
}
121-
122-
uiState.userProfile.account.movedInAt?.let {
123-
Text(
124-
style = MaterialTheme.typography.bodyMedium,
125-
text = stringResource(resource = Res.string.account_moved_in, it.getLocalDateString()),
126-
)
127-
}
128-
129-
uiState.userProfile.account.movedOutAt?.let {
130-
Text(
131-
style = MaterialTheme.typography.bodyMedium,
132-
text = stringResource(resource = Res.string.account_moved_out, it.getLocalDateString()),
133-
)
91+
Spacer(modifier = Modifier.height(height = dimension.grid_1))
92+
93+
Card(
94+
modifier = Modifier.fillMaxWidth(),
95+
) {
96+
Column(
97+
modifier = Modifier
98+
.fillMaxWidth()
99+
.padding(dimension.grid_2),
100+
verticalArrangement = Arrangement.spacedBy(dimension.grid_2),
101+
) {
102+
Column {
103+
Text(
104+
style = MaterialTheme.typography.titleLarge,
105+
fontWeight = FontWeight.Bold,
106+
maxLines = 1,
107+
text = "Welcome back ${uiState.userProfile.account.preferredName}!",
108+
)
109+
110+
Text(
111+
modifier = Modifier.alpha(0.5f),
112+
style = MaterialTheme.typography.bodyLarge,
113+
fontWeight = FontWeight.Bold,
114+
maxLines = 1,
115+
text = stringResource(resource = Res.string.account_number, uiState.userProfile.account.accountNumber),
116+
)
117+
}
118+
119+
uiState.userProfile.account.fullAddress?.let {
120+
Text(
121+
style = MaterialTheme.typography.bodyLarge,
122+
text = it,
123+
)
124+
} ?: run {
125+
Text(
126+
style = MaterialTheme.typography.bodyLarge,
127+
text = stringResource(resource = Res.string.account_unknown_installation_address),
128+
)
129+
}
130+
131+
Column {
132+
uiState.userProfile.account.movedInAt?.let {
133+
Text(
134+
style = MaterialTheme.typography.bodyMedium,
135+
text = stringResource(resource = Res.string.account_moved_in, it.getLocalDateString()),
136+
)
137+
}
138+
139+
uiState.userProfile.account.movedOutAt?.let {
140+
Text(
141+
style = MaterialTheme.typography.bodyMedium,
142+
text = stringResource(resource = Res.string.account_moved_out, it.getLocalDateString()),
143+
)
144+
}
145+
}
146+
}
134147
}
135148

136149
uiState.userProfile.account.electricityMeterPoints.forEach { meterPoint ->
@@ -158,61 +171,11 @@ internal fun AccountInformationScreen(
158171
)
159172
}
160173

161-
var shouldShowDemoModeConfirmDialog by rememberSaveable { mutableStateOf(false) }
162-
Row(
163-
modifier = Modifier.fillMaxWidth(),
164-
verticalAlignment = Alignment.Top,
165-
horizontalArrangement = Arrangement.spacedBy(
166-
dimension.grid_4,
167-
alignment = Alignment.CenterHorizontally,
168-
),
169-
) {
170-
SquareButton(
171-
modifier = Modifier
172-
.width(width = 80.dp)
173-
.wrapContentHeight(),
174-
icon = painterResource(resource = Res.drawable.key),
175-
text = stringResource(resource = Res.string.account_update_api_key),
176-
colors = ButtonDefaults.buttonColors().copy(
177-
containerColor = MaterialTheme.colorScheme.secondaryContainer,
178-
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
179-
),
180-
onClick = { isUpdateAPIKeyDialogOpened = true },
181-
)
182-
183-
SquareButton(
184-
modifier = Modifier
185-
.width(width = 80.dp)
186-
.wrapContentHeight(),
187-
icon = painterResource(resource = Res.drawable.database_remove_outline),
188-
text = stringResource(resource = Res.string.account_clear_cache),
189-
colors = ButtonDefaults.buttonColors().copy(
190-
containerColor = MaterialTheme.colorScheme.secondaryContainer,
191-
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
192-
),
193-
onClick = { uiEvent.onClearCache { getString(resource = it) } },
194-
)
195-
196-
SquareButton(
197-
modifier = Modifier
198-
.width(width = 80.dp)
199-
.wrapContentHeight(),
200-
icon = painterResource(resource = Res.drawable.eraser),
201-
text = stringResource(resource = Res.string.account_demo_mode),
202-
colors = ButtonDefaults.buttonColors().copy(
203-
containerColor = MaterialTheme.colorScheme.secondaryContainer,
204-
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
205-
),
206-
onClick = { shouldShowDemoModeConfirmDialog = true },
207-
)
208-
}
209-
210-
if (shouldShowDemoModeConfirmDialog) {
211-
DemoModeConfirmDialog(
212-
onSwitchToDemoMode = uiEvent.onClearCredentialButtonClicked,
213-
onCancel = { shouldShowDemoModeConfirmDialog = false },
214-
)
215-
}
174+
AccountOperationButtonBar(
175+
onUpdateApiKey = { isUpdateAPIKeyDialogOpened = true },
176+
onClearCache = { uiEvent.onClearCache { getString(resource = it) } },
177+
onSwitchToDemoMode = { uiEvent.onClearCredentialButtonClicked },
178+
)
216179

217180
AppInfoFooter(modifier = Modifier.fillMaxWidth())
218181
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright (c) 2024. RW MobiMedia UK Limited
3+
*
4+
* Contributions made by other developers remain the property of their respective authors but are licensed
5+
* to RW MobiMedia UK Limited and others under the same licence terms as the main project, as outlined in
6+
* the LICENSE file.
7+
*
8+
* RW MobiMedia UK Limited reserves the exclusive right to distribute this application on app stores.
9+
* Reuse of this source code, with or without modifications, requires proper attribution to
10+
* RW MobiMedia UK Limited. Commercial distribution of this code or its derivatives without prior written
11+
* permission from RW MobiMedia UK Limited is prohibited.
12+
*
13+
* Please refer to the LICENSE file for the full terms and conditions.
14+
*/
15+
16+
package com.rwmobi.kunigami.ui.destinations.account.components
17+
18+
import androidx.compose.foundation.layout.Arrangement
19+
import androidx.compose.foundation.layout.Row
20+
import androidx.compose.foundation.layout.fillMaxWidth
21+
import androidx.compose.foundation.layout.width
22+
import androidx.compose.foundation.layout.wrapContentHeight
23+
import androidx.compose.material3.ButtonDefaults
24+
import androidx.compose.material3.MaterialTheme
25+
import androidx.compose.runtime.Composable
26+
import androidx.compose.runtime.getValue
27+
import androidx.compose.runtime.mutableStateOf
28+
import androidx.compose.runtime.saveable.rememberSaveable
29+
import androidx.compose.runtime.setValue
30+
import androidx.compose.ui.Alignment
31+
import androidx.compose.ui.Modifier
32+
import androidx.compose.ui.unit.dp
33+
import com.rwmobi.kunigami.ui.components.SquareButton
34+
import com.rwmobi.kunigami.ui.composehelper.getScreenSizeInfo
35+
import com.rwmobi.kunigami.ui.theme.getDimension
36+
import kunigami.composeapp.generated.resources.Res
37+
import kunigami.composeapp.generated.resources.account_clear_cache
38+
import kunigami.composeapp.generated.resources.account_demo_mode
39+
import kunigami.composeapp.generated.resources.account_update_api_key
40+
import kunigami.composeapp.generated.resources.database_remove_outline
41+
import kunigami.composeapp.generated.resources.eraser
42+
import kunigami.composeapp.generated.resources.key
43+
import org.jetbrains.compose.resources.painterResource
44+
import org.jetbrains.compose.resources.stringResource
45+
46+
@Composable
47+
internal fun AccountOperationButtonBar(
48+
onUpdateApiKey: () -> Unit,
49+
onClearCache: () -> Unit,
50+
onSwitchToDemoMode: () -> Unit,
51+
) {
52+
val dimension = getScreenSizeInfo().getDimension()
53+
var shouldShowDemoModeConfirmDialog by rememberSaveable { mutableStateOf(false) }
54+
Row(
55+
modifier = Modifier.fillMaxWidth(),
56+
verticalAlignment = Alignment.Top,
57+
horizontalArrangement = Arrangement.spacedBy(
58+
dimension.grid_4,
59+
alignment = Alignment.CenterHorizontally,
60+
),
61+
) {
62+
SquareButton(
63+
modifier = Modifier
64+
.width(width = 80.dp)
65+
.wrapContentHeight(),
66+
icon = painterResource(resource = Res.drawable.key),
67+
text = stringResource(resource = Res.string.account_update_api_key),
68+
colors = ButtonDefaults.buttonColors().copy(
69+
containerColor = MaterialTheme.colorScheme.secondaryContainer,
70+
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
71+
),
72+
onClick = onUpdateApiKey,
73+
)
74+
75+
SquareButton(
76+
modifier = Modifier
77+
.width(width = 80.dp)
78+
.wrapContentHeight(),
79+
icon = painterResource(resource = Res.drawable.database_remove_outline),
80+
text = stringResource(resource = Res.string.account_clear_cache),
81+
colors = ButtonDefaults.buttonColors().copy(
82+
containerColor = MaterialTheme.colorScheme.secondaryContainer,
83+
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
84+
),
85+
onClick = onClearCache,
86+
)
87+
88+
SquareButton(
89+
modifier = Modifier
90+
.width(width = 80.dp)
91+
.wrapContentHeight(),
92+
icon = painterResource(resource = Res.drawable.eraser),
93+
text = stringResource(resource = Res.string.account_demo_mode),
94+
colors = ButtonDefaults.buttonColors().copy(
95+
containerColor = MaterialTheme.colorScheme.secondaryContainer,
96+
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
97+
),
98+
onClick = { shouldShowDemoModeConfirmDialog = true },
99+
)
100+
}
101+
102+
if (shouldShowDemoModeConfirmDialog) {
103+
DemoModeConfirmDialog(
104+
onSwitchToDemoMode = onSwitchToDemoMode,
105+
onCancel = { shouldShowDemoModeConfirmDialog = false },
106+
)
107+
}
108+
}

0 commit comments

Comments
 (0)