Jelajahi Sumber

Moved edit book feature to mvp

Vadik Sirekanyan 7 tahun lalu
induk
melakukan
3d0ee52027

+ 12 - 0
app/src/main/java/com/sirekanyan/knigopis/dependency/book.kt

@@ -0,0 +1,12 @@
+package com.sirekanyan.knigopis.dependency
+
+import com.sirekanyan.knigopis.common.extensions.app
+import com.sirekanyan.knigopis.common.extensions.getRootView
+import com.sirekanyan.knigopis.feature.book.*
+
+fun BookActivity.providePresenter(book: EditBookModel): BookPresenter {
+    val interactor = BookInteractorImpl(app.bookRepository)
+    return BookPresenterImpl(this, interactor, book).also { presenter ->
+        presenter.view = BookViewImpl(getRootView(), presenter, book)
+    }
+}

+ 20 - 165
app/src/main/java/com/sirekanyan/knigopis/feature/book/BookActivity.kt

@@ -3,182 +3,37 @@ package com.sirekanyan.knigopis.feature.book
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
-import android.widget.SeekBar
-import android.widget.SeekBar.OnSeekBarChangeListener
-import com.sirekanyan.knigopis.MAX_BOOK_PRIORITY
-import com.sirekanyan.knigopis.MIN_BOOK_PRIORITY
 import com.sirekanyan.knigopis.R
 import com.sirekanyan.knigopis.common.BaseActivity
-import com.sirekanyan.knigopis.common.extensions.*
-import com.sirekanyan.knigopis.common.functions.createBookImageUrl
+import com.sirekanyan.knigopis.common.extensions.app
+import com.sirekanyan.knigopis.common.extensions.setDarkTheme
 import com.sirekanyan.knigopis.common.functions.extra
-import com.sirekanyan.knigopis.common.functions.logError
-import com.sirekanyan.knigopis.model.BookDataModel
-import com.sirekanyan.knigopis.model.dto.FinishedBookToSend
-import com.sirekanyan.knigopis.model.dto.PlannedBookToSend
-import kotlinx.android.synthetic.main.book_edit.*
-import java.util.*
+import com.sirekanyan.knigopis.dependency.providePresenter
 
-private val EXTRA_BOOK_ID = extra("book_id")
-private val EXTRA_BOOK_TITLE = extra("book_title")
-private val EXTRA_BOOK_AUTHOR = extra("book_author")
-private val EXTRA_BOOK_YEAR = extra("book_year")
-private val EXTRA_BOOK_MONTH = extra("book_month")
-private val EXTRA_BOOK_DAY = extra("book_day")
-private val EXTRA_BOOK_NOTES = extra("book_notes")
-private val EXTRA_BOOK_PROGRESS = extra("book_progress")
-private val EXTRA_BOOK_FINISHED = extra("book_finished")
+private val EXTRA_BOOK = extra("book")
 
-fun Context.createNewBookIntent() = Intent(this, BookActivity::class.java)
+fun Context.createBookIntent(book: EditBookModel): Intent =
+    Intent(this, BookActivity::class.java).putExtra(EXTRA_BOOK, book)
 
