-
Notifications
You must be signed in to change notification settings - Fork 844
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
[PM-13626] Remember last opened view for 5 minutes #4574
base: main
Are you sure you want to change the base?
Conversation
…pResumeData object Signed-off-by: Andre Rosado <[email protected]>
# Conflicts: # app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeScreen.kt
Great job, no security vulnerabilities found in this Pull Request |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #4574 +/- ##
==========================================
- Coverage 88.35% 88.32% -0.04%
==========================================
Files 603 605 +2
Lines 40768 40887 +119
Branches 5738 5772 +34
==========================================
+ Hits 36022 36112 +90
- Misses 2742 2757 +15
- Partials 2004 2018 +14 ☔ View full report in Codecov by Sentry. |
app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceImpl.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/x8bit/bitwarden/data/platform/manager/AppResumeManagerImpl.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/x8bit/bitwarden/data/platform/manager/AppResumeManagerImpl.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/x8bit/bitwarden/data/platform/manager/AppResumeManagerImpl.kt
Show resolved
Hide resolved
app/src/main/java/com/x8bit/bitwarden/data/platform/manager/AppResumeManagerImpl.kt
Outdated
Show resolved
Hide resolved
) : BaseViewModel<SearchState, SearchEvent, SearchAction>( | ||
// We load the state from the savedStateHandle for testing purposes. | ||
initialState = savedStateHandle[KEY_STATE] | ||
?: run { | ||
val searchType = SearchArgs(savedStateHandle).type | ||
val userState = requireNotNull(authRepo.userStateFlow.value) | ||
val specialCircumstance = specialCircumstanceManager.specialCircumstance | ||
val searchTerm = if (specialCircumstance is SpecialCircumstance.SearchShortcut) { |
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 could also be:
(specialCircumstance as? SpecialCircumstance.SearchShortcut)
?.searchTerm
?.also {
specialCircumstanceManager.specialCircumstance = null
}
.orEmpty()
) : BaseViewModel<SearchState, SearchEvent, SearchAction>( | ||
// We load the state from the savedStateHandle for testing purposes. | ||
initialState = savedStateHandle[KEY_STATE] | ||
?: run { | ||
val searchType = SearchArgs(savedStateHandle).type | ||
val userState = requireNotNull(authRepo.userStateFlow.value) | ||
val specialCircumstance = specialCircumstanceManager.specialCircumstance | ||
val searchTerm = if (specialCircumstance is SpecialCircumstance.SearchShortcut) { |
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 could also be:
(specialCircumstance as? SpecialCircumstance.SearchShortcut)
?.searchTerm
?.also {
specialCircumstanceManager.specialCircumstance = null
}
.orEmpty()
/** | ||
* Gets the last lock timestamp for the given [userId]. | ||
*/ | ||
fun getLastLockTimestamp(userId: String): Long? |
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.
These should return the same types, either both longs or both Instants. I kinda prefer Instant
but I'll leave it up to you
app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt
Show resolved
Hide resolved
) : AppResumeManager { | ||
private companion object { | ||
// 5 minutes | ||
private const val UNLOCK_NAVIGATION_TIME: Long = 5 * 60 |
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.
Can we add _SECONDS
to the end of this to indicate units.
Also, this can be a top level property of the file.
authDiskSource.getLastLockTimestamp( | ||
userId = userId, | ||
) ?: Long.MIN_VALUE, | ||
) |
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.
We don't need to handle a null timestamp at all:
val lastLockTimestamp = authDiskSource
.getLastLockTimestamp(userId = userId)
?.let { Instant.ofEpochMilli(it) }
?: return null
app/src/main/java/com/x8bit/bitwarden/data/platform/manager/AppResumeManagerImpl.kt
Outdated
Show resolved
Hide resolved
@@ -268,6 +270,10 @@ class VaultLockManagerImpl( | |||
) | |||
if (!wasVaultLocked) { | |||
mutableVaultStateEventSharedFlow.tryEmit(VaultStateEvent.Locked(userId = userId)) | |||
authDiskSource.storeLastLockTimestamp( | |||
userId = userId, | |||
lastLockTimestamp = clock.instant(), |
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.
👍
@@ -142,6 +153,8 @@ class SearchViewModel @Inject constructor( | |||
is SearchAction.SearchTermChange -> handleSearchTermChange(action) | |||
is SearchAction.VaultFilterSelect -> handleVaultFilterSelect(action) | |||
is SearchAction.OverflowOptionClick -> handleOverflowItemClick(action) | |||
is SearchAction.LifecycleResume -> handleOnResumed() | |||
is SearchAction.LifecycleStop -> handleOnPaused() |
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 function is still called handleOnPaused
even though you are stopping. Can we rename this
app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModel.kt
Outdated
Show resolved
Hide resolved
|
||
Lifecycle.Event.ON_STOP -> { | ||
viewModel.trySendAction(GeneratorAction.LifecycleStop) | ||
} |
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.
Wanna run the formatter here, there should be some newlines.
I would recommend adding a macro for this. There are instructions here.
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.
Formatter is not making any changes on this file
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.
Hmm, are you sure you have the correct formatter configured. Because this when I run the formatter it does this:
LivecycleEventEffect { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> {
viewModel.trySendAction(GeneratorAction.LifecycleResume)
}
Lifecycle.Event.ON_STOP -> {
viewModel.trySendAction(GeneratorAction.LifecycleStop)
}
else -> Unit
}
}
@@ -2139,6 +2150,11 @@ sealed class GeneratorAction { | |||
*/ | |||
data object LifecycleResume : GeneratorAction() | |||
|
|||
/** | |||
* Indicates the UI has been entered a resumed lifecycle state. |
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 refers to resume instead of stop
@@ -314,13 +317,21 @@ class FakeAuthDiskSource : AuthDiskSource { | |||
return storedNewDeviceNoticeState[userId] ?: NewDeviceNoticeState( | |||
displayStatus = NewDeviceNoticeDisplayStatus.HAS_NOT_SEEN, | |||
lastSeenDate = null, | |||
) | |||
) |
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.
👍
@@ -53,6 +53,31 @@ class RootNavViewModelTest : BaseViewModelTest() { | |||
dispatcherManager = FakeDispatcherManager(), | |||
) | |||
|
|||
private val mockVaultUnlockedUserState = UserState( |
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.
Can we just make this a constant at the bottom of the file.
import java.time.Instant | ||
|
||
// 5 minutes | ||
private const val UNLOCK_NAVIGATION_TIME_MS: Long = 5 * 60 |
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 appreciate the comment here, but the _MS
immediately makes me think milliseconds
, UNLOCK_NAVIGATION_TIME_MINUTES
can never go wrong being UBER explicit.
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 think that is my bad, I told him to add _MS
because I thought it was milliseconds.
It should be UNLOCK_NAVIGATION_TIME_SECONDS
since the value is in seconds
…and added test to validate it
# Conflicts: # app/src/main/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt
🎟️ Tracking
https://bitwarden.atlassian.net/browse/PM-13626
📔 Objective
After lock, for the next 5 minutes, the user should be redirected to the previous screen if they were:
To achieve this, a new manager was added to control the behaviour and saving the last screen on
SettingsDiskSource
(should this be on a different disk source?). The navigation is provided by deeplink using the existing SpecialCircumstance.The current screen is always saved
onResume
and clearedonPause
(to ensure we don't navigate to previous screens)Before clearing, checking if the user is not locked, does the trick to not clear in case of lock when the app was on background
A different task will add more screens
⏰ Reminders before review
🦮 Reviewer guidelines
:+1:
) or similar for great changes:memo:
) or ℹ️ (:information_source:
) for notes or general info:question:
) for questions:thinking:
) or 💭 (:thought_balloon:
) for more open inquiry that's not quite a confirmedissue and could potentially benefit from discussion
:art:
) for suggestions / improvements:x:
) or:warning:
) for more significant problems or concerns needing attention:seedling:
) or ♻️ (:recycle:
) for future improvements or indications of technical debt:pick:
) for minor or nitpick changes