浏览代码

Added separate presenter and view for books page

Vadik Sirekanyan 7 年之前
父节点
当前提交
81761349b9

+ 16 - 11
app/src/main/java/com/sirekanyan/knigopis/dependency.kt

@@ -13,6 +13,8 @@ import com.sirekanyan.knigopis.feature.MainPresenter
 import com.sirekanyan.knigopis.feature.MainPresenterImpl
 import com.sirekanyan.knigopis.feature.MainViewImpl
 import com.sirekanyan.knigopis.feature.ProgressViewImpl
+import com.sirekanyan.knigopis.feature.books.BooksPresenterImpl
+import com.sirekanyan.knigopis.feature.books.BooksViewImpl
 import com.sirekanyan.knigopis.feature.login.LoginPresenterImpl
 import com.sirekanyan.knigopis.feature.login.LoginViewImpl
 import com.sirekanyan.knigopis.feature.notes.NotesPresenterImpl
@@ -30,6 +32,10 @@ import com.sirekanyan.knigopis.repository.*
 import com.sirekanyan.knigopis.repository.cache.CommonCache
 import com.sirekanyan.knigopis.repository.cache.CommonCacheImpl
 import com.sirekanyan.knigopis.repository.cache.HeadedModelDeserializer
+import kotlinx.android.synthetic.main.activity_main.view.*
+import kotlinx.android.synthetic.main.books_page.view.*
+import kotlinx.android.synthetic.main.notes_page.view.*
+import kotlinx.android.synthetic.main.users_page.view.*
 import okhttp3.OkHttpClient
 import okhttp3.logging.HttpLoggingInterceptor
 import org.koin.core.parameter.Parameters
@@ -87,25 +93,27 @@ val appModule = applicationContext {
 private fun KoinContext.mainModule() {
     factory {
         val params = it.getContext().createParameters()
-        val progressView = ProgressViewImpl(it.getRootView(R.id.swipeRefresh))
         val loginPresenter = LoginPresenterImpl(it.getRouter(), get(params))
+        val booksPresenter = BooksPresenterImpl(get())
         val usersPresenter = UsersPresenterImpl(get(), get())
         val notesPresenter = NotesPresenterImpl(get())
         MainPresenterImpl(
             loginPresenter,
+            booksPresenter,
             usersPresenter,
             notesPresenter,
             it.getRouter(),
             get(),
-            get(),
-            get(),
-            progressView // TODO: remove
+            get()
         ).also { p ->
+            val rootView = it.getRootView()
+            val progressView = ProgressViewImpl(rootView.swipeRefresh, p)
             val dialogs: DialogFactory = get(params)
-            loginPresenter.view = LoginViewImpl(it.getRootView(), loginPresenter)
-            usersPresenter.view = UsersViewImpl(it.getRootView(R.id.usersPage), p, progressView, dialogs)
-            notesPresenter.view = NotesViewImpl(it.getRootView(R.id.notesPage), p, progressView)
-            p.view = MainViewImpl(it.getRootView(), p, dialogs)
+            loginPresenter.view = LoginViewImpl(rootView, loginPresenter)
+            booksPresenter.view = BooksViewImpl(rootView.booksPage, p, progressView, dialogs)
+            usersPresenter.view = UsersViewImpl(rootView.usersPage, p, progressView, dialogs)
+            notesPresenter.view = NotesViewImpl(rootView.notesPage, p, progressView)
+            p.view = MainViewImpl(rootView, p)
         } as MainPresenter
     }
 }
@@ -132,9 +140,6 @@ private fun ParameterProvider.getContext(): Context =
 private fun ParameterProvider.getRootView(): View =
     this[ROOT_VIEW_KEY]
 
-private fun ParameterProvider.getRootView(id: Int): View =
-    getRootView().findViewById(id)
-
 private fun <T> ParameterProvider.getRouter(): T =
     this[ROUTER_KEY]
 

+ 13 - 31
app/src/main/java/com/sirekanyan/knigopis/feature/MainPresenter.kt

@@ -3,9 +3,9 @@ package com.sirekanyan.knigopis.feature
 import android.net.Uri
 import com.sirekanyan.knigopis.common.BasePresenter
 import com.sirekanyan.knigopis.common.Presenter
-import com.sirekanyan.knigopis.common.extensions.io2main
-import com.sirekanyan.knigopis.common.extensions.showProgressBar
 import com.sirekanyan.knigopis.common.functions.logError
+import com.sirekanyan.knigopis.feature.books.BooksPresenter
+import com.sirekanyan.knigopis.feature.books.BooksView
 import com.sirekanyan.knigopis.feature.login.LoginPresenter
 import com.sirekanyan.knigopis.feature.notes.NotesPresenter
 import com.sirekanyan.knigopis.feature.notes.NotesView
@@ -15,7 +15,6 @@ import com.sirekanyan.knigopis.feature.users.UsersView
 import com.sirekanyan.knigopis.model.*
 import com.sirekanyan.knigopis.model.CurrentTab.*
 import com.sirekanyan.knigopis.repository.AuthRepository
-import com.sirekanyan.knigopis.repository.BookRepository
 import com.sirekanyan.knigopis.repository.Configuration
 
 interface MainPresenter : Presenter {
@@ -36,22 +35,24 @@ interface MainPresenter : Presenter {
         fun openWebPage(uri: Uri)
         fun reopenScreen()
     }
+
 }
 
 class MainPresenterImpl(
     private val loginPresenter: LoginPresenter,
+    private val booksPresenter: BooksPresenter,
     private val usersPresenter: UsersPresenter,
     private val notesPresenter: NotesPresenter,
     private val router: MainPresenter.Router,
     private val config: Configuration,
-    private val auth: AuthRepository,
-    private val bookRepository: BookRepository,
-    private val progressView: ProgressView
-) : BasePresenter<MainView>(loginPresenter, notesPresenter),
+    private val auth: AuthRepository
+) : BasePresenter<MainView>(loginPresenter, booksPresenter, usersPresenter, notesPresenter),
     MainPresenter,
     MainView.Callbacks,
