Skip to content

Commit

Permalink
[MBL-17622][Parent] Inbox Details screen (#2565)
Browse files Browse the repository at this point in the history
refs: MBL-17622
affects: Parent
release note: none
  • Loading branch information
domonkosadam authored Oct 1, 2024
1 parent d13f97f commit 2f6e3d8
Show file tree
Hide file tree
Showing 54 changed files with 4,790 additions and 194 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright (C) 2024 - present Instructure, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.instructure.parentapp.ui.interaction

import androidx.compose.ui.platform.ComposeView
import androidx.test.espresso.matcher.ViewMatchers
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils
import com.google.android.apps.common.testing.accessibility.framework.checks.SpeakableTextPresentCheck
import com.instructure.canvas.espresso.common.interaction.InboxDetailsInteractionTest
import com.instructure.canvas.espresso.common.pages.InboxPage
import com.instructure.canvas.espresso.mockCanvas.MockCanvas
import com.instructure.canvas.espresso.mockCanvas.addConversation
import com.instructure.canvas.espresso.mockCanvas.addConversationWithMultipleMessages
import com.instructure.canvas.espresso.mockCanvas.addConversations
import com.instructure.canvas.espresso.mockCanvas.init
import com.instructure.canvasapi2.models.Conversation
import com.instructure.canvasapi2.models.User
import com.instructure.parentapp.BuildConfig
import com.instructure.parentapp.features.login.LoginActivity
import com.instructure.parentapp.ui.pages.DashboardPage
import com.instructure.parentapp.utils.ParentActivityTestRule
import com.instructure.parentapp.utils.tokenLogin
import dagger.hilt.android.testing.HiltAndroidTest
import org.hamcrest.Matchers

@HiltAndroidTest
class ParentInboxDetailsInteractionTest: InboxDetailsInteractionTest() {
override val isTesting = BuildConfig.IS_TESTING

override val activityRule = ParentActivityTestRule(LoginActivity::class.java)

private val dashboardPage = DashboardPage()
private val inboxPage = InboxPage()

override fun enableAndConfigureAccessibilityChecks() {
extraAccessibilitySupressions = Matchers.allOf(
AccessibilityCheckResultUtils.matchesCheck(
SpeakableTextPresentCheck::class.java
),
AccessibilityCheckResultUtils.matchesViews(
ViewMatchers.withParent(
ViewMatchers.withClassName(
Matchers.equalTo(ComposeView::class.java.name)
)
)
)
)

super.enableAndConfigureAccessibilityChecks()
}

override fun goToInboxDetails(data: MockCanvas, conversationSubject: String) {
val parent = data.parents.first()
val token = data.tokenFor(parent)!!
tokenLogin(data.domain, token, parent)

dashboardPage.openNavigationDrawer()
dashboardPage.clickInbox()

inboxPage.openConversation(conversationSubject)
}

override fun goToInboxDetails(data: MockCanvas, conversation: Conversation) {
val parent = data.parents.first()
val token = data.tokenFor(parent)!!
tokenLogin(data.domain, token, parent)

dashboardPage.openNavigationDrawer()
dashboardPage.clickInbox()

inboxPage.openConversation(conversation)
}

override fun initData(): MockCanvas {
val data = MockCanvas.init(
parentCount = 1,
studentCount = 1,
teacherCount = 2,
courseCount = 1,
favoriteCourseCount = 1,
)
MockCanvas.data.addConversations(conversationCount = 2, userId = 2, contextCode = "course_1", contextName = "Course 1")
MockCanvas.data.addConversationWithMultipleMessages(getTeachers().first().id, listOf(getLoggedInUser().id), 5)

return data
}

override fun getLoggedInUser(): User = MockCanvas.data.parents[0]

override fun getTeachers(): List<User> = MockCanvas.data.teachers

override fun getConversations(data: MockCanvas): List<Conversation> {
return data.conversations.values.toList()
}

override fun addNewConversation(
data: MockCanvas,
authorId: Long,
recipients: List<Long>,
messageSubject: String,
messageBody: String,
): Conversation {
return data.addConversation(authorId, recipients, messageBody, messageSubject)
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/*
* Copyright (C) 2024 - present Instructure, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.instructure.parentapp.features.inbox.compose

import com.instructure.canvasapi2.apis.CourseAPI
Expand All @@ -11,6 +26,7 @@ import com.instructure.canvasapi2.models.Conversation
import com.instructure.canvasapi2.models.Course
import com.instructure.canvasapi2.models.Enrollment
import com.instructure.canvasapi2.models.Group
import com.instructure.canvasapi2.models.Message
import com.instructure.canvasapi2.models.Recipient
import com.instructure.canvasapi2.utils.DataResult
import com.instructure.canvasapi2.utils.depaginate
Expand Down Expand Up @@ -72,6 +88,27 @@ class ParentInboxComposeRepository(
)
}

override suspend fun addMessage(
conversationId: Long,
recipients: List<Recipient>,
message: String,
includedMessages: List<Message>,
attachments: List<Attachment>,
context: CanvasContext,
): DataResult<Conversation> {
val restParams = RestParams()

return inboxAPI.addMessage(
conversationId = conversationId,
recipientIds = recipients.mapNotNull { it.stringId },
body = message,
includedMessageIds = includedMessages.map { it.id }.toLongArray(),
attachmentIds = attachments.map { it.id }.toLongArray(),
contextCode = context.contextId,
params = restParams
)
}

override suspend fun canSendToAll(context: CanvasContext): DataResult<Boolean> {
val restParams = RestParams()
val permissionResponse = courseAPI.getCoursePermissions(context.id, listOf(CanvasContextPermission.SEND_MESSAGES_ALL), restParams)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import androidx.fragment.app.FragmentActivity
import com.instructure.canvasapi2.apis.InboxApi
import com.instructure.canvasapi2.models.Conversation
import com.instructure.pandautils.features.inbox.list.InboxRouter
import com.instructure.pandautils.features.inbox.utils.InboxComposeOptions
import com.instructure.pandautils.utils.setupAsBackButton
import com.instructure.parentapp.util.navigation.Navigation
import org.greenrobot.eventbus.Subscribe
Expand All @@ -30,7 +31,7 @@ import org.greenrobot.eventbus.Subscribe
class ParentInboxRouter(private val activity: FragmentActivity, private val navigation: Navigation) : InboxRouter {

override fun openConversation(conversation: Conversation, scope: InboxApi.Scope) {
// TODO: Implement
navigation.navigate(activity, navigation.inboxDetailsRoute(conversation.id))
}

override fun attachNavigationIcon(toolbar: Toolbar) {
Expand All @@ -40,7 +41,12 @@ class ParentInboxRouter(private val activity: FragmentActivity, private val navi
}

override fun routeToNewMessage() {
val route = navigation.inboxCompose
val route = navigation.inboxComposeRoute(InboxComposeOptions.buildNewMessage())
navigation.navigate(activity, route)
}

override fun routeToCompose(options: InboxComposeOptions) {
val route = navigation.inboxComposeRoute(options)
navigation.navigate(activity, route)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ import com.instructure.pandautils.features.calendarevent.details.EventFragment
import com.instructure.pandautils.features.calendartodo.createupdate.CreateUpdateToDoFragment
import com.instructure.pandautils.features.calendartodo.details.ToDoFragment
import com.instructure.pandautils.features.inbox.compose.InboxComposeFragment
import com.instructure.pandautils.features.inbox.details.InboxDetailsFragment
import com.instructure.pandautils.features.inbox.list.InboxFragment
import com.instructure.pandautils.features.inbox.utils.InboxComposeOptions
import com.instructure.pandautils.features.settings.SettingsFragment
import com.instructure.pandautils.utils.Const
import com.instructure.pandautils.utils.fromJson
Expand Down Expand Up @@ -50,11 +52,16 @@ class Navigation(apiPrefs: ApiPrefs) {
val calendar = "$baseUrl/calendar"
val alerts = "$baseUrl/alerts"
val inbox = "$baseUrl/conversations"
val inboxCompose = "$baseUrl/conversations/compose"
val manageStudents = "$baseUrl/manage-students"
val qrPairing = "$baseUrl/qr-pairing"
val settings = "$baseUrl/settings"

private val inboxCompose = "$baseUrl/conversations/compose/{${InboxComposeOptions.COMPOSE_PARAMETERS}}"
fun inboxComposeRoute(options: InboxComposeOptions) = "$baseUrl/conversations/compose/${InboxComposeOptionsParametersType.serializeAsValue(options)}"

private val inboxDetails = "$baseUrl/conversations/{${InboxDetailsFragment.CONVERSATION_ID}}"
fun inboxDetailsRoute(conversationId: Long) = "$baseUrl/conversations/$conversationId"

private val calendarEvent =
"$baseUrl/{${EventFragment.CONTEXT_TYPE}}/{${EventFragment.CONTEXT_ID}}/calendar_events/{${EventFragment.SCHEDULE_ITEM_ID}}"
private val createEvent = "$baseUrl/create-event/{${CreateUpdateEventFragment.INITIAL_DATE}}"
Expand Down Expand Up @@ -99,7 +106,21 @@ class Navigation(apiPrefs: ApiPrefs) {
}
}
fragment<InboxFragment>(inbox)
fragment<InboxComposeFragment>(inboxCompose)
fragment<InboxComposeFragment>(inboxCompose) {
argument(InboxComposeOptions.COMPOSE_PARAMETERS) {
type = InboxComposeOptionsParametersType
nullable = false
}
}
fragment<InboxDetailsFragment>(inboxDetails) {
argument(InboxDetailsFragment.CONVERSATION_ID) {
type = NavType.LongType
nullable = false
}
deepLink {
uriPattern = inboxDetails
}
}
fragment<ManageStudentsFragment>(manageStudents)
fragment<QrPairingFragment>(qrPairing)
fragment<SettingsFragment>(settings)
Expand Down Expand Up @@ -240,6 +261,26 @@ private val ScheduleItemParametersType = object : NavType<ScheduleItem>(
}
}

private val InboxComposeOptionsParametersType = object : NavType<InboxComposeOptions>(
isNullableAllowed = false
) {
override fun put(bundle: Bundle, key: String, value: InboxComposeOptions) {
bundle.putParcelable(key, value)
}

override fun get(bundle: Bundle, key: String): InboxComposeOptions? {
return bundle.getParcelable(key) as? InboxComposeOptions
}

override fun serializeAsValue(value: InboxComposeOptions): String {
return Uri.encode(value.toJson())
}

override fun parseValue(value: String): InboxComposeOptions {
return value.fromJson()
}
}

private val UserParametersType = object : NavType<User>(isNullableAllowed = false) {
override fun put(bundle: Bundle, key: String, value: User) {
bundle.putParcelable(key, value)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
/*
* Copyright (C) 2024 - present Instructure, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.instructure.student.features.inbox.compose

import com.instructure.canvasapi2.models.Attachment
import com.instructure.canvasapi2.models.CanvasContext
import com.instructure.canvasapi2.models.Conversation
import com.instructure.canvasapi2.models.Course
import com.instructure.canvasapi2.models.Group
import com.instructure.canvasapi2.models.Message
import com.instructure.canvasapi2.models.Recipient
import com.instructure.canvasapi2.utils.DataResult
import com.instructure.pandautils.features.inbox.compose.InboxComposeRepository
Expand Down Expand Up @@ -37,6 +53,17 @@ class StudentInboxComposeRepository: InboxComposeRepository {
TODO("Not yet implemented")
}

override suspend fun addMessage(
conversationId: Long,
recipients: List<Recipient>,
message: String,
includedMessages: List<Message>,
attachments: List<Attachment>,
context: CanvasContext
): DataResult<Conversation> {
TODO("Not yet implemented")
}

override suspend fun canSendToAll(context: CanvasContext): DataResult<Boolean> {
TODO("Not yet implemented")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.instructure.canvasapi2.apis.InboxApi
import com.instructure.canvasapi2.models.Conversation
import com.instructure.pandautils.features.inbox.list.InboxRouter
import com.instructure.pandautils.features.inbox.list.InboxFragment
import com.instructure.pandautils.features.inbox.list.InboxRouter
import com.instructure.pandautils.features.inbox.utils.InboxComposeOptions
import com.instructure.student.activity.NavigationActivity
import com.instructure.student.events.ConversationUpdatedEvent
import com.instructure.student.fragment.InboxComposeMessageFragment
Expand All @@ -48,6 +49,10 @@ class StudentInboxRouter(private val activity: FragmentActivity, private val fra
RouteMatcher.route(activity, route)
}

override fun routeToCompose(options: InboxComposeOptions) {
TODO("Not yet implemented")
}

override fun avatarClicked(conversation: Conversation, scope: InboxApi.Scope) {
openConversation(conversation, scope)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
/*
* Copyright (C) 2024 - present Instructure, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.instructure.teacher.features.inbox.compose

import com.instructure.canvasapi2.models.Attachment
import com.instructure.canvasapi2.models.CanvasContext
import com.instructure.canvasapi2.models.Conversation
import com.instructure.canvasapi2.models.Course
import com.instructure.canvasapi2.models.Group
import com.instructure.canvasapi2.models.Message
import com.instructure.canvasapi2.models.Recipient
import com.instructure.canvasapi2.utils.DataResult
import com.instructure.pandautils.features.inbox.compose.InboxComposeRepository
Expand Down Expand Up @@ -37,6 +53,17 @@ class TeacherInboxComposeRepository: InboxComposeRepository {
TODO("Not yet implemented")
}

override suspend fun addMessage(
conversationId: Long,
recipients: List<Recipient>,
message: String,
includedMessages: List<Message>,
attachments: List<Attachment>,
context: CanvasContext
): DataResult<Conversation> {
TODO("Not yet implemented")
}

override suspend fun canSendToAll(context: CanvasContext): DataResult<Boolean> {
TODO("Not yet implemented")
}
Expand Down
Loading

0 comments on commit 2f6e3d8

Please sign in to comment.