Skip to content

Commit

Permalink
[Enhancement] - User can see Edit Profile Screen (#338)
Browse files Browse the repository at this point in the history
* added cells for edit profile and change password

* made rows for account in settings and added account activity layout with rows

* added toolbar layout, account layout, AccountActivity, Updated SettingsNewViewModel

* added edit text and line restrictions

* re-added tests that were deleted accidentally

* fixed lines and spacing issues

* fixed imports

* fixed more imports

* moved the tests into the right package

* setup edit profile cell and added the activity

* added toolbar and imageview to edit profile

* still fixing layout

* updated strings, completed edit profile ui

* removed duplicate activity in manifest and sample layout

* fixed circle issues

* added icon for different screen mods

* fixed error

* fixed more errors

* fixed the last error
  • Loading branch information
Rashad Cureton committed Sep 18, 2018
1 parent 379b532 commit 7dde6ee
Show file tree
Hide file tree
Showing 20 changed files with 598 additions and 13 deletions.
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
</activity>
<activity android:name=".ui.activities.ChangeEmailActivity" />
<activity android:name=".ui.activities.ChangePasswordActivity"/>
<activity android:name=".ui.activities.EditProfileActivity"/>
<activity
android:name=".ui.activities.CheckoutActivity"
android:parentActivityName=".ui.activities.ProjectActivity"
Expand Down Expand Up @@ -418,4 +419,4 @@

</application>

</manifest>
</manifest>
26 changes: 23 additions & 3 deletions app/src/main/assets/json/server-config.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.kickstarter.ui.activities

import android.os.Bundle
import android.widget.TextView
import com.kickstarter.R
import com.kickstarter.libs.BaseActivity
import com.kickstarter.libs.qualifiers.RequiresActivityViewModel
import com.kickstarter.libs.rx.transformers.Transformers
import com.kickstarter.libs.transformations.CircleTransformation
import com.kickstarter.libs.utils.BooleanUtils
import com.kickstarter.libs.utils.SwitchCompatUtils
import com.kickstarter.libs.utils.ViewUtils
import com.kickstarter.models.User
import com.kickstarter.viewmodels.EditProfileViewModel
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_edit_profile.*
import rx.android.schedulers.AndroidSchedulers

@RequiresActivityViewModel(EditProfileViewModel.ViewModel::class)
class EditProfileActivity : BaseActivity<EditProfileViewModel.ViewModel>() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_profile)

this.viewModel.outputs.avatarImageViewUrl()
.compose(bindToLifecycle())
.compose(Transformers.observeForUI())
.subscribe { url ->
Picasso.with(this).load(url).transform(CircleTransformation()).into(avatar_image_view)
}

this.viewModel.outputs.user()
.compose(bindToLifecycle())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ this.displayPreferences(it) })

this.viewModel.outputs.userNameTextViewText()
.compose(bindToLifecycle())
.compose(Transformers.observeForUI())
.subscribe({ name_edit_text.setText(it, TextView.BufferType.EDITABLE) })

this.viewModel.outputs.hidePrivateProfileRow()
.compose(bindToLifecycle())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
ViewUtils.setGone(private_profile_row, it)
ViewUtils.setGone(private_profile_text_view, it)
ViewUtils.setGone(public_profile_text_view, it)
})

private_profile_switch.setOnClickListener {
this.viewModel.inputs.showPublicProfile(private_profile_switch.isChecked)
}

}