-fun Context.createTodoBookIntent(title: String, author: String, userName: String): Intent =
-    Intent(this, BookActivity::class.java)
-        .putExtra(EXTRA_BOOK_TITLE, title)
-        .putExtra(EXTRA_BOOK_AUTHOR, author)
-        .putExtra(EXTRA_BOOK_NOTES, getString(R.string.book_notes_copied, userName))
+class BookActivity : BaseActivity(), BookPresenter.Router {
 
-fun Context.createDoneBookIntent(title: String, author: String): Intent =
-    Intent(this, BookActivity::class.java)
-        .putExtra(EXTRA_BOOK_TITLE, title)
-        .putExtra(EXTRA_BOOK_AUTHOR, author)
-        .putExtra(EXTRA_BOOK_PROGRESS, MAX_BOOK_PRIORITY)
-
-fun Context.createEditBookIntent(book: BookDataModel): Intent =
-    Intent(this, BookActivity::class.java)
-        .putExtra(EXTRA_BOOK_ID, book.id)
-        .putExtra(EXTRA_BOOK_TITLE, book.title)
-        .putExtra(EXTRA_BOOK_AUTHOR, book.author)
-        .putExtra(EXTRA_BOOK_FINISHED, book.isFinished)
-        .putExtra(EXTRA_BOOK_NOTES, book.notes)
-        .apply {
-            if (book.isFinished) {
-                putExtra(EXTRA_BOOK_YEAR, book.date?.year)
-                putExtra(EXTRA_BOOK_MONTH, book.date?.month)
-                putExtra(EXTRA_BOOK_DAY, book.date?.day)
-                putExtra(EXTRA_BOOK_PROGRESS, MAX_BOOK_PRIORITY)
-            } else {
-                putExtra(EXTRA_BOOK_PROGRESS, book.priority)
-            }
-        }
-
-class BookActivity : BaseActivity() {
-
-    private val repository by lazy { app.bookRepository }
-    private val today = Calendar.getInstance()
+    private val presenter by lazy { providePresenter(intent.getParcelableExtra(EXTRA_BOOK)) }
 
     override fun onCreate(savedInstanceState: Bundle?) {
         setDarkTheme(app.config.isDarkTheme)
         super.onCreate(savedInstanceState)
         setContentView(R.layout.book_edit)
-        val bookId = intent.getStringExtra(EXTRA_BOOK_ID)
-        val title = intent.getStringExtra(EXTRA_BOOK_TITLE)
-        val author = intent.getStringExtra(EXTRA_BOOK_AUTHOR)
-        val wasFinished = intent.getBooleanExtra(EXTRA_BOOK_FINISHED, false)
-        val isNewBook = bookId == null
-        toolbar.inflateMenu(R.menu.book_menu)
-        if (title.isNullOrEmpty() && author.isNullOrEmpty()) titleEditText.requestFocus()
-        toolbar.setTitle(if (isNewBook) R.string.book_title_add else R.string.book_title_edit)
-        toolbar.setNavigationIcon(R.drawable.ic_arrow_back)
-        toolbar.setNavigationOnClickListener {
-            finish()
-        }
-        val progressMenuItem = toolbar.menu.findItem(R.id.option_progress_bar)
-        toolbar.setOnMenuItemClickListener { saveMenuItem ->
-            when (saveMenuItem.itemId) {
-                R.id.option_save_book -> {
-                    getRootView().hideKeyboard()
-                    if (progressSeekBar.progress == MAX_BOOK_PRIORITY) {
-                        repository.saveBook(
-                            bookId,
-                            FinishedBookToSend(
-                                titleEditText.text.toString(),
-                                authorEditText.text.toString(),
-                                dayEditText.text.toString(),
-                                monthEditText.text.toString(),
-                                yearEditText.text.toString(),
-                                notesTextArea.text.toString()
-                            ),
-                            wasFinished.takeUnless { isNewBook }
-                        )
-                    } else {
-                        repository.saveBook(
-                            bookId,
-                            PlannedBookToSend(
-                                titleEditText.text.toString(),
-                                authorEditText.text.toString(),
-                                notesTextArea.text.toString(),
-                                progressSeekBar.progress.takeIf { it in (MIN_BOOK_PRIORITY..MAX_BOOK_PRIORITY) }
-                            ),
-                            wasFinished.takeUnless { isNewBook }
-                        )
-                    }.io2main()
-                        .doOnSubscribe {
-                            saveMenuItem.isVisible = false
-                            progressMenuItem.isVisible = true
-                            progressMenuItem.actionView.show()
-                        }
-                        .doOnError {
-                            progressMenuItem.isVisible = false
-                            progressMenuItem.actionView.hide()
-                            saveMenuItem.isVisible = true
-                        }
-                        .bind({
-                            setResult(RESULT_OK)
-                            finish()
-                        }, {
-                            toast(R.string.book_error_save)
-                            logError("cannot post planned book", it)
-                        })
-                    true
-                }
-                else -> false
-            }
-        }
-        titleEditText.setOnFocusChangeListener { _, focus ->
-            val editable = titleEditText.editableText
-            if (!focus) {
-                val url = createBookImageUrl(editable.toString())
-                preloadImage(url, {
-                    bookImage.showNow()
-                    bookImage.setSquareImage(url)
-                }, {
-                    bookImage.hideNow()
-                })
-            }
-        }
-        progressSeekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
-            override fun onStartTrackingTouch(seekBar: SeekBar?) {}
-            override fun onStopTrackingTouch(seekBar: SeekBar?) {}
-            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
-                progressText.text = getString(R.string.book_progress, progress)
-                if (progress == MAX_BOOK_PRIORITY) {
-                    bookDateInputGroup.showNow()
-                    if (!wasFinished && yearEditText.text.isEmpty() && monthEditText.text.isEmpty() && dayEditText.text.isEmpty()) {
-                        yearEditText.setText(today.get(Calendar.YEAR).toString())
-                        if (!isNewBook) {
-                            monthEditText.setText(today.get(Calendar.MONTH).inc().toString())
-                            dayEditText.setText(today.get(Calendar.DAY_OF_MONTH).toString())
-                        }
-                    }
-                } else {
-                    bookDateInputGroup.hideNow()
-                }
-            }
-        })
-        title?.let {
-            bookImage.showNow()
-            bookImage.setSquareImage(createBookImageUrl(it))
-        }
-        titleEditText.setText(title)
-        authorEditText.setText(author)
-        progressSeekBar.setProgressSmoothly(intent.getIntExtra(EXTRA_BOOK_PROGRESS, 0))
-        notesTextArea.setText(intent.getStringExtra(EXTRA_BOOK_NOTES))
-        if (!isNewBook) {
-            if (wasFinished) {
-                yearEditText.setText(intent.getStringExtra(EXTRA_BOOK_YEAR))
-                monthEditText.setText(intent.getStringExtra(EXTRA_BOOK_MONTH))
-                dayEditText.setText(intent.getStringExtra(EXTRA_BOOK_DAY))
-            }
-        }
+        presenter.init()
+    }
+
+    override fun onStop() {
+        super.onStop()
+        presenter.stop()
+    }
+
+    override fun exit(ok: Boolean) {
+        if (ok) setResult(RESULT_OK)
+        finish()
     }
