-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alan/generategeodatabasereplicafromfeatureservice migration #297
Alan/generategeodatabasereplicafromfeatureservice migration #297
Conversation
…service_migration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, a few small comments
...java/com/esri/arcgismaps/sample/generategeodatabasereplicafromfeatureservice/MainActivity.kt
Outdated
Show resolved
Hide resolved
...eplicafromfeatureservice/components/GenerateGeodatabaseReplicaFromFeatureServiceViewModel.kt
Outdated
Show resolved
Hide resolved
...eplicafromfeatureservice/components/GenerateGeodatabaseReplicaFromFeatureServiceViewModel.kt
Outdated
Show resolved
Hide resolved
...eplicafromfeatureservice/components/GenerateGeodatabaseReplicaFromFeatureServiceViewModel.kt
Outdated
Show resolved
Hide resolved
*/ | ||
fun calculateDownloadArea() { | ||
// upper left corner of the area to take offline | ||
val minScreenPoint = ScreenCoordinate(200.0, 200.0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have we double checked if this is local or global device coordinates? If it is not local to the map view, we should make sure that it is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The doc for mapViewProxy.screenToLocationOrNull
says it "Converts a screen coordinate (in pixels) to a coordinate within the mapview's spatial reference" so it seems OK to me, and it works fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did some testing and also seems to work okay 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hud10837 I've adopted all your suggestions apart from one. Please have another look.
*/ | ||
fun calculateDownloadArea() { | ||
// upper left corner of the area to take offline | ||
val minScreenPoint = ScreenCoordinate(200.0, 200.0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The doc for mapViewProxy.screenToLocationOrNull
says it "Converts a screen coordinate (in pixels) to a coordinate within the mapview's spatial reference" so it seems OK to me, and it works fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have left some comments about handling UI state in a more idiomatic way, but all seems fine beyond that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, but actually I think it's better to show the UI elements, so I'm fairly happy with the screenshot I've got.
// job progress dialog visibility state | ||
var showJobProgressDialog by mutableStateOf(false) | ||
private set | ||
|
||
// job progress percentage | ||
var jobProgress by mutableIntStateOf(0) | ||
private set | ||
|
||
// state variables indicating if the buttons are enabled | ||
var resetButtonEnabled by mutableStateOf(false) | ||
private set | ||
var generateButtonEnabled by mutableStateOf(false) | ||
private set |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This all works and we can keep it the way it is, but I'm a bit sceptical of having so much Compose state in the ViewModel.
There's quite a few places in the VM where buttons or dialogues are enabled/disabled, which is a layer of implicit UI state that would be better to make explicit (EG as an enum class), and expose that to the UI through a StateFlow
. But it could be a lot of work.
geodatabaseSyncTask.load().onFailure { error -> | ||
messageDialogVM.showMessageDialog( | ||
title = "Failed to load GeodatabaseSyncTask", | ||
description = error.message.toString() | ||
) | ||
}.onSuccess { | ||
generateButtonEnabled = true | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think this reads a bit cleaner if onSuccess
and onFailure
are swapped around.
* Reset the map to its original state. | ||
*/ | ||
fun resetMap() { | ||
// clear any layers and symbols already on the map |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// clear any layers and symbols already on the map | |
// clear any layers and symbols already on the map |
// create a flow-collection for the job's progress | ||
viewModelScope.launch(Dispatchers.Main) { | ||
job.progress.collect { progress -> | ||
jobProgress = progress | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could expose job.progress
to the UI directly as well - although there isn't always a generateGeodatabaseJob, so it may not be so simple.
.padding(16.dp), | ||
horizontalArrangement = Arrangement.SpaceEvenly | ||
) { | ||
// the Reset Map button |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think this comment (and the one for the other button) are a bit superfluous.
// display progress dialog while generating a geodatabase replica | ||
if (mapViewModel.showJobProgressDialog) { | ||
JobLoadingDialog( | ||
title = "Generating geodatabase replica...", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could extract this string into strings.xml
since all the other ones are.
…& move a string to the resources file
…when ViewModel is destroyed
…riables by one UiState flow
Thanks @01smito01, good feedback. I've made changes as you suggested, including reworking the UI state handling. Please have another look. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good - a few comments on naming which I think could be clearer. The only issue is that I haven't figured out how.
READY_FOR_GENERATE, | ||
GENERATING, | ||
REPLICA_DISPLAYED | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add newline
|
||
enum class UiStatus { | ||
STARTING, | ||
READY_FOR_GENERATE, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reads slightly odd to me, how about READY_TO_GENERATE
or READY_FOR_GENERATION
?
// state flow to expose current UI state to UI | ||
private val _uiStateFlow = MutableStateFlow(UiState(status = UiStatus.STARTING)) | ||
val uiStateFlow = _uiStateFlow.asStateFlow() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a lot of things called uiState/status here - wonder if some of them could be renamed for clarity.
Not 100% sure what to do: maybe UiState
→ GenerateGeodatabaseJobStatus
or UiStatus
→ GeodatabaseGenerationStatus
?
Think the comment could be rewritten too - surely we're exposing application state to the UI, not UI state? UI state is the product of whatever data we pass to the UI.
Button( | ||
onClick = { | ||
mapViewModel.resetMap() | ||
}, | ||
enabled = mapViewModel.resetButtonEnabled | ||
enabled = uiState.status == UiStatus.REPLICA_DISPLAYED |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On naming: uiState.status
feels particularly redundant/confusing.
@01smito01 I've renamed As for
and the UI layer guide doc, when talking about State Holders, includes several diagrams showing the ViewModel holding the UI state. Please have another look. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
Thanks @01smito01 |
Description
Migrate the
generate-geodatabase-replica-from-feature-service
sample from XML to Compose.Links and Data
What To Review
How to Test
Run the sample on the sample viewer or the repo.