Compare commits
32 Commits
develop
...
app-layout
Author | SHA1 | Date |
---|---|---|
ericdecanini | 936d643ebb | |
ericdecanini | 311fb84ca0 | |
ericdecanini | 1911f2417d | |
ericdecanini | 96477ef47b | |
ericdecanini | 6cea400637 | |
ericdecanini | 3bfcc69a37 | |
NIkita Fedrunov | e0c455a476 | |
Germain | 36a221adf1 | |
NIkita Fedrunov | ee300cf11d | |
NIkita Fedrunov | 891173c04a | |
NIkita Fedrunov | eac9371a07 | |
NIkita Fedrunov | 1b651f2aa5 | |
NIkita Fedrunov | 9a29c79233 | |
NIkita Fedrunov | 67b7bc3d83 | |
NIkita Fedrunov | 61cf3c3125 | |
NIkita Fedrunov | 1afd10e686 | |
NIkita Fedrunov | 1153297576 | |
NIkita Fedrunov | 6a9284eae3 | |
NIkita Fedrunov | d18f3cfe06 | |
Nikita Fedrunov | 7515b77f9d | |
Nikita Fedrunov | 744d212b86 | |
NIkita Fedrunov | ecd1776085 | |
NIkita Fedrunov | fc265dd480 | |
NIkita Fedrunov | 108e1b874c | |
NIkita Fedrunov | e29687fa49 | |
NIkita Fedrunov | d986fede72 | |
NIkita Fedrunov | f2309af4c6 | |
NIkita Fedrunov | 74fd14af8f | |
NIkita Fedrunov | 8314eb71c0 | |
NIkita Fedrunov | f8ec268145 | |
Germain | ad360074bf | |
NIkita Fedrunov | 5003459962 |
|
@ -142,32 +142,6 @@ jobs:
|
||||||
env:
|
env:
|
||||||
PROJECT_ID: "PN_kwDOAM0swc2KCw"
|
PROJECT_ID: "PN_kwDOAM0swc2KCw"
|
||||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
|
||||||
move_threads_issues:
|
|
||||||
name: A-Threads to Thread board
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
# Skip in forks
|
|
||||||
if: >
|
|
||||||
github.repository == 'vector-im/element-android' &&
|
|
||||||
contains(github.event.issue.labels.*.name, 'A-Threads')
|
|
||||||
steps:
|
|
||||||
- uses: octokit/graphql-action@v2.x
|
|
||||||
with:
|
|
||||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
|
||||||
query: |
|
|
||||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
|
||||||
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
|
||||||
projectNextItem {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
projectid: ${{ env.PROJECT_ID }}
|
|
||||||
contentid: ${{ github.event.issue.node_id }}
|
|
||||||
env:
|
|
||||||
PROJECT_ID: "PN_kwDOAM0swc0rRA"
|
|
||||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
|
||||||
|
|
||||||
move_message_bubbles_issues:
|
move_message_bubbles_issues:
|
||||||
name: A-Message-Bubbles to Message bubbles board
|
name: A-Message-Bubbles to Message bubbles board
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
[App Layout] Obsolete settings are not shown when App Layout flag is enabled
|
|
@ -0,0 +1 @@
|
||||||
|
[App Layout] - Invites now show empty screen after you reject last invite
|
|
@ -451,6 +451,9 @@
|
||||||
<!-- Invites fragment -->
|
<!-- Invites fragment -->
|
||||||
<string name="invites_title">Invites</string>
|
<string name="invites_title">Invites</string>
|
||||||
|
|
||||||
|
<string name="invites_empty_title">Nothing new.</string>
|
||||||
|
<string name="invites_empty_message">This is where your new requests and invites will be.</string>
|
||||||
|
|
||||||
<!-- People fragment -->
|
<!-- People fragment -->
|
||||||
<string name="direct_chats_header">Conversations</string>
|
<string name="direct_chats_header">Conversations</string>
|
||||||
<string name="matrix_only_filter">Matrix contacts only</string>
|
<string name="matrix_only_filter">Matrix contacts only</string>
|
||||||
|
@ -3256,4 +3259,15 @@
|
||||||
<string name="home_empty_no_unreads_title">Nothing to report.</string>
|
<string name="home_empty_no_unreads_title">Nothing to report.</string>
|
||||||
<string name="home_empty_no_unreads_message">This is where your unread messages will show up, when you have some.</string>
|
<string name="home_empty_no_unreads_message">This is where your unread messages will show up, when you have some.</string>
|
||||||
|
|
||||||
|
<string name="onboarding_new_app_layout_welcome_title">Welcome to a new view!</string>
|
||||||
|
<!-- Note to translators: for RTL languages, menu will be at the bottom left. Please translate "bottom-left" instead of "bottom-right". Thanks!-->
|
||||||
|
<string name="onboarding_new_app_layout_welcome_message">To simplify your ${app_name}, tabs are now optional. Manage them using the top-right menu.</string>
|
||||||
|
<string name="onboarding_new_app_layout_spaces_title">Access Spaces</string>
|
||||||
|
<!-- Note to translators: for RTL languages, Spaces will be at the bottom left. Please translate "bottom-left" instead of "bottom-right". Thanks!-->
|
||||||
|
<string name="onboarding_new_app_layout_spaces_message">Access your Spaces (bottom-right) faster and easier than ever before.</string>
|
||||||
|
<string name="onboarding_new_app_layout_feedback_title">Give Feedback</string>
|
||||||
|
<!-- Note to translators: for RTL languages, context menu will be at top left corner instead of top right corner. Thanks!-->
|
||||||
|
<string name="onboarding_new_app_layout_feedback_message">Tap top right to see the option to feedback.</string>
|
||||||
|
<string name="onboarding_new_app_layout_button_try">Try it out</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -2,4 +2,8 @@
|
||||||
<resources>
|
<resources>
|
||||||
<item name="ftue_auth_carousel_item_spacing" format="float" type="dimen">0.05</item>
|
<item name="ftue_auth_carousel_item_spacing" format="float" type="dimen">0.05</item>
|
||||||
<item name="ftue_auth_carousel_item_image_height" format="float" type="dimen">0.40</item>
|
<item name="ftue_auth_carousel_item_image_height" format="float" type="dimen">0.40</item>
|
||||||
</resources>
|
|
||||||
|
<dimen name="release_notes_vertical_margin_small">16dp</dimen>
|
||||||
|
<dimen name="release_notes_vertical_margin">40dp</dimen>
|
||||||
|
<dimen name="release_notes_vertical_margin_large">46dp</dimen>
|
||||||
|
</resources>
|
||||||
|
|
|
@ -74,4 +74,9 @@
|
||||||
|
|
||||||
<!-- Material 3 -->
|
<!-- Material 3 -->
|
||||||
<dimen name="collapsing_toolbar_layout_medium_size">112dp</dimen>
|
<dimen name="collapsing_toolbar_layout_medium_size">112dp</dimen>
|
||||||
|
|
||||||
|
|
||||||
|
<dimen name="release_notes_vertical_margin_small">8dp</dimen>
|
||||||
|
<dimen name="release_notes_vertical_margin">16dp</dimen>
|
||||||
|
<dimen name="release_notes_vertical_margin_large">28dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -101,11 +101,11 @@ class UiAllScreensSanityTest {
|
||||||
|
|
||||||
val spaceName = UUID.randomUUID().toString()
|
val spaceName = UUID.randomUUID().toString()
|
||||||
elementRobot.space {
|
elementRobot.space {
|
||||||
createSpace {
|
createSpace(true) {
|
||||||
createAndCrawl(spaceName)
|
createAndCrawl(spaceName)
|
||||||
}
|
}
|
||||||
val publicSpaceName = UUID.randomUUID().toString()
|
val publicSpaceName = UUID.randomUUID().toString()
|
||||||
createSpace {
|
createSpace(false) {
|
||||||
createPublicSpace(publicSpaceName)
|
createPublicSpace(publicSpaceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,33 +84,56 @@ class ElementRobot {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun settings(shouldGoBack: Boolean = true, block: SettingsRobot.() -> Unit) {
|
fun settings(shouldGoBack: Boolean = true, block: SettingsRobot.() -> Unit) {
|
||||||
openDrawer()
|
if (features.isNewAppLayoutEnabled()) {
|
||||||
clickOn(R.id.homeDrawerHeaderSettingsView)
|
onView(withId((R.id.avatar))).perform(click())
|
||||||
|
} else {
|
||||||
|
openDrawer()
|
||||||
|
clickOn(R.id.homeDrawerHeaderSettingsView)
|
||||||
|
}
|
||||||
|
|
||||||
block(SettingsRobot())
|
block(SettingsRobot())
|
||||||
if (shouldGoBack) pressBack()
|
if (shouldGoBack) pressBack()
|
||||||
waitUntilViewVisible(withId(R.id.roomListContainer))
|
waitUntilViewVisible(withId(R.id.roomListContainer))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun newDirectMessage(block: NewDirectMessageRobot.() -> Unit) {
|
fun newDirectMessage(block: NewDirectMessageRobot.() -> Unit) {
|
||||||
clickOn(R.id.bottom_action_people)
|
if (features.isNewAppLayoutEnabled()) {
|
||||||
clickOn(R.id.createChatRoomButton)
|
clickOn(R.id.newLayoutCreateChatButton)
|
||||||
|
waitUntilDialogVisible(withId(R.id.start_chat))
|
||||||
|
clickOn(R.id.start_chat)
|
||||||
|
} else {
|
||||||
|
clickOn(R.id.bottom_action_people)
|
||||||
|
clickOn(R.id.createChatRoomButton)
|
||||||
|
}
|
||||||
|
|
||||||
waitUntilActivityVisible<CreateDirectRoomActivity> {
|
waitUntilActivityVisible<CreateDirectRoomActivity> {
|
||||||
waitUntilViewVisible(withId(R.id.userListSearch))
|
waitUntilViewVisible(withId(R.id.userListSearch))
|
||||||
}
|
}
|
||||||
closeSoftKeyboard()
|
closeSoftKeyboard()
|
||||||
block(NewDirectMessageRobot())
|
block(NewDirectMessageRobot())
|
||||||
pressBack()
|
pressBack()
|
||||||
|
if (features.isNewAppLayoutEnabled()) {
|
||||||
|
pressBack() // close create dialog
|
||||||
|
}
|
||||||
waitUntilViewVisible(withId(R.id.roomListContainer))
|
waitUntilViewVisible(withId(R.id.roomListContainer))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun newRoom(block: NewRoomRobot.() -> Unit) {
|
fun newRoom(block: NewRoomRobot.() -> Unit) {
|
||||||
clickOn(R.id.bottom_action_rooms)
|
if (!features.isNewAppLayoutEnabled()) {
|
||||||
|
clickOn(R.id.bottom_action_rooms)
|
||||||
|
}
|
||||||
RoomListRobot().newRoom { block() }
|
RoomListRobot().newRoom { block() }
|
||||||
|
if (features.isNewAppLayoutEnabled()) {
|
||||||
|
pressBack() // close create dialog
|
||||||
|
}
|
||||||
waitUntilViewVisible(withId(R.id.roomListContainer))
|
waitUntilViewVisible(withId(R.id.roomListContainer))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun roomList(block: RoomListRobot.() -> Unit) {
|
fun roomList(block: RoomListRobot.() -> Unit) {
|
||||||
clickOn(R.id.bottom_action_rooms)
|
if (!features.isNewAppLayoutEnabled()) {
|
||||||
|
clickOn(R.id.bottom_action_rooms)
|
||||||
|
}
|
||||||
|
|
||||||
block(RoomListRobot())
|
block(RoomListRobot())
|
||||||
waitUntilViewVisible(withId(R.id.roomListContainer))
|
waitUntilViewVisible(withId(R.id.roomListContainer))
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,19 @@ import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
|
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.espresso.tools.waitUntilViewVisible
|
import im.vector.app.espresso.tools.waitUntilViewVisible
|
||||||
|
import im.vector.app.features.DefaultVectorFeatures
|
||||||
|
import im.vector.app.features.VectorFeatures
|
||||||
|
|
||||||
class NewRoomRobot(
|
class NewRoomRobot(
|
||||||
var createdRoom: Boolean = false
|
var createdRoom: Boolean = false
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
var features: VectorFeatures = DefaultVectorFeatures()
|
||||||
|
|
||||||
fun createNewRoom(block: CreateNewRoomRobot.() -> Unit) {
|
fun createNewRoom(block: CreateNewRoomRobot.() -> Unit) {
|
||||||
clickOn(R.string.create_new_room)
|
if (features.isNewAppLayoutEnabled()) {
|
||||||
|
clickOn(R.string.create_new_room)
|
||||||
|
}
|
||||||
waitUntilViewVisible(withId(R.id.createRoomForm))
|
waitUntilViewVisible(withId(R.id.createRoomForm))
|
||||||
val createNewRoomRobot = CreateNewRoomRobot()
|
val createNewRoomRobot = CreateNewRoomRobot()
|
||||||
block(createNewRoomRobot)
|
block(createNewRoomRobot)
|
||||||
|
|
|
@ -27,10 +27,15 @@ import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions
|
||||||
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
|
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.espresso.tools.waitUntilActivityVisible
|
import im.vector.app.espresso.tools.waitUntilActivityVisible
|
||||||
|
import im.vector.app.espresso.tools.waitUntilDialogVisible
|
||||||
|
import im.vector.app.features.DefaultVectorFeatures
|
||||||
|
import im.vector.app.features.VectorFeatures
|
||||||
import im.vector.app.features.roomdirectory.RoomDirectoryActivity
|
import im.vector.app.features.roomdirectory.RoomDirectoryActivity
|
||||||
|
|
||||||
class RoomListRobot {
|
class RoomListRobot {
|
||||||
|
|
||||||
|
var features: VectorFeatures = DefaultVectorFeatures()
|
||||||
|
|
||||||
fun openRoom(roomName: String, block: RoomDetailRobot.() -> Unit) {
|
fun openRoom(roomName: String, block: RoomDetailRobot.() -> Unit) {
|
||||||
clickOn(roomName)
|
clickOn(roomName)
|
||||||
block(RoomDetailRobot())
|
block(RoomDetailRobot())
|
||||||
|
@ -49,9 +54,15 @@ class RoomListRobot {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun newRoom(block: NewRoomRobot.() -> Unit) {
|
fun newRoom(block: NewRoomRobot.() -> Unit) {
|
||||||
clickOn(R.id.createGroupRoomButton)
|
if (features.isNewAppLayoutEnabled()) {
|
||||||
waitUntilActivityVisible<RoomDirectoryActivity> {
|
clickOn(R.id.newLayoutCreateChatButton)
|
||||||
BaristaVisibilityAssertions.assertDisplayed(R.id.publicRoomsList)
|
waitUntilDialogVisible(ViewMatchers.withId(R.id.create_room))
|
||||||
|
clickOn(R.id.create_room)
|
||||||
|
} else {
|
||||||
|
clickOn(R.id.createGroupRoomButton)
|
||||||
|
waitUntilActivityVisible<RoomDirectoryActivity> {
|
||||||
|
BaristaVisibilityAssertions.assertDisplayed(R.id.publicRoomsList)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val newRoomRobot = NewRoomRobot()
|
val newRoomRobot = NewRoomRobot()
|
||||||
block(newRoomRobot)
|
block(newRoomRobot)
|
||||||
|
|
|
@ -31,6 +31,7 @@ import im.vector.app.espresso.tools.waitUntilActivityVisible
|
||||||
import im.vector.app.espresso.tools.waitUntilDialogVisible
|
import im.vector.app.espresso.tools.waitUntilDialogVisible
|
||||||
import im.vector.app.espresso.tools.waitUntilViewVisible
|
import im.vector.app.espresso.tools.waitUntilViewVisible
|
||||||
import im.vector.app.features.home.HomeActivity
|
import im.vector.app.features.home.HomeActivity
|
||||||
|
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
||||||
import im.vector.app.features.spaces.manage.SpaceManageActivity
|
import im.vector.app.features.spaces.manage.SpaceManageActivity
|
||||||
|
|
||||||
class SpaceCreateRobot {
|
class SpaceCreateRobot {
|
||||||
|
@ -85,7 +86,9 @@ class SpaceCreateRobot {
|
||||||
clickOn(R.id.nextButton)
|
clickOn(R.id.nextButton)
|
||||||
waitUntilViewVisible(withId(R.id.recyclerView))
|
waitUntilViewVisible(withId(R.id.recyclerView))
|
||||||
clickOn(R.id.nextButton)
|
clickOn(R.id.nextButton)
|
||||||
waitUntilDialogVisible(withId(R.id.inviteByMxidButton))
|
waitUntilActivityVisible<RoomDetailActivity> {
|
||||||
|
waitUntilDialogVisible(withId(R.id.inviteByMxidButton))
|
||||||
|
}
|
||||||
// close invite dialog
|
// close invite dialog
|
||||||
pressBack()
|
pressBack()
|
||||||
waitUntilViewVisible(withId(R.id.timelineRecyclerView))
|
waitUntilViewVisible(withId(R.id.timelineRecyclerView))
|
||||||
|
|
|
@ -18,6 +18,8 @@ package im.vector.app.ui.robot.space
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.test.espresso.Espresso
|
import androidx.test.espresso.Espresso
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.action.ViewActions.longClick
|
||||||
import androidx.test.espresso.contrib.RecyclerViewActions
|
import androidx.test.espresso.contrib.RecyclerViewActions
|
||||||
import androidx.test.espresso.matcher.ViewMatchers
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
|
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
|
||||||
|
@ -26,18 +28,44 @@ import com.adevinta.android.barista.internal.viewaction.ClickChildAction
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.espresso.tools.waitUntilDialogVisible
|
import im.vector.app.espresso.tools.waitUntilDialogVisible
|
||||||
import im.vector.app.espresso.tools.waitUntilViewVisible
|
import im.vector.app.espresso.tools.waitUntilViewVisible
|
||||||
|
import im.vector.app.features.DefaultVectorFeatures
|
||||||
|
import im.vector.app.features.VectorFeatures
|
||||||
import org.hamcrest.Matchers
|
import org.hamcrest.Matchers
|
||||||
|
|
||||||
class SpaceRobot {
|
class SpaceRobot {
|
||||||
|
|
||||||
fun createSpace(block: SpaceCreateRobot.() -> Unit) {
|
var features: VectorFeatures = DefaultVectorFeatures()
|
||||||
openDrawer()
|
|
||||||
clickOn(R.string.create_space)
|
fun createSpace(isFirstSpace: Boolean, block: SpaceCreateRobot.() -> Unit) {
|
||||||
|
if (features.isNewAppLayoutEnabled()) {
|
||||||
|
clickOn(R.id.newLayoutOpenSpacesButton)
|
||||||
|
if (isFirstSpace) {
|
||||||
|
waitUntilDialogVisible(ViewMatchers.withId(R.id.spaces_empty_group))
|
||||||
|
clickOn(R.id.spaces_empty_button)
|
||||||
|
} else {
|
||||||
|
waitUntilDialogVisible(ViewMatchers.withId(R.id.groupListView))
|
||||||
|
Espresso.onView(ViewMatchers.withId(R.id.groupListView))
|
||||||
|
.perform(
|
||||||
|
RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
|
||||||
|
ViewMatchers.hasDescendant(ViewMatchers.withId(R.id.plus)),
|
||||||
|
click()
|
||||||
|
).atPosition(0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
openDrawer()
|
||||||
|
clickOn(R.string.create_space)
|
||||||
|
}
|
||||||
block(SpaceCreateRobot())
|
block(SpaceCreateRobot())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun spaceMenu(spaceName: String, block: SpaceMenuRobot.() -> Unit) {
|
fun spaceMenu(spaceName: String, block: SpaceMenuRobot.() -> Unit) {
|
||||||
openDrawer()
|
if (features.isNewAppLayoutEnabled()) {
|
||||||
|
clickOn(R.id.newLayoutOpenSpacesButton)
|
||||||
|
waitUntilDialogVisible(ViewMatchers.withId(R.id.groupListView))
|
||||||
|
} else {
|
||||||
|
openDrawer()
|
||||||
|
}
|
||||||
with(SpaceMenuRobot()) {
|
with(SpaceMenuRobot()) {
|
||||||
openMenu(spaceName)
|
openMenu(spaceName)
|
||||||
block()
|
block()
|
||||||
|
@ -46,19 +74,32 @@ class SpaceRobot {
|
||||||
|
|
||||||
fun openMenu(spaceName: String) {
|
fun openMenu(spaceName: String) {
|
||||||
waitUntilViewVisible(ViewMatchers.withId(R.id.groupListView))
|
waitUntilViewVisible(ViewMatchers.withId(R.id.groupListView))
|
||||||
Espresso.onView(ViewMatchers.withId(R.id.groupListView))
|
if (features.isNewAppLayoutEnabled()) {
|
||||||
.perform(
|
Espresso.onView(ViewMatchers.withId(R.id.groupListView))
|
||||||
RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
|
.perform(
|
||||||
ViewMatchers.hasDescendant(Matchers.allOf(ViewMatchers.withId(R.id.groupNameView), ViewMatchers.withText(spaceName))),
|
RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
|
||||||
ClickChildAction.clickChildWithId(R.id.groupTmpLeave)
|
ViewMatchers.hasDescendant(Matchers.allOf(ViewMatchers.withId(R.id.name), ViewMatchers.withText(spaceName))),
|
||||||
).atPosition(0)
|
longClick()
|
||||||
)
|
).atPosition(0)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Espresso.onView(ViewMatchers.withId(R.id.groupListView))
|
||||||
|
.perform(
|
||||||
|
RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
|
||||||
|
ViewMatchers.hasDescendant(Matchers.allOf(ViewMatchers.withId(R.id.groupNameView), ViewMatchers.withText(spaceName))),
|
||||||
|
ClickChildAction.clickChildWithId(R.id.groupTmpLeave)
|
||||||
|
).atPosition(0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
waitUntilDialogVisible(ViewMatchers.withId(R.id.spaceNameView))
|
waitUntilDialogVisible(ViewMatchers.withId(R.id.spaceNameView))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectSpace(spaceName: String) {
|
fun selectSpace(spaceName: String) {
|
||||||
openDrawer()
|
if (!features.isNewAppLayoutEnabled()) {
|
||||||
waitUntilViewVisible(ViewMatchers.withId(R.id.groupListView))
|
openDrawer()
|
||||||
|
waitUntilViewVisible(ViewMatchers.withId(R.id.groupListView))
|
||||||
|
}
|
||||||
clickOn(spaceName)
|
clickOn(spaceName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -338,6 +338,7 @@
|
||||||
<activity android:name=".features.settings.font.FontScaleSettingActivity"/>
|
<activity android:name=".features.settings.font.FontScaleSettingActivity"/>
|
||||||
<activity android:name=".features.call.dialpad.PstnDialActivity" />
|
<activity android:name=".features.call.dialpad.PstnDialActivity" />
|
||||||
<activity android:name=".features.home.room.list.home.invites.InvitesActivity"/>
|
<activity android:name=".features.home.room.list.home.invites.InvitesActivity"/>
|
||||||
|
<activity android:name=".features.home.room.list.home.release.ReleaseNotesActivity"/>
|
||||||
|
|
||||||
<!-- Services -->
|
<!-- Services -->
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ import im.vector.app.features.home.room.detail.upgrade.MigrateRoomViewModel
|
||||||
import im.vector.app.features.home.room.list.RoomListViewModel
|
import im.vector.app.features.home.room.list.RoomListViewModel
|
||||||
import im.vector.app.features.home.room.list.home.HomeRoomListViewModel
|
import im.vector.app.features.home.room.list.home.HomeRoomListViewModel
|
||||||
import im.vector.app.features.home.room.list.home.invites.InvitesViewModel
|
import im.vector.app.features.home.room.list.home.invites.InvitesViewModel
|
||||||
|
import im.vector.app.features.home.room.list.home.release.ReleaseNotesViewModel
|
||||||
import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel
|
import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel
|
||||||
import im.vector.app.features.invite.InviteUsersToRoomViewModel
|
import im.vector.app.features.invite.InviteUsersToRoomViewModel
|
||||||
import im.vector.app.features.location.LocationSharingViewModel
|
import im.vector.app.features.location.LocationSharingViewModel
|
||||||
|
@ -624,4 +625,9 @@ interface MavericksViewModelModule {
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@MavericksViewModelKey(InvitesViewModel::class)
|
@MavericksViewModelKey(InvitesViewModel::class)
|
||||||
fun invitesViewModel(factory: InvitesViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
fun invitesViewModel(factory: InvitesViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@MavericksViewModelKey(ReleaseNotesViewModel::class)
|
||||||
|
fun releaseNotesViewModel(factory: ReleaseNotesViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ import im.vector.app.features.disclaimer.showDisclaimerDialog
|
||||||
import im.vector.app.features.home.room.list.actions.RoomListSharedAction
|
import im.vector.app.features.home.room.list.actions.RoomListSharedAction
|
||||||
import im.vector.app.features.home.room.list.actions.RoomListSharedActionViewModel
|
import im.vector.app.features.home.room.list.actions.RoomListSharedActionViewModel
|
||||||
import im.vector.app.features.home.room.list.home.layout.HomeLayoutSettingBottomDialogFragment
|
import im.vector.app.features.home.room.list.home.layout.HomeLayoutSettingBottomDialogFragment
|
||||||
|
import im.vector.app.features.home.room.list.home.release.ReleaseNotesActivity
|
||||||
import im.vector.app.features.matrixto.MatrixToBottomSheet
|
import im.vector.app.features.matrixto.MatrixToBottomSheet
|
||||||
import im.vector.app.features.matrixto.OriginOfMatrixTo
|
import im.vector.app.features.matrixto.OriginOfMatrixTo
|
||||||
import im.vector.app.features.navigation.Navigator
|
import im.vector.app.features.navigation.Navigator
|
||||||
|
@ -268,6 +269,7 @@ class HomeActivity :
|
||||||
}
|
}
|
||||||
is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
|
is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
|
||||||
HomeActivityViewEvents.ShowAnalyticsOptIn -> handleShowAnalyticsOptIn()
|
HomeActivityViewEvents.ShowAnalyticsOptIn -> handleShowAnalyticsOptIn()
|
||||||
|
HomeActivityViewEvents.ShowReleaseNotes -> handleShowReleaseNotes()
|
||||||
HomeActivityViewEvents.NotifyUserForThreadsMigration -> handleNotifyUserForThreadsMigration()
|
HomeActivityViewEvents.NotifyUserForThreadsMigration -> handleNotifyUserForThreadsMigration()
|
||||||
is HomeActivityViewEvents.MigrateThreads -> migrateThreadsIfNeeded(it.checkSession)
|
is HomeActivityViewEvents.MigrateThreads -> migrateThreadsIfNeeded(it.checkSession)
|
||||||
}
|
}
|
||||||
|
@ -282,6 +284,10 @@ class HomeActivity :
|
||||||
homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted)
|
homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleShowReleaseNotes() {
|
||||||
|
startActivity(Intent(this, ReleaseNotesActivity::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
private fun showSpaceSettings(spaceId: String) {
|
private fun showSpaceSettings(spaceId: String) {
|
||||||
// open bottom sheet
|
// open bottom sheet
|
||||||
SpaceSettingsMenuBottomSheet
|
SpaceSettingsMenuBottomSheet
|
||||||
|
|
|
@ -31,6 +31,7 @@ sealed interface HomeActivityViewEvents : VectorViewEvents {
|
||||||
data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents
|
data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents
|
||||||
object PromptToEnableSessionPush : HomeActivityViewEvents
|
object PromptToEnableSessionPush : HomeActivityViewEvents
|
||||||
object ShowAnalyticsOptIn : HomeActivityViewEvents
|
object ShowAnalyticsOptIn : HomeActivityViewEvents
|
||||||
|
object ShowReleaseNotes : HomeActivityViewEvents
|
||||||
object NotifyUserForThreadsMigration : HomeActivityViewEvents
|
object NotifyUserForThreadsMigration : HomeActivityViewEvents
|
||||||
data class MigrateThreads(val checkSession: Boolean) : HomeActivityViewEvents
|
data class MigrateThreads(val checkSession: Boolean) : HomeActivityViewEvents
|
||||||
object StartRecoverySetupFlow : HomeActivityViewEvents
|
object StartRecoverySetupFlow : HomeActivityViewEvents
|
||||||
|
|
|
@ -26,11 +26,13 @@ import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.features.VectorFeatures
|
||||||
import im.vector.app.features.analytics.AnalyticsConfig
|
import im.vector.app.features.analytics.AnalyticsConfig
|
||||||
import im.vector.app.features.analytics.AnalyticsTracker
|
import im.vector.app.features.analytics.AnalyticsTracker
|
||||||
import im.vector.app.features.analytics.extensions.toAnalyticsType
|
import im.vector.app.features.analytics.extensions.toAnalyticsType
|
||||||
import im.vector.app.features.analytics.plan.Signup
|
import im.vector.app.features.analytics.plan.Signup
|
||||||
import im.vector.app.features.analytics.store.AnalyticsStore
|
import im.vector.app.features.analytics.store.AnalyticsStore
|
||||||
|
import im.vector.app.features.home.room.list.home.release.ReleaseNotesPreferencesStore
|
||||||
import im.vector.app.features.login.ReAuthHelper
|
import im.vector.app.features.login.ReAuthHelper
|
||||||
import im.vector.app.features.onboarding.AuthenticationDescription
|
import im.vector.app.features.onboarding.AuthenticationDescription
|
||||||
import im.vector.app.features.raw.wellknown.ElementWellKnown
|
import im.vector.app.features.raw.wellknown.ElementWellKnown
|
||||||
|
@ -82,6 +84,8 @@ class HomeActivityViewModel @AssistedInject constructor(
|
||||||
private val vectorPreferences: VectorPreferences,
|
private val vectorPreferences: VectorPreferences,
|
||||||
private val analyticsTracker: AnalyticsTracker,
|
private val analyticsTracker: AnalyticsTracker,
|
||||||
private val analyticsConfig: AnalyticsConfig,
|
private val analyticsConfig: AnalyticsConfig,
|
||||||
|
private val releaseNotesPreferencesStore: ReleaseNotesPreferencesStore,
|
||||||
|
private val vectorFeatures: VectorFeatures,
|
||||||
) : VectorViewModel<HomeActivityViewState, HomeActivityViewActions, HomeActivityViewEvents>(initialState) {
|
) : VectorViewModel<HomeActivityViewState, HomeActivityViewActions, HomeActivityViewEvents>(initialState) {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
|
@ -110,9 +114,27 @@ class HomeActivityViewModel @AssistedInject constructor(
|
||||||
checkSessionPushIsOn()
|
checkSessionPushIsOn()
|
||||||
observeCrossSigningReset()
|
observeCrossSigningReset()
|
||||||
observeAnalytics()
|
observeAnalytics()
|
||||||
|
observeReleaseNotes()
|
||||||
initThreadsMigration()
|
initThreadsMigration()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun observeReleaseNotes() = withState { state ->
|
||||||
|
// we don't want to show release notes for new users or after relogin
|
||||||
|
if (state.authenticationDescription == null && vectorFeatures.isNewAppLayoutEnabled()) {
|
||||||
|
releaseNotesPreferencesStore.appLayoutOnboardingShown.onEach { isAppLayoutOnboardingShown ->
|
||||||
|
if (!isAppLayoutOnboardingShown) {
|
||||||
|
releaseNotesPreferencesStore.setAppLayoutOnboardingShown(true)
|
||||||
|
_viewEvents.post(HomeActivityViewEvents.ShowReleaseNotes)
|
||||||
|
}
|
||||||
|
}.launchIn(viewModelScope)
|
||||||
|
} else {
|
||||||
|
// we assume that users which came from auth flow either have seen updates already (relogin) or don't need them (new user)
|
||||||
|
viewModelScope.launch {
|
||||||
|
releaseNotesPreferencesStore.setAppLayoutOnboardingShown(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun observeAnalytics() {
|
private fun observeAnalytics() {
|
||||||
if (analyticsConfig.isEnabled) {
|
if (analyticsConfig.isEnabled) {
|
||||||
analyticsStore.didAskUserConsentFlow
|
analyticsStore.didAskUserConsentFlow
|
||||||
|
|
|
@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
@ -202,24 +203,38 @@ class HomeRoomListViewModel @AssistedInject constructor(
|
||||||
private fun getFiltersDataFlow(): SharedFlow<Optional<List<HomeRoomFilter>>> {
|
private fun getFiltersDataFlow(): SharedFlow<Optional<List<HomeRoomFilter>>> {
|
||||||
val flow = MutableSharedFlow<Optional<List<HomeRoomFilter>>>(replay = 1)
|
val flow = MutableSharedFlow<Optional<List<HomeRoomFilter>>>(replay = 1)
|
||||||
|
|
||||||
val favouritesFlow = session.flow()
|
val spaceFLow = spaceStateHandler.getSelectedSpaceFlow()
|
||||||
.liveRoomSummaries(
|
|
||||||
RoomSummaryQueryParams.Builder().also { builder ->
|
|
||||||
builder.roomTagQueryFilter = RoomTagQueryFilter(true, null, null)
|
|
||||||
}.build()
|
|
||||||
)
|
|
||||||
.map { it.isNotEmpty() }
|
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
|
.onStart {
|
||||||
|
emit(spaceStateHandler.getCurrentSpace().toOption())
|
||||||
|
}
|
||||||
|
|
||||||
val dmsFLow = session.flow()
|
val favouritesFlow =
|
||||||
.liveRoomSummaries(
|
spaceFLow.flatMapLatest { selectedSpace ->
|
||||||
RoomSummaryQueryParams.Builder().also { builder ->
|
session.flow()
|
||||||
builder.memberships = listOf(Membership.JOIN)
|
.liveRoomSummaries(
|
||||||
builder.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
|
RoomSummaryQueryParams.Builder().also { builder ->
|
||||||
}.build()
|
builder.spaceFilter = selectedSpace.orNull()?.roomId.toActiveSpaceOrNoFilter()
|
||||||
)
|
builder.roomTagQueryFilter = RoomTagQueryFilter(true, null, null)
|
||||||
.map { it.isNotEmpty() }
|
}.build()
|
||||||
.distinctUntilChanged()
|
)
|
||||||
|
}
|
||||||
|
.map { it.isNotEmpty() }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
|
||||||
|
val dmsFLow =
|
||||||
|
spaceFLow.flatMapLatest { selectedSpace ->
|
||||||
|
session.flow()
|
||||||
|
.liveRoomSummaries(
|
||||||
|
RoomSummaryQueryParams.Builder().also { builder ->
|
||||||
|
builder.spaceFilter = selectedSpace.orNull()?.roomId.toActiveSpaceOrNoFilter()
|
||||||
|
builder.memberships = listOf(Membership.JOIN)
|
||||||
|
builder.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
|
||||||
|
}.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.map { it.isNotEmpty() }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
|
||||||
combine(favouritesFlow, dmsFLow, preferencesStore.areFiltersEnabledFlow) { hasFavourite, hasDm, areFiltersEnabled ->
|
combine(favouritesFlow, dmsFLow, preferencesStore.areFiltersEnabledFlow) { hasFavourite, hasDm, areFiltersEnabled ->
|
||||||
Triple(hasFavourite, hasDm, areFiltersEnabled)
|
Triple(hasFavourite, hasDm, areFiltersEnabled)
|
||||||
|
|
|
@ -20,15 +20,18 @@ import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.extensions.configureWith
|
import im.vector.app.core.extensions.configureWith
|
||||||
|
import im.vector.app.core.platform.StateView
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.databinding.FragmentInvitesBinding
|
import im.vector.app.databinding.FragmentInvitesBinding
|
||||||
import im.vector.app.features.analytics.plan.ViewRoom
|
import im.vector.app.features.analytics.plan.ViewRoom
|
||||||
import im.vector.app.features.home.room.list.RoomListListener
|
import im.vector.app.features.home.room.list.RoomListListener
|
||||||
import im.vector.app.features.notifications.NotificationDrawerManager
|
import im.vector.app.features.notifications.NotificationDrawerManager
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -51,6 +54,8 @@ class InvitesFragment : VectorBaseFragment<FragmentInvitesBinding>(), RoomListLi
|
||||||
setupToolbar(views.invitesToolbar)
|
setupToolbar(views.invitesToolbar)
|
||||||
.allowBack()
|
.allowBack()
|
||||||
|
|
||||||
|
views.invitesStateView.contentView = views.invitesRecycler
|
||||||
|
|
||||||
views.invitesRecycler.configureWith(controller)
|
views.invitesRecycler.configureWith(controller)
|
||||||
controller.listener = this
|
controller.listener = this
|
||||||
|
|
||||||
|
@ -62,13 +67,31 @@ class InvitesFragment : VectorBaseFragment<FragmentInvitesBinding>(), RoomListLi
|
||||||
when (it) {
|
when (it) {
|
||||||
is InvitesViewEvents.Failure -> showFailure(it.throwable)
|
is InvitesViewEvents.Failure -> showFailure(it.throwable)
|
||||||
is InvitesViewEvents.OpenRoom -> handleOpenRoom(it.roomSummary, it.shouldCloseInviteView)
|
is InvitesViewEvents.OpenRoom -> handleOpenRoom(it.roomSummary, it.shouldCloseInviteView)
|
||||||
InvitesViewEvents.Close -> handleClose()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleClose() {
|
viewModel.invites.onEach {
|
||||||
requireActivity().finish()
|
when (it) {
|
||||||
|
is InvitesContentState.Content -> {
|
||||||
|
views.invitesStateView.state = StateView.State.Content
|
||||||
|
controller.submitList(it.content)
|
||||||
|
}
|
||||||
|
is InvitesContentState.Empty -> {
|
||||||
|
views.invitesStateView.state = StateView.State.Empty(
|
||||||
|
title = it.title,
|
||||||
|
image = it.image,
|
||||||
|
message = it.message
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is InvitesContentState.Error -> {
|
||||||
|
when (views.invitesStateView.state) {
|
||||||
|
StateView.State.Content -> showErrorInSnackbar(it.throwable)
|
||||||
|
else -> views.invitesStateView.state = StateView.State.Error(it.throwable.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InvitesContentState.Loading -> views.invitesStateView.state = StateView.State.Loading
|
||||||
|
}
|
||||||
|
}.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleOpenRoom(roomSummary: RoomSummary, shouldCloseInviteView: Boolean) {
|
private fun handleOpenRoom(roomSummary: RoomSummary, shouldCloseInviteView: Boolean) {
|
||||||
|
@ -83,14 +106,6 @@ class InvitesFragment : VectorBaseFragment<FragmentInvitesBinding>(), RoomListLi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate(): Unit = withState(viewModel) { state ->
|
|
||||||
super.invalidate()
|
|
||||||
|
|
||||||
state.pagedList?.observe(viewLifecycleOwner) { list ->
|
|
||||||
controller.submitList(list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRejectRoomInvitation(room: RoomSummary) {
|
override fun onRejectRoomInvitation(room: RoomSummary) {
|
||||||
notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(room.roomId) }
|
notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(room.roomId) }
|
||||||
viewModel.handle(InvitesAction.RejectInvitation(room))
|
viewModel.handle(InvitesAction.RejectInvitation(room))
|
||||||
|
|
|
@ -22,5 +22,4 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
sealed class InvitesViewEvents : VectorViewEvents {
|
sealed class InvitesViewEvents : VectorViewEvents {
|
||||||
data class Failure(val throwable: Throwable) : InvitesViewEvents()
|
data class Failure(val throwable: Throwable) : InvitesViewEvents()
|
||||||
data class OpenRoom(val roomSummary: RoomSummary, val shouldCloseInviteView: Boolean) : InvitesViewEvents()
|
data class OpenRoom(val roomSummary: RoomSummary, val shouldCloseInviteView: Boolean) : InvitesViewEvents()
|
||||||
object Close : InvitesViewEvents()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,25 @@
|
||||||
|
|
||||||
package im.vector.app.features.home.room.list.home.invites
|
package im.vector.app.features.home.room.list.home.invites
|
||||||
|
|
||||||
|
import androidx.lifecycle.asFlow
|
||||||
import androidx.paging.PagedList
|
import androidx.paging.PagedList
|
||||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.core.resources.DrawableProvider
|
||||||
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
@ -36,6 +47,8 @@ import timber.log.Timber
|
||||||
class InvitesViewModel @AssistedInject constructor(
|
class InvitesViewModel @AssistedInject constructor(
|
||||||
@Assisted val initialState: InvitesViewState,
|
@Assisted val initialState: InvitesViewState,
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
|
private val drawableProvider: DrawableProvider
|
||||||
) : VectorViewModel<InvitesViewState, InvitesAction, InvitesViewEvents>(initialState) {
|
) : VectorViewModel<InvitesViewState, InvitesAction, InvitesViewEvents>(initialState) {
|
||||||
|
|
||||||
private val pagedListConfig = PagedList.Config.Builder()
|
private val pagedListConfig = PagedList.Config.Builder()
|
||||||
|
@ -52,6 +65,11 @@ class InvitesViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
companion object : MavericksViewModelFactory<InvitesViewModel, InvitesViewState> by hiltMavericksViewModelFactory()
|
companion object : MavericksViewModelFactory<InvitesViewModel, InvitesViewState> by hiltMavericksViewModelFactory()
|
||||||
|
|
||||||
|
private val _invites = MutableSharedFlow<InvitesContentState>(replay = 1)
|
||||||
|
val invites = _invites.asSharedFlow()
|
||||||
|
|
||||||
|
private var invitesCount = -1
|
||||||
|
|
||||||
init {
|
init {
|
||||||
observeInvites()
|
observeInvites()
|
||||||
}
|
}
|
||||||
|
@ -72,8 +90,6 @@ class InvitesViewModel @AssistedInject constructor(
|
||||||
return@withState
|
return@withState
|
||||||
}
|
}
|
||||||
|
|
||||||
val shouldCloseInviteView = state.pagedList?.value?.size == 1
|
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
session.roomService().leaveRoom(roomId)
|
session.roomService().leaveRoom(roomId)
|
||||||
|
@ -81,9 +97,6 @@ class InvitesViewModel @AssistedInject constructor(
|
||||||
// Instead, we wait for the room to be rejected
|
// Instead, we wait for the room to be rejected
|
||||||
// Known bug: if the user is invited again (after rejecting the first invitation), the loading will be displayed instead of the buttons.
|
// Known bug: if the user is invited again (after rejecting the first invitation), the loading will be displayed instead of the buttons.
|
||||||
// If we update the state, the button will be displayed again, so it's not ideal...
|
// If we update the state, the button will be displayed again, so it's not ideal...
|
||||||
if (shouldCloseInviteView) {
|
|
||||||
_viewEvents.post(InvitesViewEvents.Close)
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
// Notify the user
|
// Notify the user
|
||||||
_viewEvents.post(InvitesViewEvents.Failure(failure))
|
_viewEvents.post(InvitesViewEvents.Failure(failure))
|
||||||
|
@ -101,9 +114,7 @@ class InvitesViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
// close invites view when navigate to a room from the last one invite
|
// close invites view when navigate to a room from the last one invite
|
||||||
|
|
||||||
val shouldCloseInviteView = state.pagedList?.value?.size == 1
|
val shouldCloseInviteView = invitesCount == 1
|
||||||
|
|
||||||
_viewEvents.post(InvitesViewEvents.OpenRoom(action.roomSummary, shouldCloseInviteView))
|
|
||||||
|
|
||||||
// quick echo
|
// quick echo
|
||||||
setState {
|
setState {
|
||||||
|
@ -117,6 +128,8 @@ class InvitesViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_viewEvents.post(InvitesViewEvents.OpenRoom(action.roomSummary, shouldCloseInviteView))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeInvites() {
|
private fun observeInvites() {
|
||||||
|
@ -129,8 +142,26 @@ class InvitesViewModel @AssistedInject constructor(
|
||||||
sortOrder = RoomSortOrder.ACTIVITY
|
sortOrder = RoomSortOrder.ACTIVITY
|
||||||
)
|
)
|
||||||
|
|
||||||
setState {
|
pagedList.asFlow()
|
||||||
copy(pagedList = pagedList)
|
.map {
|
||||||
}
|
if (it.isEmpty()) {
|
||||||
|
InvitesContentState.Empty(
|
||||||
|
title = stringProvider.getString(R.string.invites_empty_title),
|
||||||
|
image = drawableProvider.getDrawable(R.drawable.ic_invites_empty),
|
||||||
|
message = stringProvider.getString(R.string.invites_empty_message)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
invitesCount = it.loadedCount
|
||||||
|
InvitesContentState.Content(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.catch {
|
||||||
|
emit(InvitesContentState.Error(it))
|
||||||
|
}
|
||||||
|
.onStart {
|
||||||
|
emit(InvitesContentState.Loading)
|
||||||
|
}.onEach {
|
||||||
|
_invites.emit(it)
|
||||||
|
}.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,24 @@
|
||||||
|
|
||||||
package im.vector.app.features.home.room.list.home.invites
|
package im.vector.app.features.home.room.list.home.invites
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import android.graphics.drawable.Drawable
|
||||||
import androidx.paging.PagedList
|
import androidx.paging.PagedList
|
||||||
import com.airbnb.mvrx.MavericksState
|
import com.airbnb.mvrx.MavericksState
|
||||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
|
||||||
data class InvitesViewState(
|
data class InvitesViewState(
|
||||||
val pagedList: LiveData<PagedList<RoomSummary>>? = null,
|
|
||||||
val roomMembershipChanges: Map<String, ChangeMembershipState> = emptyMap(),
|
val roomMembershipChanges: Map<String, ChangeMembershipState> = emptyMap(),
|
||||||
) : MavericksState
|
) : MavericksState
|
||||||
|
|
||||||
|
sealed interface InvitesContentState {
|
||||||
|
object Loading : InvitesContentState
|
||||||
|
data class Empty(
|
||||||
|
val title: CharSequence,
|
||||||
|
val image: Drawable?,
|
||||||
|
val message: CharSequence
|
||||||
|
) : InvitesContentState
|
||||||
|
|
||||||
|
data class Content(val content: PagedList<RoomSummary>) : InvitesContentState
|
||||||
|
data class Error(val throwable: Throwable) : InvitesContentState
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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 im.vector.app.features.home.room.list.home.release
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
|
||||||
|
class ReleaseCarouselData(
|
||||||
|
val items: List<Item>
|
||||||
|
) {
|
||||||
|
data class Item(
|
||||||
|
@StringRes val title: Int,
|
||||||
|
@StringRes val body: Int,
|
||||||
|
@DrawableRes val image: Int,
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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 im.vector.app.features.home.room.list.home.release
|
||||||
|
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
|
|
||||||
|
@EpoxyModelClass
|
||||||
|
abstract class ReleaseCarouselItem : VectorEpoxyModel<ReleaseCarouselItem.Holder>(R.layout.item_release_carousel) {
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
lateinit var item: ReleaseCarouselData.Item
|
||||||
|
|
||||||
|
override fun bind(holder: Holder) {
|
||||||
|
super.bind(holder)
|
||||||
|
|
||||||
|
holder.image.setImageResource(item.image)
|
||||||
|
holder.title.setText(item.title)
|
||||||
|
holder.body.setText(item.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
val image by bind<ImageView>(R.id.carousel_item_image)
|
||||||
|
val title by bind<TextView>(R.id.carousel_item_title)
|
||||||
|
val body by bind<TextView>(R.id.carousel_item_body)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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 im.vector.app.features.home.room.list.home.release
|
||||||
|
|
||||||
|
import im.vector.app.core.platform.VectorViewModelAction
|
||||||
|
|
||||||
|
sealed class ReleaseNotesAction : VectorViewModelAction {
|
||||||
|
data class NextPressed(
|
||||||
|
val isLastItemSelected: Boolean = false
|
||||||
|
) : ReleaseNotesAction()
|
||||||
|
data class PageSelected(
|
||||||
|
val selectedPageIndex: Int = 0
|
||||||
|
) : ReleaseNotesAction()
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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 im.vector.app.features.home.room.list.home.release
|
||||||
|
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import im.vector.app.core.extensions.addFragment
|
||||||
|
import im.vector.app.core.platform.ScreenOrientationLocker
|
||||||
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
|
import im.vector.app.databinding.ActivitySimpleBinding
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class ReleaseNotesActivity : VectorBaseActivity<ActivitySimpleBinding>() {
|
||||||
|
|
||||||
|
@Inject lateinit var orientationLocker: ScreenOrientationLocker
|
||||||
|
|
||||||
|
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
|
||||||
|
override fun initUiAndData() {
|
||||||
|
orientationLocker.lockPhonesToPortrait(this)
|
||||||
|
if (isFirstCreation()) {
|
||||||
|
addFragment(views.simpleFragmentContainer, ReleaseNotesFragment::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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 im.vector.app.features.home.room.list.home.release
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class ReleaseNotesCarouselController @Inject constructor() : TypedEpoxyController<ReleaseCarouselData>() {
|
||||||
|
override fun buildModels(data: ReleaseCarouselData) {
|
||||||
|
data.items.forEachIndexed { index, item ->
|
||||||
|
releaseCarouselItem {
|
||||||
|
id(index)
|
||||||
|
item(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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 im.vector.app.features.home.room.list.home.release
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.onClick
|
||||||
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
|
import im.vector.app.databinding.BottomSheetReleaseNotesBinding
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class ReleaseNotesFragment : VectorBaseFragment<BottomSheetReleaseNotesBinding>() {
|
||||||
|
|
||||||
|
@Inject lateinit var carouselController: ReleaseNotesCarouselController
|
||||||
|
private var tabLayoutMediator: TabLayoutMediator? = null
|
||||||
|
|
||||||
|
private val viewModel by fragmentViewModel(ReleaseNotesViewModel::class)
|
||||||
|
|
||||||
|
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetReleaseNotesBinding {
|
||||||
|
return BottomSheetReleaseNotesBinding.inflate(inflater, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
val carouselAdapter = carouselController.adapter
|
||||||
|
views.releaseNotesCarousel.adapter = carouselAdapter
|
||||||
|
|
||||||
|
tabLayoutMediator = TabLayoutMediator(views.releaseNotesCarouselIndicator, views.releaseNotesCarousel) { _, _ -> }
|
||||||
|
.also { it.attach() }
|
||||||
|
|
||||||
|
val pageCallback = object : ViewPager2.OnPageChangeCallback() {
|
||||||
|
override fun onPageSelected(position: Int) {
|
||||||
|
viewModel.handle(ReleaseNotesAction.PageSelected(position))
|
||||||
|
updateButtonText(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
|
||||||
|
override fun onCreate(owner: LifecycleOwner) {
|
||||||
|
views.releaseNotesCarousel.registerOnPageChangeCallback(pageCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy(owner: LifecycleOwner) {
|
||||||
|
views.releaseNotesCarousel.unregisterOnPageChangeCallback(pageCallback)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
carouselController.setData(createCarouselData())
|
||||||
|
|
||||||
|
views.releaseNotesBtnClose.onClick { close() }
|
||||||
|
views.releaseNotesButtonNext.onClick {
|
||||||
|
val isLastItemSelected = with(views.releaseNotesCarouselIndicator) {
|
||||||
|
selectedTabPosition == tabCount - 1
|
||||||
|
}
|
||||||
|
viewModel.handle(ReleaseNotesAction.NextPressed(isLastItemSelected))
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.observeViewEvents {
|
||||||
|
when (it) {
|
||||||
|
is ReleaseNotesViewEvents.SelectPage -> selectPage(it.index)
|
||||||
|
ReleaseNotesViewEvents.Close -> close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createCarouselData(): ReleaseCarouselData {
|
||||||
|
return ReleaseCarouselData(
|
||||||
|
listOf(
|
||||||
|
ReleaseCarouselData.Item(
|
||||||
|
R.string.onboarding_new_app_layout_welcome_title,
|
||||||
|
R.string.onboarding_new_app_layout_welcome_message,
|
||||||
|
R.drawable.ill_app_layout_onboarding_rooms
|
||||||
|
),
|
||||||
|
ReleaseCarouselData.Item(
|
||||||
|
R.string.onboarding_new_app_layout_spaces_title,
|
||||||
|
R.string.onboarding_new_app_layout_spaces_message,
|
||||||
|
R.drawable.ill_app_layout_onboarding_spaces
|
||||||
|
),
|
||||||
|
ReleaseCarouselData.Item(
|
||||||
|
R.string.onboarding_new_app_layout_feedback_title,
|
||||||
|
R.string.onboarding_new_app_layout_feedback_message,
|
||||||
|
R.drawable.ill_app_layout_onboarding_rooms
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun close() {
|
||||||
|
requireActivity().finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectPage(index: Int) {
|
||||||
|
views.releaseNotesCarouselIndicator.selectTab(views.releaseNotesCarouselIndicator.getTabAt(index))
|
||||||
|
updateButtonText(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateButtonText(selectedIndex: Int) {
|
||||||
|
val isLastItem = selectedIndex == views.releaseNotesCarouselIndicator.tabCount - 1
|
||||||
|
if (isLastItem) {
|
||||||
|
views.releaseNotesButtonNext.setText(R.string.onboarding_new_app_layout_button_try)
|
||||||
|
} else {
|
||||||
|
views.releaseNotesButtonNext.setText(R.string.action_next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
tabLayoutMediator?.detach()
|
||||||
|
tabLayoutMediator = null
|
||||||
|
|
||||||
|
views.releaseNotesCarousel.adapter = null
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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 im.vector.app.features.home.room.list.home.release
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||||
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "release_notes")
|
||||||
|
|
||||||
|
class ReleaseNotesPreferencesStore @Inject constructor(
|
||||||
|
private val context: Context
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val isAppLayoutOnboardingShown = booleanPreferencesKey("SETTINGS_APP_LAYOUT_ONBOARDING_SHOWN")
|
||||||
|
|
||||||
|
val appLayoutOnboardingShown: Flow<Boolean> = context.dataStore.data
|
||||||
|
.map { preferences -> preferences[isAppLayoutOnboardingShown].orFalse() }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
|
||||||
|
suspend fun setAppLayoutOnboardingShown(isShown: Boolean) {
|
||||||
|
context.dataStore.edit { settings ->
|
||||||
|
settings[isAppLayoutOnboardingShown] = isShown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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 im.vector.app.features.home.room.list.home.release
|
||||||
|
|
||||||
|
import im.vector.app.core.platform.VectorViewEvents
|
||||||
|
|
||||||
|
sealed class ReleaseNotesViewEvents : VectorViewEvents {
|
||||||
|
object Close : ReleaseNotesViewEvents()
|
||||||
|
data class SelectPage(val index: Int) : ReleaseNotesViewEvents()
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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 im.vector.app.features.home.room.list.home.release
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||||
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
|
import im.vector.app.core.platform.VectorDummyViewState
|
||||||
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
|
||||||
|
class ReleaseNotesViewModel @AssistedInject constructor(
|
||||||
|
@Assisted initialState: VectorDummyViewState,
|
||||||
|
) : VectorViewModel<VectorDummyViewState, ReleaseNotesAction, ReleaseNotesViewEvents>(initialState) {
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory : MavericksAssistedViewModelFactory<ReleaseNotesViewModel, VectorDummyViewState> {
|
||||||
|
override fun create(initialState: VectorDummyViewState): ReleaseNotesViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : MavericksViewModelFactory<ReleaseNotesViewModel, VectorDummyViewState> by hiltMavericksViewModelFactory()
|
||||||
|
|
||||||
|
private var selectedPageIndex = 0
|
||||||
|
|
||||||
|
init {
|
||||||
|
_viewEvents.post(ReleaseNotesViewEvents.SelectPage(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handle(action: ReleaseNotesAction) {
|
||||||
|
when (action) {
|
||||||
|
is ReleaseNotesAction.NextPressed -> handleNextPressed(action)
|
||||||
|
is ReleaseNotesAction.PageSelected -> handlePageSelected(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handlePageSelected(action: ReleaseNotesAction.PageSelected) {
|
||||||
|
selectedPageIndex = action.selectedPageIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleNextPressed(action: ReleaseNotesAction.NextPressed) {
|
||||||
|
if (action.isLastItemSelected) {
|
||||||
|
_viewEvents.post(ReleaseNotesViewEvents.Close)
|
||||||
|
} else {
|
||||||
|
_viewEvents.post(ReleaseNotesViewEvents.SelectPage(++selectedPageIndex))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -627,7 +627,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(OnboardingViewEvents.OnAccountCreated)
|
_viewEvents.post(OnboardingViewEvents.OnAccountCreated)
|
||||||
}
|
}
|
||||||
AuthenticationDescription.Login -> {
|
AuthenticationDescription.Login -> {
|
||||||
setState { copy(isLoading = false) }
|
setState { copy(isLoading = false, selectedAuthenticationState = SelectedAuthenticationState(authenticationDescription)) }
|
||||||
_viewEvents.post(OnboardingViewEvents.OnAccountSignedIn)
|
_viewEvents.post(OnboardingViewEvents.OnAccountSignedIn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import im.vector.app.core.time.Clock
|
||||||
import im.vector.app.core.utils.isAnimationEnabled
|
import im.vector.app.core.utils.isAnimationEnabled
|
||||||
import im.vector.app.features.MainActivity
|
import im.vector.app.features.MainActivity
|
||||||
import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity
|
import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity
|
||||||
|
import im.vector.app.features.home.room.list.home.release.ReleaseNotesActivity
|
||||||
import im.vector.app.features.pin.PinActivity
|
import im.vector.app.features.pin.PinActivity
|
||||||
import im.vector.app.features.signout.hard.SignedOutActivity
|
import im.vector.app.features.signout.hard.SignedOutActivity
|
||||||
import im.vector.app.features.themes.ThemeUtils
|
import im.vector.app.features.themes.ThemeUtils
|
||||||
|
@ -307,6 +308,7 @@ class PopupAlertManager @Inject constructor(
|
||||||
activity !is PinActivity &&
|
activity !is PinActivity &&
|
||||||
activity !is SignedOutActivity &&
|
activity !is SignedOutActivity &&
|
||||||
activity !is AnalyticsOptInActivity &&
|
activity !is AnalyticsOptInActivity &&
|
||||||
|
activity !is ReleaseNotesActivity &&
|
||||||
activity is VectorBaseActivity<*> &&
|
activity is VectorBaseActivity<*> &&
|
||||||
alert.shouldBeDisplayedIn.invoke(activity)
|
alert.shouldBeDisplayedIn.invoke(activity)
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,6 +165,11 @@ class VectorPreferences @Inject constructor(
|
||||||
const val SETTINGS_LABS_AUTO_REPORT_UISI = "SETTINGS_LABS_AUTO_REPORT_UISI"
|
const val SETTINGS_LABS_AUTO_REPORT_UISI = "SETTINGS_LABS_AUTO_REPORT_UISI"
|
||||||
const val SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME = "SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME"
|
const val SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME = "SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is not preference, but category on preferences screen which contains [SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME].
|
||||||
|
* Needed to show/hide this category, depending on visibility of [SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME]. */
|
||||||
|
const val SETTINGS_PREF_SPACE_CATEGORY = "SETTINGS_PREF_SPACE_CATEGORY"
|
||||||
|
|
||||||
private const val SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY = "SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY"
|
private const val SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY = "SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY"
|
||||||
private const val SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY = "SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY"
|
private const val SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY = "SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY"
|
||||||
private const val SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY = "SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY"
|
private const val SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY = "SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY"
|
||||||
|
|
|
@ -27,6 +27,7 @@ import im.vector.app.R
|
||||||
import im.vector.app.core.preference.VectorSwitchPreference
|
import im.vector.app.core.preference.VectorSwitchPreference
|
||||||
import im.vector.app.features.MainActivity
|
import im.vector.app.features.MainActivity
|
||||||
import im.vector.app.features.MainActivityArgs
|
import im.vector.app.features.MainActivityArgs
|
||||||
|
import im.vector.app.features.VectorFeatures
|
||||||
import im.vector.app.features.analytics.plan.MobileScreen
|
import im.vector.app.features.analytics.plan.MobileScreen
|
||||||
import im.vector.app.features.home.room.threads.ThreadsManager
|
import im.vector.app.features.home.room.threads.ThreadsManager
|
||||||
import org.matrix.android.sdk.api.settings.LightweightSettingsStorage
|
import org.matrix.android.sdk.api.settings.LightweightSettingsStorage
|
||||||
|
@ -39,6 +40,7 @@ class VectorSettingsLabsFragment :
|
||||||
@Inject lateinit var vectorPreferences: VectorPreferences
|
@Inject lateinit var vectorPreferences: VectorPreferences
|
||||||
@Inject lateinit var lightweightSettingsStorage: LightweightSettingsStorage
|
@Inject lateinit var lightweightSettingsStorage: LightweightSettingsStorage
|
||||||
@Inject lateinit var threadsManager: ThreadsManager
|
@Inject lateinit var threadsManager: ThreadsManager
|
||||||
|
@Inject lateinit var vectorFeatures: VectorFeatures
|
||||||
|
|
||||||
override var titleRes = R.string.room_settings_labs_pref_title
|
override var titleRes = R.string.room_settings_labs_pref_title
|
||||||
override val preferenceXmlRes = R.xml.vector_settings_labs
|
override val preferenceXmlRes = R.xml.vector_settings_labs
|
||||||
|
@ -72,6 +74,10 @@ class VectorSettingsLabsFragment :
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB)!!.let {
|
||||||
|
it.isVisible = !vectorFeatures.isNewAppLayoutEnabled()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -31,6 +31,7 @@ import im.vector.app.core.preference.VectorPreference
|
||||||
import im.vector.app.core.preference.VectorSwitchPreference
|
import im.vector.app.core.preference.VectorSwitchPreference
|
||||||
import im.vector.app.features.MainActivity
|
import im.vector.app.features.MainActivity
|
||||||
import im.vector.app.features.MainActivityArgs
|
import im.vector.app.features.MainActivityArgs
|
||||||
|
import im.vector.app.features.VectorFeatures
|
||||||
import im.vector.app.features.analytics.plan.MobileScreen
|
import im.vector.app.features.analytics.plan.MobileScreen
|
||||||
import im.vector.app.features.settings.font.FontScaleSettingActivity
|
import im.vector.app.features.settings.font.FontScaleSettingActivity
|
||||||
import im.vector.app.features.themes.ThemeUtils
|
import im.vector.app.features.themes.ThemeUtils
|
||||||
|
@ -44,6 +45,7 @@ class VectorSettingsPreferencesFragment :
|
||||||
|
|
||||||
@Inject lateinit var vectorPreferences: VectorPreferences
|
@Inject lateinit var vectorPreferences: VectorPreferences
|
||||||
@Inject lateinit var fontScalePreferences: FontScalePreferences
|
@Inject lateinit var fontScalePreferences: FontScalePreferences
|
||||||
|
@Inject lateinit var vectorFeatures: VectorFeatures
|
||||||
|
|
||||||
override var titleRes = R.string.settings_preferences
|
override var titleRes = R.string.settings_preferences
|
||||||
override val preferenceXmlRes = R.xml.vector_settings_preferences
|
override val preferenceXmlRes = R.xml.vector_settings_preferences
|
||||||
|
@ -99,6 +101,10 @@ class VectorSettingsPreferencesFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findPreference<Preference>(VectorPreferences.SETTINGS_PREF_SPACE_CATEGORY)!!.let { pref ->
|
||||||
|
pref.isVisible = !vectorFeatures.isNewAppLayoutEnabled()
|
||||||
|
}
|
||||||
|
|
||||||
// Url preview
|
// Url preview
|
||||||
/*
|
/*
|
||||||
TODO Note: we keep the setting client side for now
|
TODO Note: we keep the setting client side for now
|
||||||
|
|
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 52 KiB |
|
@ -0,0 +1,14 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="60dp"
|
||||||
|
android:height="60dp"
|
||||||
|
android:viewportWidth="60"
|
||||||
|
android:viewportHeight="60">
|
||||||
|
<path
|
||||||
|
android:pathData="M30,30m-30,0a30,30 0,1 1,60 0a30,30 0,1 1,-60 0"
|
||||||
|
android:fillColor="#E3E8F0"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M25.665,33.544L15.229,23.209L29.236,13.398C29.993,12.868 31.007,12.868 31.764,13.398L45.771,23.209L35.247,33.631L33.851,32.446C31.93,30.816 29.11,30.778 27.145,32.355L25.665,33.544ZM22.439,36.134L14,42.91V27.777L22.439,36.134ZM47,27.777V43.606L38.393,36.301L47,27.777ZM31.177,35.566L43.47,46H16.714L29.733,35.546C30.156,35.208 30.765,35.216 31.177,35.566Z"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#737D8C"
|
||||||
|
android:strokeColor="#737D8C"/>
|
||||||
|
</vector>
|
|
@ -28,4 +28,4 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="start" />
|
android:layout_gravity="start" />
|
||||||
|
|
||||||
</androidx.drawerlayout.widget.DrawerLayout>
|
</androidx.drawerlayout.widget.DrawerLayout>
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:background="?colorSurface">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
|
android:id="@+id/release_notes_btn_close"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="@null"
|
||||||
|
android:src="@drawable/ic_close_24dp"
|
||||||
|
android:tint="?vctr_content_secondary"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"/>
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/release_notes_carousel_indicator"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_marginBottom="@dimen/release_notes_vertical_margin_small"
|
||||||
|
android:background="@null"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/releaseNotesButtonNext"
|
||||||
|
app:tabBackground="@drawable/indicator_onboarding_carousel_selector"
|
||||||
|
app:tabGravity="center"
|
||||||
|
app:tabIndicatorHeight="0dp"
|
||||||
|
app:tabPaddingEnd="8dp"
|
||||||
|
app:tabPaddingStart="8dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/releaseNotesButtonNext"
|
||||||
|
style="@style/Widget.Vector.Button.Login"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginBottom="@dimen/release_notes_vertical_margin"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
tools:text="@string/action_next" />
|
||||||
|
|
||||||
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
|
android:id="@+id/release_notes_carousel"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="@dimen/release_notes_vertical_margin_small"
|
||||||
|
android:layout_marginBottom="@dimen/release_notes_vertical_margin"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/release_notes_carousel_indicator"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/release_notes_btn_close" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -20,17 +20,24 @@
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<im.vector.app.core.platform.StateView
|
||||||
android:id="@+id/invites_recycler"
|
android:id="@+id/invites_state_view"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:fastScrollEnabled="true"
|
|
||||||
android:overScrollMode="always"
|
|
||||||
android:scrollbars="vertical"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/appBarLayout" />
|
app:layout_constraintTop_toBottomOf="@id/appBarLayout">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/invites_recycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fastScrollEnabled="true"
|
||||||
|
android:overScrollMode="always"
|
||||||
|
android:scrollbars="vertical" />
|
||||||
|
|
||||||
|
</im.vector.app.core.platform.StateView>
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
android:id="@+id/groupListView"
|
android:id="@+id/groupListView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:minHeight="195dp"
|
||||||
android:overScrollMode="always"
|
android:overScrollMode="always"
|
||||||
tools:listitem="@layout/item_space" />
|
tools:listitem="@layout/item_space" />
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="bottom">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/splashCarouselGutterStart"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintGuide_percent="@dimen/ftue_auth_gutter_start_percent" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/splashCarouselGutterEnd"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintGuide_percent="@dimen/ftue_auth_gutter_end_percent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/carousel_item_image"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:layout_marginBottom="@dimen/release_notes_vertical_margin_large"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/splashCarouselGutterStart"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/splashCarouselGutterStart"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/carousel_item_title"
|
||||||
|
tools:src="@drawable/ill_app_layout_onboarding_rooms"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/carousel_item_title"
|
||||||
|
style="@style/Widget.Vector.TextView.Title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="?vctr_content_primary"
|
||||||
|
android:maxLines="2"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/carousel_item_body"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/splashCarouselGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/splashCarouselGutterStart"
|
||||||
|
tools:text="@string/onboarding_new_app_layout_welcome_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/carousel_item_body"
|
||||||
|
style="@style/Widget.Vector.TextView.Subtitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="?vctr_content_secondary"
|
||||||
|
android:maxLines="3"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/splashCarouselGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/splashCarouselGutterStart"
|
||||||
|
tools:text="@string/onboarding_new_app_layout_welcome_message" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 32 KiB |
|
@ -30,7 +30,9 @@
|
||||||
|
|
||||||
</im.vector.app.core.preference.VectorPreferenceCategory>
|
</im.vector.app.core.preference.VectorPreferenceCategory>
|
||||||
|
|
||||||
<im.vector.app.core.preference.VectorPreferenceCategory android:title="@string/spaces">
|
<im.vector.app.core.preference.VectorPreferenceCategory
|
||||||
|
android:title="@string/spaces"
|
||||||
|
android:key="SETTINGS_PREF_SPACE_CATEGORY">
|
||||||
|
|
||||||
<im.vector.app.core.preference.VectorSwitchPreference
|
<im.vector.app.core.preference.VectorSwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
|
|
|
@ -170,7 +170,7 @@ class OnboardingViewModelTest {
|
||||||
.assertStatesChanges(
|
.assertStatesChanges(
|
||||||
initialState,
|
initialState,
|
||||||
{ copy(isLoading = true) },
|
{ copy(isLoading = true) },
|
||||||
{ copy(isLoading = false) }
|
{ copy(isLoading = false, selectedAuthenticationState = SelectedAuthenticationState(description = AuthenticationDescription.Login)) }
|
||||||
)
|
)
|
||||||
.assertEvents(OnboardingViewEvents.OnAccountSignedIn)
|
.assertEvents(OnboardingViewEvents.OnAccountSignedIn)
|
||||||
.finish()
|
.finish()
|
||||||
|
@ -189,7 +189,7 @@ class OnboardingViewModelTest {
|
||||||
.assertStatesChanges(
|
.assertStatesChanges(
|
||||||
initialState,
|
initialState,
|
||||||
{ copy(isLoading = true) },
|
{ copy(isLoading = true) },
|
||||||
{ copy(isLoading = false) }
|
{ copy(isLoading = false, selectedAuthenticationState = SelectedAuthenticationState(description = AuthenticationDescription.Login)) }
|
||||||
)
|
)
|
||||||
.assertEvents(OnboardingViewEvents.OnAccountSignedIn)
|
.assertEvents(OnboardingViewEvents.OnAccountSignedIn)
|
||||||
.finish()
|
.finish()
|
||||||
|
@ -284,7 +284,7 @@ class OnboardingViewModelTest {
|
||||||
.assertStatesChanges(
|
.assertStatesChanges(
|
||||||
initialState,
|
initialState,
|
||||||
{ copy(isLoading = true) },
|
{ copy(isLoading = true) },
|
||||||
{ copy(isLoading = false) }
|
{ copy(isLoading = false, selectedAuthenticationState = SelectedAuthenticationState(description = AuthenticationDescription.Login)) }
|
||||||
)
|
)
|
||||||
.assertEvents(OnboardingViewEvents.OnAccountSignedIn)
|
.assertEvents(OnboardingViewEvents.OnAccountSignedIn)
|
||||||
.finish()
|
.finish()
|
||||||
|
@ -870,7 +870,7 @@ class OnboardingViewModelTest {
|
||||||
.assertStatesChanges(
|
.assertStatesChanges(
|
||||||
initialState,
|
initialState,
|
||||||
{ copy(isLoading = true) },
|
{ copy(isLoading = true) },
|
||||||
{ copy(isLoading = false) }
|
{ copy(isLoading = false, selectedAuthenticationState = SelectedAuthenticationState(description = AuthenticationDescription.Login)) }
|
||||||
)
|
)
|
||||||
.assertEvents(OnboardingViewEvents.OnAccountSignedIn)
|
.assertEvents(OnboardingViewEvents.OnAccountSignedIn)
|
||||||
.finish()
|
.finish()
|
||||||
|
|