+
 }

+ 34 - 0
app/src/main/java/com/sirekanyan/knigopis/feature/book/BookInteractor.kt

@@ -0,0 +1,34 @@
+package com.sirekanyan.knigopis.feature.book
+
+import com.sirekanyan.knigopis.MAX_BOOK_PRIORITY
+import com.sirekanyan.knigopis.MIN_BOOK_PRIORITY
+import com.sirekanyan.knigopis.model.dto.FinishedBookToSend
+import com.sirekanyan.knigopis.model.dto.PlannedBookToSend
+import com.sirekanyan.knigopis.repository.BookRepository
+import io.reactivex.Completable
+
+interface BookInteractor {
+
+    fun saveBook(initialBook: EditBookModel, book: EditBookModel): Completable
+
+}
+
+class BookInteractorImpl(private val repository: BookRepository) : BookInteractor {
+
+    override fun saveBook(initialBook: EditBookModel, book: EditBookModel): Completable =
+        if (book.progress == MAX_BOOK_PRIORITY) {
+            repository.saveBook(book.id, book.toFinishedBook(), initialBook.isFinished)
+        } else {
+            repository.saveBook(book.id, book.toPlannedBook(), initialBook.isPlanned)
+        }
+
+    private fun EditBookModel.toPlannedBook(): PlannedBookToSend {
+        val priority = progress.takeIf { it in (MIN_BOOK_PRIORITY..MAX_BOOK_PRIORITY) }
+        return PlannedBookToSend(title, author, notes, priority)
+    }
+
+    private fun EditBookModel.toFinishedBook(): FinishedBookToSend {
+        return FinishedBookToSend(title, author, date.day, date.month, date.year, notes)
+    }
+
+}

+ 89 - 0
app/src/main/java/com/sirekanyan/knigopis/feature/book/BookPresenter.kt

@@ -0,0 +1,89 @@
+package com.sirekanyan.knigopis.feature.book
+
+import com.sirekanyan.knigopis.MAX_BOOK_PRIORITY
+import com.sirekanyan.knigopis.R
+import com.sirekanyan.knigopis.common.BasePresenter
+import com.sirekanyan.knigopis.common.Presenter
+import com.sirekanyan.knigopis.common.extensions.io2main
+import com.sirekanyan.knigopis.common.functions.createBookImageUrl
+import com.sirekanyan.knigopis.common.functions.logError
+import com.sirekanyan.knigopis.model.DateModel
+import java.util.*
+
+interface BookPresenter : Presenter {
+
+    fun init()
+
+    interface Router {
+        fun exit(ok: Boolean = false)
+    }
+
+}
+
+class BookPresenterImpl(
+    private val router: BookPresenter.Router,
+    private val interactor: BookInteractor,
+    private val initialBook: EditBookModel
+) : BasePresenter<BookView>(),
+    BookPresenter,
+    BookView.Callbacks {
+
+    private val isNewAction = initialBook.action == BookAction.NEW
+    private val isEditAction = initialBook.action == BookAction.EDIT
+    private val today = Calendar.getInstance()
+
+    override fun init() {
+        view.setTitle(
+            if (isEditAction) {
+                R.string.book_title_edit
+            } else {
+                R.string.book_title_add
+            }
+        )
+        view.setBook(initialBook)
+        if (isNewAction) {
+            view.showKeyboard()
+        }
+    }
+
+    override fun onNavigationBackClicked() {
+        router.exit()
+    }
+
+    override fun onSaveOptionClicked(book: EditBookModel) {
+        interactor.saveBook(initialBook, book)
+            .io2main()
+            .doOnSubscribe {
+                view.showSaveOption(false)
+                view.showSaveProgress(true)
+            }
+            .doOnError {
+                view.showSaveProgress(false)
+                view.showSaveOption(true)
+            }
+            .bind({
+                router.exit(ok = true)
+            }, {
+                view.showSaveError()
+                logError("cannot post planned book", it)
+            })
+    }
+
+    override fun onTitleFocusRemoved(title: String) {
+        view.showBookImage(createBookImageUrl(title))
+    }
+
+    override fun onProgressChanged(progress: Int, date: DateModel) {
+        view.setBookProgress(progress)
+        view.showBookDate(progress == MAX_BOOK_PRIORITY)
+        val wasFinished = initialBook.isFinished
+        val isFinished = progress == MAX_BOOK_PRIORITY
+        if (!wasFinished && isFinished && date.isEmpty()) {
+            val year = today.get(Calendar.YEAR)
+            val month = if (isEditAction) today.get(Calendar.MONTH).inc() else null
+            val day = if (isEditAction) today.get(Calendar.DAY_OF_MONTH) else null
+            view.setBookDate(year, month, day)
+        }
+    }
+
+}

