-
Notifications
You must be signed in to change notification settings - Fork 709
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
FTUE - Choose a display picture #5323
Changes from 17 commits
6b62b5b
53e9e8f
281b65c
8172589
c4dc874
05394a9
dcb16dd
bcf84b8
d3589fb
479bff3
efb08b1
146ad6f
9518581
989e762
c934fdc
aa3f1b0
6b04efd
deec923
1fc1935
3a2dea2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<resources> | ||
<item name="ftue_auth_profile_picture_height" format="float" type="dimen">0.15</item> | ||
<item name="ftue_auth_profile_picture_icon_height" format="float" type="dimen">0.05</item> | ||
</resources> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,6 +64,7 @@ import org.matrix.android.sdk.api.failure.Failure | |
import org.matrix.android.sdk.api.failure.MatrixIdFailure | ||
import org.matrix.android.sdk.api.session.Session | ||
import timber.log.Timber | ||
import java.util.UUID | ||
import java.util.concurrent.CancellationException | ||
|
||
/** | ||
|
@@ -80,6 +81,7 @@ class OnboardingViewModel @AssistedInject constructor( | |
private val homeServerHistoryService: HomeServerHistoryService, | ||
private val vectorFeatures: VectorFeatures, | ||
private val analyticsTracker: AnalyticsTracker, | ||
private val uriFilenameResolver: UriFilenameResolver, | ||
private val vectorOverrides: VectorOverrides | ||
) : VectorViewModel<OnboardingViewState, OnboardingAction, OnboardingViewEvents>(initialState) { | ||
|
||
|
@@ -157,6 +159,9 @@ class OnboardingViewModel @AssistedInject constructor( | |
is OnboardingAction.PostViewEvent -> _viewEvents.post(action.viewEvent) | ||
is OnboardingAction.UpdateDisplayName -> updateDisplayName(action.displayName) | ||
OnboardingAction.UpdateDisplayNameSkipped -> _viewEvents.post(OnboardingViewEvents.OnDisplayNameSkipped) | ||
OnboardingAction.UpdateProfilePictureSkipped -> _viewEvents.post(OnboardingViewEvents.OnPersonalizationComplete) | ||
is OnboardingAction.ProfilePictureSelected -> handleProfilePictureSelected(action) | ||
OnboardingAction.SaveSelectedProfilePicture -> updateProfilePicture() | ||
}.exhaustive | ||
} | ||
|
||
|
@@ -899,14 +904,59 @@ class OnboardingViewModel @AssistedInject constructor( | |
val activeSession = activeSessionHolder.getActiveSession() | ||
try { | ||
activeSession.setDisplayName(activeSession.myUserId, displayName) | ||
setState { copy(asyncDisplayName = Success(Unit)) } | ||
setState { | ||
copy( | ||
asyncDisplayName = Success(Unit), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't it be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a good point, at the moment those asyncs are only being used for showing/hiding the loading view, in WDYT? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds like a plan! OK for me. What I want to avoid is having multiple sources of truth. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will raise a PR for it 💯 |
||
personalizationState = personalizationState.copy(displayName = displayName) | ||
) | ||
} | ||
_viewEvents.post(OnboardingViewEvents.OnDisplayNameUpdated) | ||
} catch (error: Throwable) { | ||
setState { copy(asyncDisplayName = Fail(error)) } | ||
_viewEvents.post(OnboardingViewEvents.Failure(error)) | ||
} | ||
} | ||
} | ||
|
||
private fun handleProfilePictureSelected(action: OnboardingAction.ProfilePictureSelected) { | ||
setState { | ||
copy(personalizationState = personalizationState.copy(selectedPictureUri = action.uri)) | ||
} | ||
} | ||
|
||
private fun updateProfilePicture() { | ||
withState { state -> | ||
when (val pictureUri = state.personalizationState.selectedPictureUri) { | ||
null -> _viewEvents.post(OnboardingViewEvents.Failure(NullPointerException("picture uri is missing from state"))) | ||
else -> { | ||
setState { copy(asyncProfilePicture = Loading()) } | ||
viewModelScope.launch { | ||
val activeSession = activeSessionHolder.getActiveSession() | ||
try { | ||
activeSession.updateAvatar( | ||
activeSession.myUserId, | ||
pictureUri, | ||
uriFilenameResolver.getFilenameFromUri(pictureUri) ?: UUID.randomUUID().toString() | ||
) | ||
setState { | ||
copy( | ||
asyncProfilePicture = Success(Unit), | ||
) | ||
} | ||
onProfilePictureSaved() | ||
} catch (error: Throwable) { | ||
setState { copy(asyncProfilePicture = Fail(error)) } | ||
_viewEvents.post(OnboardingViewEvents.Failure(error)) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
private fun onProfilePictureSaved() { | ||
_viewEvents.post(OnboardingViewEvents.OnPersonalizationComplete) | ||
} | ||
} | ||
|
||
private fun LoginMode.supportsSignModeScreen(): Boolean { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
* Copyright (c) 2022 New Vector Ltd | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package im.vector.app.features.onboarding | ||
|
||
import android.content.Context | ||
import android.net.Uri | ||
import android.provider.OpenableColumns | ||
import im.vector.lib.multipicker.utils.readStringColumnOrNull | ||
import javax.inject.Inject | ||
|
||
class UriFilenameResolver @Inject constructor(private val context: Context) { | ||
|
||
fun getFilenameFromUri(uri: Uri): String? { | ||
val fallback = uri.path?.substringAfterLast('/') | ||
return when (uri.scheme) { | ||
"content" -> readResolvedDisplayName(uri) ?: fallback | ||
else -> fallback | ||
} | ||
} | ||
|
||
private fun readResolvedDisplayName(uri: Uri): String? { | ||
return context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> | ||
cursor.takeIf { cursor.moveToFirst() } | ||
?.readStringColumnOrNull(OpenableColumns.DISPLAY_NAME) | ||
} | ||
} | ||
} |
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.
Those 2 values are the same than in default resource, is it a mistake?
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.
ah great catch 👀 ! they can be deleted, they were separated whilst checking with design about the different screen sizes, we chose to maintain the same % of screen for small screens and allow the entire avatar to be clickable instead of making the image bigger
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.
1fc1935