Переглянути джерело

Moved user feature to mvp

Vadik Sirekanyan 7 роки тому
батько
коміт
fda2e01269

+ 7 - 5
app/src/main/java/com/sirekanyan/knigopis/dependency/user.kt

@@ -1,9 +1,11 @@
 package com.sirekanyan.knigopis.dependency
 
 import com.sirekanyan.knigopis.common.extensions.app
-import com.sirekanyan.knigopis.feature.user.UserActivity
-import com.sirekanyan.knigopis.feature.user.UserInteractor
-import com.sirekanyan.knigopis.feature.user.UserInteractorImpl
+import com.sirekanyan.knigopis.common.extensions.getRootView
+import com.sirekanyan.knigopis.feature.user.*
 
-fun UserActivity.provideInteractor(): UserInteractor =
-    UserInteractorImpl(app.authRepository, app.endpoint, app.resourceProvider)
+fun UserActivity.providePresenter(id: String, name: String, image: String?): UserPresenter {
+    val interactor = UserInteractorImpl(app.authRepository, app.endpoint, app.resourceProvider)
+    return UserPresenterImpl(this, interactor, id, name, image, app.resourceProvider)
+        .also { it.view = UserViewImpl(getRootView(), it, provideDialogs()) }
+}

+ 24 - 116
app/src/main/java/com/sirekanyan/knigopis/feature/user/UserActivity.kt

@@ -4,151 +4,59 @@ import android.content.ClipData
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
-import android.support.v7.widget.LinearLayoutManager
-import android.view.Menu
-import android.view.MenuItem
 import com.sirekanyan.knigopis.R
 import com.sirekanyan.knigopis.common.BaseActivity
-import com.sirekanyan.knigopis.common.android.dialog.createDialogItem
-import com.sirekanyan.knigopis.common.android.header.HeaderItemDecoration
-import com.sirekanyan.knigopis.common.android.header.StickyHeaderImpl
-import com.sirekanyan.knigopis.common.extensions.*
+import com.sirekanyan.knigopis.common.extensions.app
+import com.sirekanyan.knigopis.common.extensions.setDarkTheme
+import com.sirekanyan.knigopis.common.extensions.systemClipboardManager
 import com.sirekanyan.knigopis.common.functions.extra
-import com.sirekanyan.knigopis.common.functions.logError
-import com.sirekanyan.knigopis.dependency.provideDialogs
-import com.sirekanyan.knigopis.dependency.provideInteractor
+import com.sirekanyan.knigopis.dependency.providePresenter
 import com.sirekanyan.knigopis.feature.book.createBookIntent
-import com.sirekanyan.knigopis.model.BookDataModel
-import com.sirekanyan.knigopis.model.createDoneBook
-import com.sirekanyan.knigopis.model.createTodoBook
-import kotlinx.android.synthetic.main.user_activity.*
+import com.sirekanyan.knigopis.model.EditBookModel
 
 private val EXTRA_USER_ID = extra("user_id")
 private val EXTRA_USER_NAME = extra("user_name")
-private val EXTRA_USER_PHOTO = extra("user_photo")
+private val EXTRA_USER_IMAGE = extra("user_image")
 
 fun Context.createUserIntent(id: String, name: String, avatar: String?): Intent =
     Intent(this, UserActivity::class.java)
         .putExtra(EXTRA_USER_ID, id)
         .putExtra(EXTRA_USER_NAME, name)
-        .putExtra(EXTRA_USER_PHOTO, avatar)
+        .putExtra(EXTRA_USER_IMAGE, avatar)
 