+    BooksView.Callbacks,
     UsersView.Callbacks,
-    NotesView.Callbacks {
+    NotesView.Callbacks,
+    ProgressView.Callbacks {
 
     private val loadedTabs = mutableSetOf<CurrentTab>()
     private var currentTab: CurrentTab? = null
@@ -121,7 +122,7 @@ class MainPresenterImpl(
         val isFirst = !loadedTabs.contains(tab)
         if (isFirst || isForce) {
             when (tab) {
-                HOME_TAB -> refreshHomeTab()
+                HOME_TAB -> booksPresenter.refresh()
                 USERS_TAB -> usersPresenter.refresh()
                 NOTES_TAB -> notesPresenter.refresh()
             }
@@ -181,7 +182,7 @@ class MainPresenterImpl(
     }
 
     override fun onBookLongClicked(book: BookDataModel) {
-        view.showBookActions(book)
+        booksPresenter.showBookActions(book)
     }
 
     override fun onEditBookClicked(book: BookDataModel) {
@@ -189,18 +190,11 @@ class MainPresenterImpl(
     }
 
     override fun onDeleteBookClicked(book: BookDataModel) {
-        view.showBookDeleteDialog(book)
+        booksPresenter.showBookDeleteDialog(book)
     }
 
     override fun onDeleteBookConfirmed(book: BookDataModel) {
-        bookRepository.deleteBook(book)
-            .io2main()
-            .bind({
-                refresh(isForce = true)
-            }, {
-                view.showBookDeleteError()
-                logError("cannot delete finished book", it)
-            })
+        booksPresenter.deleteBook(book)
     }
 
     override fun onUserClicked(user: UserModel) {
@@ -231,16 +225,4 @@ class MainPresenterImpl(
         loadedTabs.add(NOTES_TAB)
     }
 
-    private fun refreshHomeTab() {
-        bookRepository.observeBooks()
-            .io2main()
-            .showProgressBar(progressView)
-            .bind({ books ->
-                view.updateBooks(books)
-            }, {
-                logError("cannot load books", it)
-                view.showBooksError(it)
-            })
-    }
-
 }

+ 3 - 75
app/src/main/java/com/sirekanyan/knigopis/feature/MainView.kt

@@ -2,24 +2,12 @@ package com.sirekanyan.knigopis.feature
 
 import android.content.Context.MODE_PRIVATE
 import android.support.v7.app.AlertDialog
-import android.support.v7.widget.RecyclerView
 import android.view.MenuItem
 import android.view.View
 import com.sirekanyan.knigopis.BuildConfig
 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.extensions.getFullTitleString
 import com.sirekanyan.knigopis.common.extensions.hide
 import com.sirekanyan.knigopis.common.extensions.show
-import com.sirekanyan.knigopis.common.extensions.toast
-import com.sirekanyan.knigopis.common.functions.handleError
-import com.sirekanyan.knigopis.feature.books.BooksAdapter
-import com.sirekanyan.knigopis.feature.books.BooksView
-import com.sirekanyan.knigopis.model.BookDataModel
-import com.sirekanyan.knigopis.model.BookModel
 import com.sirekanyan.knigopis.model.CurrentTab
 import com.sirekanyan.knigopis.model.CurrentTab.*
 import com.sirekanyan.knigopis.repository.cache.COMMON_PREFS_NAME
@@ -30,7 +18,7 @@ import kotlinx.android.synthetic.main.books_page.*
 import kotlinx.android.synthetic.main.notes_page.*
 import kotlinx.android.synthetic.main.users_page.*
 
-interface MainView : BooksView {
+interface MainView {
 
     fun showAboutDialog()
     fun showPage(tab: CurrentTab)
@@ -40,28 +28,23 @@ interface MainView : BooksView {
     fun showProfileOption(isVisible: Boolean)
     fun setDarkThemeOptionChecked(isChecked: Boolean)
 
-    interface Callbacks : BooksView.Callbacks {
+    interface Callbacks {
         fun onNavigationClicked(itemId: Int)
         fun onToolbarClicked()
         fun onLoginOptionClicked()
         fun onProfileOptionClicked()
         fun onAboutOptionClicked()
         fun onDarkThemeOptionClicked(isChecked: Boolean)
-        fun onAddBookClicked()
-        fun onRefreshSwiped()
     }
 
 }
 
 class MainViewImpl(
     override val containerView: View,
-    private val callbacks: MainView.Callbacks,
-    private val dialogs: DialogFactory
+    private val callbacks: MainView.Callbacks
 ) : MainView, LayoutContainer {
 
     private val context = containerView.context
-    private val resources = context.resources
-    private val booksAdapter = BooksAdapter(callbacks::onBookClicked, callbacks::onBookLongClicked)
     private val loginOption: MenuItem
     private val profileOption: MenuItem
     private val darkThemeOption: MenuItem
@@ -103,20 +86,6 @@ class MainViewImpl(
         profileOption = toolbar.menu.findItem(R.id.option_profile)
         darkThemeOption = toolbar.menu.findItem(R.id.option_dark_theme)
         toolbar.menu.findItem(R.id.option_clear_cache).isVisible = BuildConfig.DEBUG
-        booksRecyclerView.adapter = booksAdapter
-        booksRecyclerView.addItemDecoration(HeaderItemDecoration(StickyHeaderImpl(booksAdapter)))
-        addBookButton.setOnClickListener {
-            callbacks.onAddBookClicked()
-        }
-        booksRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
-            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
-                when {
-                    dy > 0 -> addBookButton.hide()
-                    dy < 0 -> addBookButton.show()
-                }
-            }
-        })
-        swipeRefresh.setOnRefreshListener(callbacks::onRefreshSwiped)
     }
 
     override fun showAboutDialog() {
@@ -131,17 +100,6 @@ class MainViewImpl(
         notesPage.show(tab == NOTES_TAB)
     }
 
-    override fun updateBooks(books: List<BookModel>) {
-        booksPlaceholder.show(books.isEmpty())
-        booksErrorPlaceholder.hide()
-        booksAdapter.submitList(books)
-        callbacks.onBooksUpdated()
-    }
-
-    override fun showBooksError(throwable: Throwable) {
-        handleError(throwable, booksPlaceholder, booksErrorPlaceholder, booksAdapter)
-    }
-
     override fun showNavigation(isVisible: Boolean) {
         if (isVisible) {
             bottomNavigation.show()
@@ -171,34 +129,4 @@ class MainViewImpl(
         darkThemeOption.isChecked = isChecked
     }
 
-    override fun showBookActions(book: BookDataModel) {
-        val bookFullTitle = resources.getFullTitleString(book.title, book.author)
-        dialogs.showDialog(
-            bookFullTitle,
-            createDialogItem(R.string.books_button_edit, R.drawable.ic_edit) {
-                callbacks.onEditBookClicked(book)
-            },
-            createDialogItem(R.string.books_button_delete, R.drawable.ic_delete) {
-                callbacks.onDeleteBookClicked(book)
-            }
-        )
-    }
-
-    override fun showBookDeleteDialog(book: BookDataModel) {
-        val bookFullTitle = resources.getFullTitleString(book.title, book.author)
-        AlertDialog.Builder(context)
-            .setTitle(R.string.books_title_confirm_delete)
-            .setMessage(context.getString(R.string.books_message_confirm_delete, bookFullTitle))
-            .setNegativeButton(R.string.common_button_cancel) { d, _ -> d.dismiss() }
-            .setPositiveButton(R.string.books_button_confirm_delete) { d, _ ->
-                callbacks.onDeleteBookConfirmed(book)
-                d.dismiss()
-            }
-            .show()
-    }
-
-    override fun showBookDeleteError() {
-        context.toast(R.string.books_error_delete)
-    }
-
 }

+ 12 - 1
app/src/main/java/com/sirekanyan/knigopis/feature/ProgressView.kt

@@ -7,16 +7,27 @@ import kotlinx.android.extensions.LayoutContainer
 import kotlinx.android.synthetic.main.activity_main.*
 
 interface ProgressView {
+
     fun showProgress()
     fun hideProgress()
     fun hideSwipeRefresh()
+
+    interface Callbacks {
+        fun onRefreshSwiped()
+    }
+
 }
 
 class ProgressViewImpl(
-    override val containerView: View
+    override val containerView: View,
+    callbacks: ProgressView.Callbacks
 ) : ProgressView,
     LayoutContainer {
 
+    init {
+        swipeRefresh.setOnRefreshListener(callbacks::onRefreshSwiped)
+    }
+
     override fun showProgress() {
         if (!swipeRefresh.isRefreshing) {
             booksProgressBar.show()

+ 54 - 0
app/src/main/java/com/sirekanyan/knigopis/feature/books/BooksPresenter.kt

@@ -0,0 +1,54 @@
+package com.sirekanyan.knigopis.feature.books
+
+import com.sirekanyan.knigopis.common.BasePresenter
+import com.sirekanyan.knigopis.common.Presenter
+import com.sirekanyan.knigopis.common.extensions.io2main
+import com.sirekanyan.knigopis.common.extensions.showProgressBar
+import com.sirekanyan.knigopis.common.functions.logError
+import com.sirekanyan.knigopis.model.BookDataModel
+import com.sirekanyan.knigopis.repository.BookRepository
+
+interface BooksPresenter : Presenter {
+    fun refresh()
+    fun deleteBook(book: BookDataModel)
+    fun showBookActions(book: BookDataModel)
+    fun showBookDeleteDialog(book: BookDataModel)
+}
+
+class BooksPresenterImpl(
+    private val bookRepository: BookRepository
+) : BasePresenter<BooksView>(),
+    BooksPresenter {
+
+    override fun refresh() {
+        bookRepository.observeBooks()
+            .io2main()
+            .showProgressBar(view)
+            .bind({ books ->
+                view.updateBooks(books)
+            }, {
+                logError("cannot load books", it)
+                view.showBooksError(it)
+            })
+    }
+
+    override fun deleteBook(book: BookDataModel) {
+        bookRepository.deleteBook(book)
+            .io2main()
+            .bind({
+                refresh()
+            }, {
+                view.showBookDeleteError()
+                logError("cannot delete finished book", it)
+            })
+    }
+
+    override fun showBookActions(book: BookDataModel) {
+        view.showBookActions(book)
+    }
+
+    override fun showBookDeleteDialog(book: BookDataModel) {
+        view.showBookDeleteDialog(book)
+    }
+
+}

+ 88 - 1
app/src/main/java/com/sirekanyan/knigopis/feature/books/BooksView.kt

@@ -1,9 +1,25 @@
 package com.sirekanyan.knigopis.feature.books
 
+import android.support.v7.app.AlertDialog
+import android.support.v7.widget.RecyclerView
+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.extensions.getFullTitleString
+import com.sirekanyan.knigopis.common.extensions.hide
+import com.sirekanyan.knigopis.common.extensions.show
+import com.sirekanyan.knigopis.common.extensions.toast
+import com.sirekanyan.knigopis.common.functions.handleError
+import com.sirekanyan.knigopis.feature.ProgressView
 import com.sirekanyan.knigopis.model.BookDataModel
 import com.sirekanyan.knigopis.model.BookModel
+import kotlinx.android.extensions.LayoutContainer
+import kotlinx.android.synthetic.main.books_page.*
 
-interface BooksView {
+interface BooksView : ProgressView {
 
     fun updateBooks(books: List<BookModel>)
     fun showBooksError(throwable: Throwable)
@@ -12,6 +28,7 @@ interface BooksView {
     fun showBookDeleteError()
 
     interface Callbacks {
+        fun onAddBookClicked()
         fun onEditBookClicked(book: BookDataModel)
         fun onDeleteBookClicked(book: BookDataModel)
         fun onDeleteBookConfirmed(book: BookDataModel)
@@ -20,4 +37,74 @@ interface BooksView {
         fun onBooksUpdated()
     }
 
+}
+
+class BooksViewImpl(
+    override val containerView: View,
+    private val callbacks: BooksView.Callbacks,
+    progressView: ProgressView,
+    private val dialogs: DialogFactory
+) : BooksView, LayoutContainer, ProgressView by progressView {
+
+    private val context = containerView.context
+    private val resources = context.resources
+    private val booksAdapter = BooksAdapter(callbacks::onBookClicked, callbacks::onBookLongClicked)
+
+    init {
+        booksRecyclerView.adapter = booksAdapter
+        booksRecyclerView.addItemDecoration(HeaderItemDecoration(StickyHeaderImpl(booksAdapter)))
+        addBookButton.setOnClickListener {
+            callbacks.onAddBookClicked()
+        }
+        booksRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+                when {
+                    dy > 0 -> addBookButton.hide()
+                    dy < 0 -> addBookButton.show()
+                }
+            }
+        })
+    }
+
+    override fun updateBooks(books: List<BookModel>) {
+        booksPlaceholder.show(books.isEmpty())
+        booksErrorPlaceholder.hide()
+        booksAdapter.submitList(books)
+        callbacks.onBooksUpdated()
+    }
+
+    override fun showBooksError(throwable: Throwable) {
+        handleError(throwable, booksPlaceholder, booksErrorPlaceholder, booksAdapter)
+    }
+
+    override fun showBookActions(book: BookDataModel) {
+        val bookFullTitle = resources.getFullTitleString(book.title, book.author)
+        dialogs.showDialog(
+            bookFullTitle,
+            createDialogItem(R.string.books_button_edit, R.drawable.ic_edit) {
+                callbacks.onEditBookClicked(book)
+            },
+            createDialogItem(R.string.books_button_delete, R.drawable.ic_delete) {
+                callbacks.onDeleteBookClicked(book)
+            }
+        )
+    }
+
+    override fun showBookDeleteDialog(book: BookDataModel) {
+        val bookFullTitle = resources.getFullTitleString(book.title, book.author)
+        AlertDialog.Builder(context)
+            .setTitle(R.string.books_title_confirm_delete)
+            .setMessage(context.getString(R.string.books_message_confirm_delete, bookFullTitle))
+            .setNegativeButton(R.string.common_button_cancel) { d, _ -> d.dismiss() }
+            .setPositiveButton(R.string.books_button_confirm_delete) { d, _ ->
+                callbacks.onDeleteBookConfirmed(book)
+                d.dismiss()
+            }
+            .show()
+    }
+
+    override fun showBookDeleteError() {
+        context.toast(R.string.books_error_delete)
+    }
+
 }

+ 2 - 1
app/src/main/java/com/sirekanyan/knigopis/feature/users/UsersPresenter.kt

@@ -1,6 +1,7 @@
 package com.sirekanyan.knigopis.feature.users
 
 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.io2main
 import com.sirekanyan.knigopis.common.extensions.showProgressBar
@@ -10,7 +11,7 @@ import com.sirekanyan.knigopis.model.ProfileItem
 import com.sirekanyan.knigopis.model.UserModel
 import com.sirekanyan.knigopis.repository.UserRepository
 
-interface UsersPresenter {
+interface UsersPresenter : Presenter {
     fun refresh()
     fun showUserProfiles(user: UserModel)
 }