+ 159 - 0
app/src/main/java/com/sirekanyan/knigopis/feature/book/BookView.kt

@@ -0,0 +1,159 @@
+package com.sirekanyan.knigopis.feature.book
+
+import android.support.annotation.StringRes
+import android.view.MenuItem
+import android.view.View
+import android.widget.SeekBar
+import com.sirekanyan.knigopis.R
+import com.sirekanyan.knigopis.common.extensions.*
+import com.sirekanyan.knigopis.common.functions.createBookImageUrl
+import com.sirekanyan.knigopis.model.DateModel
+import kotlinx.android.extensions.LayoutContainer
+import kotlinx.android.synthetic.main.book_edit.*
+
+interface BookView {
+
+    fun setTitle(@StringRes title: Int)
+    fun setBook(book: EditBookModel)
+    fun setBookProgress(progress: Int)
+    fun setBookDate(year: Int?, month: Int?, day: Int?)
+    fun showBookImage(url: String)
+    fun showBookDate(isVisible: Boolean)
+    fun showSaveOption(isVisible: Boolean)
+    fun showSaveProgress(isVisible: Boolean)
+    fun showSaveError()
+    fun showKeyboard()
+
+    interface Callbacks {
+        fun onNavigationBackClicked()
+        fun onSaveOptionClicked(book: EditBookModel)
+        fun onTitleFocusRemoved(title: String)
+        fun onProgressChanged(progress: Int, date: DateModel)
+    }
+
+}
+
+class BookViewImpl(
+    override val containerView: View,
+    callbacks: BookView.Callbacks,
+    initialBook: EditBookModel
+) : BookView,
+    LayoutContainer {
+
+    private val context = containerView.context
+    private val saveMenuItem: MenuItem
+    private val progressMenuItem: MenuItem
+    private val bookAction = initialBook.action
+    private val bookId = initialBook.id
+
+    init {
+        initToolbar(callbacks)
+        saveMenuItem = toolbar.menu.findItem(R.id.option_save_book)
+        progressMenuItem = toolbar.menu.findItem(R.id.option_progress_bar)
+        titleEditText.setOnFocusChangeListener { _, focus ->
+            if (!focus) {
+                callbacks.onTitleFocusRemoved(titleEditText.editableText.toString())
+            }
+        }
+        progressSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
+            override fun onStartTrackingTouch(seekBar: SeekBar?) {}
+            override fun onStopTrackingTouch(seekBar: SeekBar?) {}
+            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+                callbacks.onProgressChanged(progress, dateModel)
+            }
+        })
+    }
+
+    private val bookModel
+        get() = EditBookModel(
+            bookAction,
+            bookId,
+            titleEditText.text.toString(),
+            authorEditText.text.toString(),
+            progressSeekBar.progress,
+            dateModel,
+            notesTextArea.text.toString()
+        )
+
+    private val dateModel
+        get() = DateModel(
+            yearEditText.text.toString(),
+            monthEditText.text.toString(),
+            dayEditText.text.toString()
+        )
+
+    override fun setTitle(title: Int) {
+        toolbar.setTitle(title)
+    }
+
+    override fun setBook(book: EditBookModel) {
+        if (book.title.isNotEmpty()) {
+            bookImage.showNow()
+            bookImage.setSquareImage(createBookImageUrl(book.title))
+        }
+        titleEditText.setText(book.title)
+        authorEditText.setText(book.author)
+        progressSeekBar.setProgressSmoothly(book.progress)
+        yearEditText.setText(book.date.year)
+        monthEditText.setText(book.date.month)
+        dayEditText.setText(book.date.day)
+        notesTextArea.setText(book.notes)
+    }
+
+    override fun setBookProgress(progress: Int) {
+        progressText.text = context.getString(R.string.book_progress, progress)
+    }
+
+    override fun setBookDate(year: Int?, month: Int?, day: Int?) {
+        yearEditText.setText(year?.toString().orEmpty())
+        monthEditText.setText(month?.toString().orEmpty())
+        dayEditText.setText(day?.toString().orEmpty())
+    }
+
+    override fun showBookImage(url: String) {
+        context.preloadImage(url, {
+            bookImage.showNow()
+            bookImage.setSquareImage(url)
+        }, {
+            bookImage.hideNow()
+        })
+    }
+
+    override fun showBookDate(isVisible: Boolean) {
+        bookDateInputGroup.showNow(isVisible)
+    }
+
+    override fun showSaveOption(isVisible: Boolean) {
+        saveMenuItem.isVisible = isVisible
+    }
+
+    override fun showSaveProgress(isVisible: Boolean) {
+        progressMenuItem.isVisible = isVisible
+        progressMenuItem.actionView.show(isVisible)
+    }
+
+    override fun showSaveError() {
+        context.toast(R.string.book_error_save)
+    }
+
+    override fun showKeyboard() {
+        titleEditText.requestFocus()
+    }
+
+    private fun initToolbar(callbacks: BookView.Callbacks) {
+        toolbar.inflateMenu(R.menu.book_menu)
+        toolbar.setNavigationIcon(R.drawable.ic_arrow_back)
+        toolbar.setNavigationOnClickListener { callbacks.onNavigationBackClicked() }
+        toolbar.setOnMenuItemClickListener { item ->
+            when (item.itemId) {
+                R.id.option_save_book -> {
+                    containerView.hideKeyboard()
+                    callbacks.onSaveOptionClicked(bookModel)
+                    true
+                }
+                else -> false
+            }
+        }
+    }
+
+}