-class UserActivity : BaseActivity() {
+class UserActivity : BaseActivity(), UserPresenter.Router {
 
-    private val interactor by lazy { provideInteractor() }
-    private val dialogs by lazy { provideDialogs() }
-    private val userId by lazy { intent.getStringExtra(EXTRA_USER_ID) }
-    private val userName by lazy { intent.getStringExtra(EXTRA_USER_NAME) }
-    private val userPhoto by lazy { intent.getStringExtra(EXTRA_USER_PHOTO) }
-    private val booksAdapter = UserBooksAdapter(::onBookLongClicked)
-    private lateinit var unsubscribeOption: MenuItem
+    private val presenter by lazy {
+        providePresenter(
+            intent.getStringExtra(EXTRA_USER_ID),
+            intent.getStringExtra(EXTRA_USER_NAME),
+            intent.getStringExtra(EXTRA_USER_IMAGE)
+        )
+    }
 
     override fun onCreate(savedInstanceState: Bundle?) {
         setDarkTheme(app.config.isDarkTheme)
         super.onCreate(savedInstanceState)
         setContentView(R.layout.user_activity)
-        toolbar.title = userName
-        userImage.setCircleImage(userPhoto, R.drawable.oval_dark_placeholder_background)
-        userImage.setElevationRes(R.dimen.image_view_elevation)
-        setSupportActionBar(toolbar)
-        fab.setOnClickListener { view ->
-            interactor.addFriend(userId)
-                .doOnSubscribe { fab.startCollapseAnimation() }
-                .doFinally { fab.startExpandAnimation() }
-                .bind({
-                    fab.setOnClickListener(null)
-                    fab.isSelected = true
-                    fab.setImageResource(R.drawable.ic_done)
-                }, {
-                    logError("Cannot update subscription", it)
-                    view.snackbar(R.string.common_error_network)
-                    fab.isSelected = false
-                    fab.setImageResource(R.drawable.ic_person_add)
-                })
-        }
-        supportActionBar?.setDisplayHomeAsUpEnabled(true)
-        val layoutManager = LinearLayoutManager(this)
-        userBooksRecyclerView.layoutManager = layoutManager
-        userBooksRecyclerView.addItemDecoration(HeaderItemDecoration(StickyHeaderImpl(booksAdapter)))
-        userBooksRecyclerView.adapter = booksAdapter
+        presenter.init()
     }
 
     override fun onStart() {
         super.onStart()
-        interactor.getBooks(userId)
-            .doOnSubscribe {
-                userBooksProgressBar.showNow()
-                userBooksErrorPlaceholder.hideNow()
-                userBooksRecyclerView.hideNow()
-            }
-            .doFinally { userBooksProgressBar.hide() }
-            .doOnSuccess { userBooksRecyclerView.show() }
-            .doOnError { userBooksErrorPlaceholder.show() }
-            .bind({
-                booksAdapter.submitList(it)
-                onBooksLoaded()
-            }, {
-                logError("Cannot load user books", it)
-            })
+        presenter.start()
     }
 
-    override fun onCreateOptionsMenu(menu: Menu): Boolean {
-        menuInflater.inflate(R.menu.user_menu, menu)
-        unsubscribeOption = menu.findItem(R.id.option_unsubscribe)
-        return true
+    override fun onStop() {
+        super.onStop()
+        presenter.stop()
     }
 
-    override fun onOptionsItemSelected(item: MenuItem): Boolean {
-        return when (item.itemId) {
-            android.R.id.home -> {
-                onBackPressed()
-                true
-            }
-            R.id.option_copy -> {
-                val link = "http://www.knigopis.com/#/user/books?u=$userId"
-                systemClipboardManager.primaryClip = ClipData.newPlainText(null, link)
-                toast(R.string.user_info_copied, link)
-                true
-            }
-            R.id.option_unsubscribe -> {
-                interactor.removeFriend(userId)
-                    .bind({}, {
-                        logError("Cannot unsubscribe", it)
-                        toast(R.string.user_error_unsubscribe)
-                    })
-                true
-            }
-            else -> false
-        }
+    override fun openBookScreen(book: EditBookModel) {
+        startActivity(createBookIntent(book))
     }
 
-    private fun onBooksLoaded() {
-        interactor.isFriend(userId)
-            .bind({ isSubscribed ->
-                if (isSubscribed) {
-                    unsubscribeOption.isVisible = true
-                } else {
-                    fab.showNow()
-                    fab.startExpandAnimation()
-                }
-            }, {
-                logError("Cannot check subscription", it)
-            })
-    }
-
-    private fun onBookLongClicked(book: BookDataModel) {
-        dialogs.showDialog(
-            resources.getFullTitleString(book.title, book.author),
-            createDialogItem(R.string.user_button_todo, R.drawable.ic_playlist_add) {
-                val notes = getString(R.string.book_notes_copied, userName)
-                val todoBook = createTodoBook(book.title, book.author, notes)
-                startActivity(createBookIntent(todoBook))
-            },
-            createDialogItem(R.string.user_button_done, R.drawable.ic_playlist_add_check) {
-                val doneBook = createDoneBook(book.title, book.author)
-                startActivity(createBookIntent(doneBook))
-            }
-        )
+    override fun copyToClipboard(text: String) {
+        systemClipboardManager.primaryClip = ClipData.newPlainText(null, text)
     }
 
 }

+ 118 - 0
app/src/main/java/com/sirekanyan/knigopis/feature/user/UserPresenter.kt

@@ -0,0 +1,118 @@
+package com.sirekanyan.knigopis.feature.user
+
+import com.sirekanyan.knigopis.R
+import com.sirekanyan.knigopis.common.BasePresenter
+import com.sirekanyan.knigopis.common.Presenter
+import com.sirekanyan.knigopis.common.android.ResourceProvider
+import com.sirekanyan.knigopis.common.extensions.snackbar
+import com.sirekanyan.knigopis.common.extensions.toast
+import com.sirekanyan.knigopis.common.functions.logError
+import com.sirekanyan.knigopis.model.BookDataModel
+import com.sirekanyan.knigopis.model.EditBookModel
+import com.sirekanyan.knigopis.model.createDoneBook
+import com.sirekanyan.knigopis.model.createTodoBook
+
+interface UserPresenter : Presenter {
+
+    fun init()
+    fun start()
+
+    interface Router {
+        fun onBackPressed()
+        fun openBookScreen(book: EditBookModel)
+        fun copyToClipboard(text: String)
+    }
+
+}
+
+class UserPresenterImpl(
+    private val router: UserPresenter.Router,
+    private val interactor: UserInteractor,
+    private val userId: String,
+    private val userName: String,
+    private val userImage: String?,
+    private val resources: ResourceProvider
+) : BasePresenter<UserView>(),
+    UserPresenter,
+    UserView.Callbacks {
+
+    override fun init() {
+        view.setTitle(userName)
+        view.setImage(userImage)
+    }
+
+    override fun start() {
+        interactor.getBooks(userId)
+            .doOnSubscribe { view.showProgress() }
+            .doFinally { view.hideProgress() }
+            .doOnSuccess { view.showBooks() }
+            .doOnError { view.showError() }
+            .bind({
+                view.setBooks(it)
+                onBooksLoaded()
+            }, {
+                logError("Cannot load user books", it)
+            })
+    }
+
+    override fun onNavigationBackClicked() {
+        router.onBackPressed()
+    }
+
+    override fun onCopyOptionClicked() {
+        val link = "http://www.knigopis.com/#/user/books?u=$userId"
+        router.copyToClipboard(link)
+        view.toast(R.string.user_info_copied, link)
+    }
+
+    override fun onUnsubscribeOptionClicked() {
+        interactor.removeFriend(userId)
+            .bind({}, {
+                logError("Cannot unsubscribe", it)
+                view.toast(R.string.user_error_unsubscribe)
+            })
+    }
+
+    override fun onFabClicked() {
+        interactor.addFriend(userId)
+            .doOnSubscribe { view.showFab(false) }
+            .doFinally { view.showFab(true) }
+            .bind({
+                view.disableFabClick()
+                view.setFabSelected(true)
+            }, {
+                logError("Cannot update subscription", it)
+                view.snackbar(R.string.common_error_network)
+                view.setFabSelected(false)
+            })
+    }
+
+    override fun onBookLongClicked(book: BookDataModel) {
+        view.showActionsDialog(book.title, book.author, book)
+    }
+
+    override fun onTodoActionClicked(book: BookDataModel) {
+        val notes = resources.getString(R.string.book_notes_copied, userName)
+        val todoBook = createTodoBook(book.title, book.author, notes)
+        router.openBookScreen(todoBook)
+    }
+
+    override fun onDoneActionClicked(book: BookDataModel) {
+        val doneBook = createDoneBook(book.title, book.author)
+        router.openBookScreen(doneBook)
+    }
+
+    private fun onBooksLoaded() {
+        interactor.isFriend(userId)
+            .bind({ isSubscribed ->
+                if (isSubscribed) {
+                    view.showUnsubscribeOption()
+                } else {
+                    view.showFab(true)
+                }
+            }, {
+                logError("Cannot check subscription", it)
+            })
+    }
+
+}

+ 156 - 0
app/src/main/java/com/sirekanyan/knigopis/feature/user/UserView.kt

@@ -0,0 +1,156 @@
+package com.sirekanyan.knigopis.feature.user
+
+import android.support.v7.widget.LinearLayoutManager
+import android.view.MenuItem
+import android.view.View
+import com.sirekanyan.knigopis.R
+import com.sirekanyan.knigopis.common.android.dialog.DialogFactory
+import com.sirekanyan.knigopis.common.android.dialog.createDialogItem
+import com.sirekanyan.knigopis.common.android.header.HeaderItemDecoration
+import com.sirekanyan.knigopis.common.android.header.StickyHeaderImpl
+import com.sirekanyan.knigopis.common.android.toast.CommonView
+import com.sirekanyan.knigopis.common.extensions.*
+import com.sirekanyan.knigopis.model.BookDataModel
+import com.sirekanyan.knigopis.model.BookModel
+import kotlinx.android.extensions.LayoutContainer
+import kotlinx.android.synthetic.main.user_activity.*
+
+interface UserView : CommonView {
+
+    fun setTitle(title: String)
+    fun setImage(url: String?)
+    fun setBooks(books: List<BookModel>)
+    fun showUnsubscribeOption()
+    fun showProgress()
+    fun hideProgress()
+    fun showBooks()
+    fun showError()
+    fun showFab(isVisible: Boolean)
+    fun disableFabClick()
+    fun setFabSelected(isSelected: Boolean)
+    fun showActionsDialog(title: String, author: String, book: BookDataModel)
+
+    interface Callbacks {
+        fun onNavigationBackClicked()
+        fun onCopyOptionClicked()
+        fun onUnsubscribeOptionClicked()
+        fun onFabClicked()
+        fun onBookLongClicked(book: BookDataModel)
+        fun onTodoActionClicked(book: BookDataModel)
+        fun onDoneActionClicked(book: BookDataModel)
+    }
+
+}
+
+class UserViewImpl(
+    override val containerView: View,
+    private val callbacks: UserView.Callbacks,
+    private val dialogs: DialogFactory
+) : UserView,
+    LayoutContainer {
+
+    private val context = containerView.context
+    private val unsubscribeOption: MenuItem
+    private val booksAdapter = UserBooksAdapter(callbacks::onBookLongClicked)
+
+    init {
+        initToolbar(callbacks)
+        unsubscribeOption = toolbar.menu.findItem(R.id.option_unsubscribe)
+        fab.setOnClickListener { callbacks.onFabClicked() }
+        val layoutManager = LinearLayoutManager(context)
+        userBooksRecyclerView.layoutManager = layoutManager
+        userBooksRecyclerView.addItemDecoration(HeaderItemDecoration(StickyHeaderImpl(booksAdapter)))
+        userBooksRecyclerView.adapter = booksAdapter
+    }
+
+    override fun setTitle(title: String) {
+        toolbar.title = title
+    }
+
+    override fun setImage(url: String?) {
+        userImage.setCircleImage(url, R.drawable.oval_dark_placeholder_background)
+        userImage.setElevationRes(R.dimen.image_view_elevation)
+    }
+
+    override fun setBooks(books: List<BookModel>) {
+        booksAdapter.submitList(books)
+    }
+
+    override fun showUnsubscribeOption() {
+        unsubscribeOption.isVisible = true
+    }
+
+    override fun showProgress() {
+        userBooksProgressBar.showNow()
+        userBooksRecyclerView.hideNow()
+        userBooksErrorPlaceholder.hideNow()
+    }
+
+    override fun hideProgress() {
+        userBooksProgressBar.hide()
+    }
+
+    override fun showBooks() {
+        userBooksRecyclerView.show()
+    }
+
+    override fun showError() {
+        userBooksErrorPlaceholder.show()
+    }
+
+    override fun showFab(isVisible: Boolean) {
+        if (isVisible) {
+            fab.showNow()
+            fab.startExpandAnimation()
+        } else {
+            fab.startCollapseAnimation()
+        }
+    }
+
+    override fun disableFabClick() {
+        fab.setOnClickListener(null)
+    }
+
+    override fun setFabSelected(isSelected: Boolean) {
+        fab.isSelected = isSelected
+        fab.setImageResource(
+            if (isSelected) {
+                R.drawable.ic_done
+            } else {
+                R.drawable.ic_person_add
+            }
+        )
+    }
+
+    override fun showActionsDialog(title: String, author: String, book: BookDataModel) {
+        dialogs.showDialog(
+            context.resources.getFullTitleString(book.title, book.author),
+            createDialogItem(R.string.user_button_todo, R.drawable.ic_playlist_add) {
+                callbacks.onTodoActionClicked(book)
+            },
+            createDialogItem(R.string.user_button_done, R.drawable.ic_playlist_add_check) {
+                callbacks.onDoneActionClicked(book)
+            }
+        )
+    }
+
+    private fun initToolbar(callbacks: UserView.Callbacks) {
+        toolbar.inflateMenu(R.menu.user_menu)
+        toolbar.setNavigationIcon(R.drawable.ic_arrow_back)
+        toolbar.setNavigationOnClickListener { callbacks.onNavigationBackClicked() }
+        toolbar.setOnMenuItemClickListener { item ->
+            when (item.itemId) {
+                R.id.option_copy -> {
+                    callbacks.onNavigationBackClicked()
+                    true
+                }
+                R.id.option_unsubscribe -> {
+                    callbacks.onUnsubscribeOptionClicked()
+                    true
+                }
+                else -> false
+            }
+        }
+    }
+
+}