Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.instructure.canvasapi2.models.CanvasContext
import com.instructure.canvasapi2.models.Course
import com.instructure.canvasapi2.models.Group
import com.instructure.student.mobius.conferences.conference_list.ConferenceHeaderType
import com.instructure.student.mobius.conferences.conference_list.ui.ConferenceListItemViewState
import com.instructure.student.mobius.conferences.conference_list.ui.ConferenceListRepositoryFragment
import com.instructure.student.mobius.conferences.conference_list.ui.ConferenceListViewState
Expand Down Expand Up @@ -81,7 +82,11 @@ class ConferenceListRenderTest : StudentRenderTest() {
fun displaysListItems() {
val tint = Color.BLUE
val itemStates = listOf(
ConferenceListItemViewState.ConferenceHeader("Header 1"),
ConferenceListItemViewState.ConferenceHeader(
title = "Header 1",
headerType = ConferenceHeaderType.NEW_CONFERENCES,
isExpanded = true
),
ConferenceListItemViewState.ConferenceItem(
tint = tint,
title = "Conference 1",
Expand All @@ -100,7 +105,11 @@ class ConferenceListRenderTest : StudentRenderTest() {
conferenceId = 0,
isJoinable = false
),
ConferenceListItemViewState.ConferenceHeader("Header 2"),
ConferenceListItemViewState.ConferenceHeader(
title = "Header 2",
headerType = ConferenceHeaderType.CONCLUDED_CONFERENCES,
isExpanded = true
),
ConferenceListItemViewState.ConferenceItem(
tint = tint,
title = "Conference 3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ sealed class ConferenceListEvent {
object LaunchInBrowserFinished : ConferenceListEvent()
data class DataLoaded(val listResult: DataResult<List<Conference>>) : ConferenceListEvent()
data class ConferenceClicked(val conferenceId: Long) : ConferenceListEvent()
data class HeaderClicked(val headerType: ConferenceHeaderType) : ConferenceListEvent()
}

enum class ConferenceHeaderType {
NEW_CONFERENCES,
CONCLUDED_CONFERENCES
}

sealed class ConferenceListEffect {
Expand All @@ -38,5 +44,7 @@ data class ConferenceListModel(
val canvasContext: CanvasContext,
val isLoading: Boolean = false,
val isLaunchingInBrowser: Boolean = false,
val listResult: DataResult<List<Conference>>? = null
val listResult: DataResult<List<Conference>>? = null,
val isNewConferencesExpanded: Boolean = true,
val isConcludedConferencesExpanded: Boolean = true
)
Original file line number Diff line number Diff line change
Expand Up @@ -58,26 +58,38 @@ object ConferenceListPresenter : Presenter<ConferenceListModel, ConferenceListVi
if (new.isNotEmpty()) {
// Header
itemStates.add(
ConferenceListItemViewState.ConferenceHeader(context.getString(R.string.newConferences))
ConferenceListItemViewState.ConferenceHeader(
context.getString(R.string.newConferences),
ConferenceHeaderType.NEW_CONFERENCES,
model.isNewConferencesExpanded
)
)

// Conferences
itemStates.addAll(new.map {
mapItemState(color, it, context)
})
// Conferences - only add if expanded
if (model.isNewConferencesExpanded) {
itemStates.addAll(new.map {
mapItemState(color, it, context)
})
}
}

// Set up 'Concluded Conferences' list items
if (concluded.isNotEmpty()) {
// Header
itemStates.add(
ConferenceListItemViewState.ConferenceHeader(context.getString(R.string.concludedConferences))
ConferenceListItemViewState.ConferenceHeader(
context.getString(R.string.concludedConferences),
ConferenceHeaderType.CONCLUDED_CONFERENCES,
model.isConcludedConferencesExpanded
)
)

// Conferences
itemStates.addAll(concluded.map {
mapItemState(color, it, context)
})
// Conferences - only add if expanded
if (model.isConcludedConferencesExpanded) {
itemStates.addAll(concluded.map {
mapItemState(color, it, context)
})
}
}

return ConferenceListViewState.Loaded(model.isLaunchingInBrowser, itemStates)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ class ConferenceListUpdate : UpdateInit<ConferenceListModel, ConferenceListEvent
)
}
ConferenceListEvent.LaunchInBrowserFinished -> Next.next(model.copy(isLaunchingInBrowser = false))
is ConferenceListEvent.HeaderClicked -> {
when (event.headerType) {
ConferenceHeaderType.NEW_CONFERENCES -> {
Next.next(model.copy(isNewConferencesExpanded = !model.isNewConferencesExpanded))
}
ConferenceHeaderType.CONCLUDED_CONFERENCES -> {
Next.next(model.copy(isConcludedConferencesExpanded = !model.isConcludedConferencesExpanded))
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ import com.instructure.student.R
import com.instructure.student.databinding.AdapterConferenceHeaderBinding
import com.instructure.student.databinding.AdapterConferenceItemBinding
import com.instructure.student.databinding.AdapterConferenceListErrorBinding
import com.instructure.student.mobius.conferences.conference_list.ConferenceHeaderType

interface ConferenceListAdapterCallback : BasicItemCallback {
fun onConferenceClicked(conferenceId: Long)
fun reload()
fun onHeaderClicked(headerType: ConferenceHeaderType)
}

class ConferenceListAdapter(callback: ConferenceListAdapterCallback) :
Expand Down Expand Up @@ -59,9 +61,18 @@ class ConferenceListErrorBinder : BasicItemBinder<ConferenceListItemViewState.Er

class ConferenceListHeaderBinder : BasicItemBinder<ConferenceListItemViewState.ConferenceHeader, ConferenceListAdapterCallback>() {
override val layoutResId = R.layout.adapter_conference_header
override val bindBehavior = Item {data, _, _ ->
override val bindBehavior = Item {data, callback, _ ->
val binding = AdapterConferenceHeaderBinding.bind(this)
binding.title.text = data.title

binding.expandIcon.animate()
.rotation(if (data.isExpanded) 180f else 0f)
.setDuration(200)
.start()

binding.headerContainer.onClick {
callback.onHeaderClicked(data.headerType)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import com.instructure.student.R
import com.instructure.student.databinding.FragmentConferenceListBinding
import com.instructure.student.mobius.common.ui.MobiusView
import com.instructure.student.mobius.conferences.conference_details.ui.ConferenceDetailsRepositoryFragment
import com.instructure.student.mobius.conferences.conference_list.ConferenceHeaderType
import com.instructure.student.mobius.conferences.conference_list.ConferenceListEvent
import com.instructure.student.router.RouteMatcher
import com.spotify.mobius.functions.Consumer
Expand Down Expand Up @@ -84,6 +85,10 @@ class ConferenceListView(
}

override fun reload() = output.accept(ConferenceListEvent.PullToRefresh)

override fun onHeaderClicked(headerType: ConferenceHeaderType) {
output.accept(ConferenceListEvent.HeaderClicked(headerType))
}
})
binding.recyclerView.layoutManager = LinearLayoutManager(context)
binding.recyclerView.adapter = listAdapter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
package com.instructure.student.mobius.conferences.conference_list.ui

import com.instructure.student.mobius.conferences.conference_list.ConferenceHeaderType

sealed class ConferenceListViewState(open val isLaunchingInBrowser: Boolean) {
class Loading(isLaunchingInBrowser: Boolean) : ConferenceListViewState(isLaunchingInBrowser)
data class Loaded(
Expand All @@ -27,7 +29,11 @@ sealed class ConferenceListViewState(open val isLaunchingInBrowser: Boolean) {
sealed class ConferenceListItemViewState {
object Empty : ConferenceListItemViewState()
object Error : ConferenceListItemViewState()
data class ConferenceHeader(val title: String): ConferenceListItemViewState()
data class ConferenceHeader(
val title: String,
val headerType: ConferenceHeaderType,
val isExpanded: Boolean
): ConferenceListItemViewState()
data class ConferenceItem(
val tint: Int,
val title: String,
Expand Down
22 changes: 18 additions & 4 deletions apps/student/src/main/res/layout/adapter_conference_header.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,38 @@
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/headerContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingTop="16dp">
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:gravity="center_vertical"
android:background="?attr/selectableItemBackground">

<TextView
android:id="@+id/title"
style="@style/TextFont.Medium"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="2"
android:textSize="12sp"
android:textColor="@color/textDark"
tools:text="New Conferences" />

</FrameLayout>
<ImageView
android:id="@+id/expandIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:tint="@color/textDark"
android:src="@drawable/ic_expand"
android:contentDescription="@string/a11y_expand_collapse" />

</LinearLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.instructure.canvasapi2.models.Course
import com.instructure.canvasapi2.utils.DataResult
import com.instructure.pandautils.utils.color
import com.instructure.student.R
import com.instructure.student.mobius.conferences.conference_list.ConferenceHeaderType
import com.instructure.student.mobius.conferences.conference_list.ConferenceListModel
import com.instructure.student.mobius.conferences.conference_list.ConferenceListPresenter
import com.instructure.student.mobius.conferences.conference_list.ui.ConferenceListItemViewState
Expand Down Expand Up @@ -97,7 +98,11 @@ class ConferenceListPresenterTest : Assert() {
// Generate state
val state = ConferenceListPresenter.present(model, context) as ConferenceListViewState.Loaded

val expectedHeader = ConferenceListItemViewState.ConferenceHeader(context.getString(R.string.newConferences))
val expectedHeader = ConferenceListItemViewState.ConferenceHeader(
context.getString(R.string.newConferences),
ConferenceHeaderType.NEW_CONFERENCES,
true
)
val expectedConferenceItem = ConferenceListPresenter.mapItemState(canvasContext.color, conference, context)

// Should have two list items - one header and one conference
Expand All @@ -116,7 +121,11 @@ class ConferenceListPresenterTest : Assert() {
// Generate state
val state = ConferenceListPresenter.present(model, context) as ConferenceListViewState.Loaded

val expectedHeader = ConferenceListItemViewState.ConferenceHeader(context.getString(R.string.concludedConferences))
val expectedHeader = ConferenceListItemViewState.ConferenceHeader(
context.getString(R.string.concludedConferences),
ConferenceHeaderType.CONCLUDED_CONFERENCES,
true
)
val expectedConferenceItem = ConferenceListPresenter.mapItemState(canvasContext.color, conference, context)

// Should have two list items - one header and one conference
Expand All @@ -137,8 +146,16 @@ class ConferenceListPresenterTest : Assert() {
// Generate state
val state = ConferenceListPresenter.present(model, context) as ConferenceListViewState.Loaded

val newHeader = ConferenceListItemViewState.ConferenceHeader(context.getString(R.string.newConferences))
val concludedHeader = ConferenceListItemViewState.ConferenceHeader(context.getString(R.string.concludedConferences))
val newHeader = ConferenceListItemViewState.ConferenceHeader(
context.getString(R.string.newConferences),
ConferenceHeaderType.NEW_CONFERENCES,
true
)
val concludedHeader = ConferenceListItemViewState.ConferenceHeader(
context.getString(R.string.concludedConferences),
ConferenceHeaderType.CONCLUDED_CONFERENCES,
true
)
val inProgressItem = ConferenceListPresenter.mapItemState(canvasContext.color, inProgress, context)
val notStartedItem = ConferenceListPresenter.mapItemState(canvasContext.color, notStarted, context)
val concludedItem = ConferenceListPresenter.mapItemState(canvasContext.color, concluded, context)
Expand Down Expand Up @@ -236,4 +253,81 @@ class ConferenceListPresenterTest : Assert() {

assertEquals(state, expected)
}

@Test
fun `Returns only header when new conferences section is collapsed`() {
// Set up model with a not-started conference but collapsed state
val conference = Conference()
val result = DataResult.Success(listOf(conference))
val model = ConferenceListModel(canvasContext, listResult = result, isNewConferencesExpanded = false)

// Generate state
val state = ConferenceListPresenter.present(model, context) as ConferenceListViewState.Loaded

val expectedHeader = ConferenceListItemViewState.ConferenceHeader(
context.getString(R.string.newConferences),
ConferenceHeaderType.NEW_CONFERENCES,
false
)

// Should have only the header, no conference items
assertEquals(state.itemStates.size, 1)
assertEquals(state.itemStates[0], expectedHeader)
}

@Test
fun `Returns only header when concluded conferences section is collapsed`() {
// Set up model with a concluded conference but collapsed state
val conference = Conference(startedAt = Date(), endedAt = Date())
val result = DataResult.Success(listOf(conference))
val model = ConferenceListModel(canvasContext, listResult = result, isConcludedConferencesExpanded = false)

// Generate state
val state = ConferenceListPresenter.present(model, context) as ConferenceListViewState.Loaded

val expectedHeader = ConferenceListItemViewState.ConferenceHeader(
context.getString(R.string.concludedConferences),
ConferenceHeaderType.CONCLUDED_CONFERENCES,
false
)

// Should have only the header, no conference items
assertEquals(state.itemStates.size, 1)
assertEquals(state.itemStates[0], expectedHeader)
}

@Test
fun `Sections can be collapsed independently`() {
// Set up model with both types but new conferences collapsed
val notStarted = Conference(startedAt = null, endedAt = null)
val concluded = Conference(startedAt = Date(), endedAt = Date())
val result = DataResult.Success(listOf(notStarted, concluded))
val model = ConferenceListModel(
canvasContext,
listResult = result,
isNewConferencesExpanded = false,
isConcludedConferencesExpanded = true
)

// Generate state
val state = ConferenceListPresenter.present(model, context) as ConferenceListViewState.Loaded

val newHeader = ConferenceListItemViewState.ConferenceHeader(
context.getString(R.string.newConferences),
ConferenceHeaderType.NEW_CONFERENCES,
false
)
val concludedHeader = ConferenceListItemViewState.ConferenceHeader(
context.getString(R.string.concludedConferences),
ConferenceHeaderType.CONCLUDED_CONFERENCES,
true
)
val concludedItem = ConferenceListPresenter.mapItemState(canvasContext.color, concluded, context)

// Should have collapsed new header (no items), expanded concluded header with item
assertEquals(state.itemStates.size, 3)
assertEquals(state.itemStates[0], newHeader)
assertEquals(state.itemStates[1], concludedHeader)
assertEquals(state.itemStates[2], concludedItem)
}
}
Loading
Loading