+ 6 - 8
app/src/main/java/com/sirekanyan/knigopis/repository/BookRepository.kt

@@ -22,9 +22,9 @@ interface BookRepository {
 
     fun findCached(): Maybe<List<BookModel>>
 
-    fun saveBook(bookId: String?, book: FinishedBookToSend, done: Boolean?): Completable
+    fun saveBook(bookId: String?, book: FinishedBookToSend, wasFinished: Boolean): Completable
 
-    fun saveBook(bookId: String?, book: PlannedBookToSend, done: Boolean?): Completable
+    fun saveBook(bookId: String?, book: PlannedBookToSend, wasPlanned: Boolean): Completable
 
     fun deleteBook(book: BookDataModel): Completable
 
@@ -42,22 +42,20 @@ class BookRepositoryImpl(
 
     override fun observeBooks() = observe()
 
-    override fun saveBook(bookId: String?, book: FinishedBookToSend, done: Boolean?): Completable =
+    override fun saveBook(bookId: String?, book: FinishedBookToSend, wasFinished: Boolean): Completable =
         when {
             bookId == null -> api.createFinishedBook(auth.getAccessToken(), book)
-            done == null -> Completable.error(UnsupportedOperationException())
-            done -> api.updateFinishedBook(bookId, auth.getAccessToken(), book)
+            wasFinished -> api.updateFinishedBook(bookId, auth.getAccessToken(), book)
             else -> {
                 api.createFinishedBook(auth.getAccessToken(), book)
                     .andThen(api.deletePlannedBook(bookId, auth.getAccessToken()))
             }
         }
 
-    override fun saveBook(bookId: String?, book: PlannedBookToSend, done: Boolean?): Completable =
+    override fun saveBook(bookId: String?, book: PlannedBookToSend, wasPlanned: Boolean): Completable =
         when {
             bookId == null -> api.createPlannedBook(auth.getAccessToken(), book)
-            done == null -> Completable.error(UnsupportedOperationException())
-            !done -> api.updatePlannedBook(bookId, auth.getAccessToken(), book)
+            wasPlanned -> api.updatePlannedBook(bookId, auth.getAccessToken(), book)
             else -> {
                 api.createPlannedBook(auth.getAccessToken(), book)
                     .andThen(api.deleteFinishedBook(bookId, auth.getAccessToken()))