private fun displayPreferences(user: User) {
SwitchCompatUtils.setCheckedWithoutAnimation(private_profile_switch, BooleanUtils.isFalse(user.showPublicProfile()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import com.kickstarter.R
import com.kickstarter.extensions.startActivityWithSlideUpTransition
import com.kickstarter.libs.*
import com.kickstarter.libs.qualifiers.RequiresActivityViewModel
import com.kickstarter.libs.rx.transformers.Transformers
import com.kickstarter.libs.transformations.CircleTransformation
import com.kickstarter.libs.utils.ApplicationUtils
import com.kickstarter.libs.utils.TransitionUtils.slideInFromLeft
import com.kickstarter.libs.utils.ViewUtils
import com.kickstarter.viewmodels.SettingsViewModel
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.settings_layout.*
import rx.android.schedulers.AndroidSchedulers

Expand All @@ -33,6 +36,16 @@ class SettingsActivity : BaseActivity<SettingsViewModel.ViewModel>() {

version_name_text_view.text = this.build.versionName()

this.viewModel.outputs.avatarImageViewUrl()
.compose(bindToLifecycle())
.compose(Transformers.observeForUI())
.subscribe { url -> Picasso.with(this).load(url).transform(CircleTransformation()).into(profile_picture_image_view) }

this.viewModel.outputs.logout()
.compose(bindToLifecycle())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { logout() }

this.viewModel.outputs.showConfirmLogoutPrompt()
.compose(bindToLifecycle())
.observeOn(AndroidSchedulers.mainThread())
Expand All @@ -44,15 +57,19 @@ class SettingsActivity : BaseActivity<SettingsViewModel.ViewModel>() {
}
})

this.viewModel.outputs.logout()
this.viewModel.outputs.userNameTextViewText()
.compose(bindToLifecycle())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { logout() }
.compose(Transformers.observeForUI())
.subscribe({ name_text_view.text = it })

account_row.setOnClickListener {
startActivityWithSlideUpTransition(Intent(this, AccountActivity::class.java))
}

edit_profile_row.setOnClickListener {
startActivityWithSlideUpTransition(Intent(this, EditProfileActivity::class.java))
}

help_row.setOnClickListener {
startActivityWithSlideUpTransition(Intent(this, HelpSettingsActivity::class.java))
}
Expand Down
133 changes: 133 additions & 0 deletions app/src/main/java/com/kickstarter/viewmodels/EditProfileViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package com.kickstarter.viewmodels

import android.support.annotation.NonNull
import com.kickstarter.libs.ActivityViewModel
import com.kickstarter.libs.CurrentUserType
import com.kickstarter.libs.Environment
import com.kickstarter.libs.rx.transformers.Transformers
import com.kickstarter.libs.utils.IntegerUtils
import com.kickstarter.libs.utils.ListUtils
import com.kickstarter.libs.utils.ObjectUtils
import com.kickstarter.models.User
import com.kickstarter.services.ApiClientType
import com.kickstarter.ui.activities.EditProfileActivity
import rx.Observable
import rx.subjects.BehaviorSubject
import rx.subjects.PublishSubject

interface EditProfileViewModel {

interface Inputs {
/** Call when user toggles the private profile switch. */
fun showPublicProfile(checked: Boolean)
}

interface Outputs {
/** Emits the user avatar image to be displayed. */
fun avatarImageViewUrl(): Observable<String>

/** Emits when the user is a creator and we need to hide the private profile row. */
fun hidePrivateProfileRow(): Observable<Boolean>

/** Emits user containing settings state. */
fun user(): Observable<User>

/** Emits the user name to be displayed. */
fun userNameTextViewText(): Observable<String>
}

interface Errors {
/** Emits when saving preference fails. */
fun unableToSavePreferenceError(): Observable<String>
}

class ViewModel(@NonNull val environment: Environment) : ActivityViewModel<EditProfileActivity>(environment), Inputs, Outputs, Errors {

private val client: ApiClientType = environment.apiClient()
private val currentUser: CurrentUserType = environment.currentUser()

private val userInput = PublishSubject.create<User>()
private val unableToSavePreferenceError = PublishSubject.create<Throwable>()
private val updateSuccess = PublishSubject.create<Void>()

private var hidePrivateProfileRow = BehaviorSubject.create<Boolean>()
private val userOutput = BehaviorSubject.create<User>()

private val avatarImageViewUrl: Observable<String>
private val userNameTextViewText: Observable<String>

val inputs: Inputs = this
val outputs: Outputs = this

init {

this.client.fetchCurrentUser()
.retry(2)
.compose(Transformers.neverError())
.compose(bindToLifecycle())
.subscribe { this.currentUser.refresh(it) }

this.currentUser.observable()
.take(1)
.compose(bindToLifecycle())
.subscribe({ this.userOutput.onNext(it) })

this.userInput
.concatMap<User>({ this.updateSettings(it) })
.compose(bindToLifecycle())
.subscribe({ this.success(it) })

this.userInput
.compose(bindToLifecycle())
.subscribe(this.userOutput)

this.userOutput
.window(2, 1)
.flatMap<List<User>>({ it.toList() })
.map<User>({ ListUtils.first(it) })
.compose<User>(Transformers.takeWhen<User, Throwable>(this.unableToSavePreferenceError))
.compose(bindToLifecycle())
.subscribe(this.userOutput)

val currentUser = this.currentUser.observable()

currentUser
.compose(bindToLifecycle())
.filter(ObjectUtils::isNotNull)
.map { user -> IntegerUtils.isNonZero(user.createdProjectsCount()) }
.subscribe(this.hidePrivateProfileRow)

this.avatarImageViewUrl = this.currentUser.loggedInUser().map { u -> u.avatar().medium() }

this.userNameTextViewText = this.currentUser.loggedInUser().map({ it.name() })

this.koala.trackSettingsView()
}

override fun avatarImageViewUrl() = this.avatarImageViewUrl

override fun hidePrivateProfileRow(): Observable<Boolean> = this.hidePrivateProfileRow

override fun showPublicProfile(checked: Boolean) {
this.userInput.onNext(this.userOutput.value.toBuilder().showPublicProfile(!checked).build())
}

override fun unableToSavePreferenceError(): Observable<String> = this.unableToSavePreferenceError
.takeUntil(this.updateSuccess)
.map { _ -> null }

override fun user(): Observable<User> = this.userOutput

override fun userNameTextViewText() = this.userNameTextViewText

private fun success(user: User) {
this.currentUser.refresh(user)
this.updateSuccess.onNext(null)
}

private fun updateSettings(user: User): Observable<User> {
return this.client.updateUserSettings(user)
.compose(Transformers.pipeErrorsTo(this.unableToSavePreferenceError))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,17 @@ interface SettingsViewModel {
}

interface Outputs {
/** Emits the user avatar image to be displayed. */
fun avatarImageViewUrl(): Observable<String>

/** Emits when its time to log the user out. */
fun logout(): Observable<Void>

/** Emits a boolean that determines if the logout confirmation should be displayed. */
fun showConfirmLogoutPrompt(): Observable<Boolean>

/** Emits the user name to be displayed. */
fun userNameTextViewText(): Observable<String>
}

class ViewModel(@NonNull val environment: Environment) : ActivityViewModel<SettingsActivity>(environment), Inputs, Outputs {
Expand All @@ -43,6 +49,9 @@ interface SettingsViewModel {
private val showConfirmLogoutPrompt = BehaviorSubject.create<Boolean>()
private val userOutput = BehaviorSubject.create<User>()

private val avatarImageViewUrl: Observable<String>
private val userNameTextViewText: Observable<String>

val inputs: Inputs = this
val outputs: Outputs = this

Expand All @@ -61,14 +70,20 @@ interface SettingsViewModel {

this.confirmLogoutClicked
.compose(bindToLifecycle())
.subscribe{
.subscribe {
this.koala.trackLogout()
this.logout.onNext(null)
}

this.avatarImageViewUrl = this.currentUser.loggedInUser().map { u -> u.avatar().medium() }

this.userNameTextViewText = this.currentUser.loggedInUser().map({ it.name() })

this.koala.trackSettingsView()
}

override fun avatarImageViewUrl() = this.avatarImageViewUrl

override fun closeLogoutConfirmationClicked() = this.showConfirmLogoutPrompt.onNext(false)

override fun confirmLogoutClicked() = this.confirmLogoutClicked.onNext(null)
Expand All @@ -79,5 +94,7 @@ interface SettingsViewModel {

override fun showConfirmLogoutPrompt(): Observable<Boolean> = this.showConfirmLogoutPrompt

override fun userNameTextViewText() = this.userNameTextViewText

}
}
Binary file added app/src/main/res/drawable-hdpi/edit_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-mdpi/edit_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-xhdpi/edit_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-xxhdpi/edit_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-xxxhdpi/edit_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions app/src/main/res/drawable/edit_profile_linear_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:top="40dp">
<shape
android:dither="true"
android:shape="rectangle">
<solid android:color="@color/white" />
</shape>
</item>

<item android:bottom="60dp">
<shape
android:dither="true"
android:shape="rectangle">
<solid android:color="@color/ksr_grey_100" />
</shape>
</item>
</layer-list>
Loading

0 comments on commit 7dde6ee

Please sign in to comment.