mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
Feature | New page menu + new undo/redo buttons (#1196)
This commit is contained in:
parent
5bcc165fcd
commit
04ace2224f
26 changed files with 655 additions and 45 deletions
|
@ -79,6 +79,9 @@ open class ArchiveFragment : NavigationFragment(R.layout.fragment_archive) {
|
|||
vm.onBackButtonPressed()
|
||||
}.launchIn(lifecycleScope)
|
||||
|
||||
topToolbar.undo.invisible()
|
||||
topToolbar.redo.invisible()
|
||||
|
||||
with(bottomMenu) {
|
||||
update(COUNTER_INIT)
|
||||
findViewById<TextView>(R.id.btnPutBack).setOnClickListener {
|
||||
|
|
|
@ -47,8 +47,6 @@ import com.anytypeio.anytype.core_ui.features.page.TurnIntoActionReceiver
|
|||
import com.anytypeio.anytype.core_ui.features.page.scrollandmove.DefaultScrollAndMoveTargetDescriptor
|
||||
import com.anytypeio.anytype.core_ui.features.page.scrollandmove.ScrollAndMoveStateListener
|
||||
import com.anytypeio.anytype.core_ui.features.page.scrollandmove.ScrollAndMoveTargetHighlighter
|
||||
import com.anytypeio.anytype.core_ui.menu.DocumentPopUpMenu
|
||||
import com.anytypeio.anytype.core_ui.menu.ProfilePopUpMenu
|
||||
import com.anytypeio.anytype.core_ui.reactive.clicks
|
||||
import com.anytypeio.anytype.core_ui.reactive.layoutChanges
|
||||
import com.anytypeio.anytype.core_ui.tools.ClipboardInterceptor
|
||||
|
@ -85,6 +83,8 @@ import com.anytypeio.anytype.ui.page.modals.*
|
|||
import com.anytypeio.anytype.ui.page.modals.actions.BlockActionToolbarFactory
|
||||
import com.anytypeio.anytype.ui.page.modals.actions.DocumentIconActionMenuFragment
|
||||
import com.anytypeio.anytype.ui.page.modals.actions.ProfileIconActionMenuFragment
|
||||
import com.anytypeio.anytype.ui.page.sheets.DocMenuBottomSheet
|
||||
import com.anytypeio.anytype.ui.page.sheets.DocMenuBottomSheet.DocumentMenuActionReceiver
|
||||
import com.bumptech.glide.Glide
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.hbisoft.pickit.PickiT
|
||||
|
@ -109,6 +109,7 @@ open class PageFragment :
|
|||
AddBlockFragment.AddBlockActionReceiver,
|
||||
TurnIntoActionReceiver,
|
||||
SelectProgrammingLanguageReceiver,
|
||||
DocumentMenuActionReceiver,
|
||||
ClipboardInterceptor,
|
||||
PickiTCallbacks {
|
||||
|
||||
|
@ -469,6 +470,14 @@ open class PageFragment :
|
|||
vm.onBackButtonPressed()
|
||||
}.launchIn(lifecycleScope)
|
||||
|
||||
topToolbar.undo.clicks().onEach {
|
||||
vm.onActionUndoClicked()
|
||||
}.launchIn(lifecycleScope)
|
||||
|
||||
topToolbar.redo.clicks().onEach {
|
||||
vm.onActionRedoClicked()
|
||||
}.launchIn(lifecycleScope)
|
||||
|
||||
mentionSuggesterToolbar.setupClicks(
|
||||
mentionClick = vm::onMentionSuggestClick,
|
||||
newPageClick = vm::onAddMentionNewPageClicked
|
||||
|
@ -557,6 +566,8 @@ open class PageFragment :
|
|||
.launchIn(lifecycleScope)
|
||||
|
||||
vm.syncStatus.onEach { status -> bindSyncStatus(status) }.launchIn(lifecycleScope)
|
||||
vm.isUndoEnabled.onEach { topToolbar.setUndoState(it) }.launchIn(lifecycleScope)
|
||||
vm.isRedoEnabled.onEach { topToolbar.setRedoState(it) }.launchIn(lifecycleScope)
|
||||
}
|
||||
|
||||
private fun bindSyncStatus(status: SyncStatus?) {
|
||||
|
@ -719,25 +730,25 @@ open class PageFragment :
|
|||
}
|
||||
}
|
||||
is Command.OpenDocumentMenu -> {
|
||||
DocumentPopUpMenu(
|
||||
context = requireContext(),
|
||||
view = topToolbar.menu,
|
||||
onArchiveClicked = vm::onArchiveThisPageClicked,
|
||||
onRedoClicked = vm::onActionRedoClicked,
|
||||
onUndoClicked = vm::onActionUndoClicked,
|
||||
onEnterMultiSelect = vm::onEnterMultiSelectModeClicked,
|
||||
onSearchClicked = vm::onEnterSearchModeClicked
|
||||
).show()
|
||||
hideKeyboard()
|
||||
val fr = DocMenuBottomSheet.new(
|
||||
title = command.title,
|
||||
status = command.status,
|
||||
image = command.image,
|
||||
emoji = command.emoji
|
||||
)
|
||||
fr.show(childFragmentManager, null)
|
||||
}
|
||||
is Command.OpenProfileMenu -> {
|
||||
ProfilePopUpMenu(
|
||||
context = requireContext(),
|
||||
view = topToolbar.menu,
|
||||
onRedoClicked = vm::onActionRedoClicked,
|
||||
onUndoClicked = vm::onActionUndoClicked,
|
||||
onEnterMultiSelect = vm::onEnterMultiSelectModeClicked,
|
||||
onSearchClicked = vm::onEnterSearchModeClicked
|
||||
).show()
|
||||
hideKeyboard()
|
||||
val fr = DocMenuBottomSheet.new(
|
||||
title = command.title,
|
||||
status = command.status,
|
||||
image = command.image,
|
||||
emoji = command.emoji,
|
||||
isProfile = true
|
||||
)
|
||||
fr.show(childFragmentManager, null)
|
||||
}
|
||||
is Command.OpenFullScreenImage -> {
|
||||
val screen = FullScreenPictureFragment.new(command.target, command.url).apply {
|
||||
|
@ -854,7 +865,6 @@ open class PageFragment :
|
|||
}
|
||||
|
||||
private fun render(state: ControlPanelState) {
|
||||
|
||||
if (state.navigationToolbar.isVisible) {
|
||||
placeholder.requestFocus()
|
||||
hideKeyboard()
|
||||
|
@ -1218,6 +1228,19 @@ open class PageFragment :
|
|||
vm.onSelectProgrammingLanguageClicked(target, key)
|
||||
}
|
||||
|
||||
override fun onArchiveClicked() {
|
||||
vm.onArchiveThisPageClicked()
|
||||
}
|
||||
|
||||
override fun onSearchOnPageClicked() {
|
||||
vm.onEnterSearchModeClicked()
|
||||
}
|
||||
|
||||
override fun onDismissBlockActionToolbar() {
|
||||
Blurry.delete(root)
|
||||
vm.onDismissBlockActionMenu(childFragmentManager.backStackEntryCount > 0)
|
||||
}
|
||||
|
||||
//------------ End of Anytype Custom Context Menu ------------
|
||||
|
||||
companion object {
|
||||
|
@ -1242,11 +1265,6 @@ open class PageFragment :
|
|||
const val TAG_ALERT = "tag.alert"
|
||||
const val TAG_LINK = "tag.link"
|
||||
}
|
||||
|
||||
override fun onDismissBlockActionToolbar() {
|
||||
Blurry.delete(root)
|
||||
vm.onDismissBlockActionMenu(childFragmentManager.backStackEntryCount > 0)
|
||||
}
|
||||
}
|
||||
|
||||
interface OnFragmentInteractionListener {
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
package com.anytypeio.anytype.ui.page.sheets
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.extensions.*
|
||||
import com.anytypeio.anytype.core_ui.reactive.clicks
|
||||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ext.firstDigitByHash
|
||||
import com.anytypeio.anytype.core_utils.ext.gone
|
||||
import com.anytypeio.anytype.core_utils.ext.visible
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetFragment
|
||||
import com.anytypeio.anytype.domain.common.Url
|
||||
import com.anytypeio.anytype.domain.status.SyncStatus
|
||||
import kotlinx.android.synthetic.main.fragment_doc_menu_bottom_sheet.*
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
class DocMenuBottomSheet : BaseBottomSheetFragment() {
|
||||
|
||||
private val title get() = arg<String?>(TITLE_KEY)
|
||||
private val status get() = SyncStatus.valueOf(arg(STATUS_KEY))
|
||||
private val image get() = arg<String?>(IMAGE_KEY)
|
||||
private val emoji get() = arg<String?>(EMOJI_KEY)
|
||||
private val isProfile get() = arg<Boolean>(IS_PROFILE_KEY)
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? = inflater.inflate(R.layout.fragment_doc_menu_bottom_sheet, container, false)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
bindTitle()
|
||||
bindSyncStatus(status)
|
||||
closeButton.clicks().onEach { dismiss() }.launchIn(lifecycleScope)
|
||||
searchOnPageContainer
|
||||
.clicks()
|
||||
.onEach {
|
||||
(parentFragment as? DocumentMenuActionReceiver)?.onSearchOnPageClicked()
|
||||
dismiss()
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
archiveContainer
|
||||
.clicks()
|
||||
.onEach {
|
||||
(parentFragment as? DocumentMenuActionReceiver)?.onArchiveClicked()
|
||||
dismiss()
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
if (image != null && !isProfile) icon.setImageOrNull(image)
|
||||
if (emoji != null && !isProfile) icon.setEmojiOrNull(emoji)
|
||||
|
||||
if (isProfile) {
|
||||
avatar.visible()
|
||||
image?.let { avatar.icon(it) } ?: avatar.bind(
|
||||
name = title.orEmpty(),
|
||||
color = title.orEmpty().firstDigitByHash().let {
|
||||
requireContext().avatarColor(it)
|
||||
}
|
||||
)
|
||||
archiveContainer.gone()
|
||||
searchOnPageContainer.setBackgroundResource(R.drawable.rectangle_doc_menu_default)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindTitle() {
|
||||
tvTitle.text = title ?: getString(R.string.untitled)
|
||||
}
|
||||
|
||||
private fun bindSyncStatus(status: SyncStatus) {
|
||||
when (status) {
|
||||
SyncStatus.UNKNOWN -> {
|
||||
badge.tint(
|
||||
color = requireContext().color(R.color.sync_status_red)
|
||||
)
|
||||
tvSubtitle.setText(R.string.sync_status_unknown)
|
||||
}
|
||||
SyncStatus.FAILED -> {
|
||||
badge.tint(
|
||||
color = requireContext().color(R.color.sync_status_red)
|
||||
)
|
||||
tvSubtitle.setText(R.string.sync_status_failed)
|
||||
}
|
||||
SyncStatus.OFFLINE -> {
|
||||
badge.tint(
|
||||
color = requireContext().color(R.color.sync_status_red)
|
||||
)
|
||||
tvSubtitle.setText(R.string.sync_status_offline)
|
||||
}
|
||||
SyncStatus.SYNCING -> {
|
||||
badge.tint(
|
||||
color = requireContext().color(R.color.sync_status_orange)
|
||||
)
|
||||
tvSubtitle.setText(R.string.sync_status_syncing)
|
||||
}
|
||||
SyncStatus.SYNCED -> {
|
||||
badge.tint(
|
||||
color = requireContext().color(R.color.sync_status_green)
|
||||
)
|
||||
tvSubtitle.setText(R.string.sync_status_synced)
|
||||
}
|
||||
else -> badge.tint(Color.WHITE)
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectDependencies() {}
|
||||
override fun releaseDependencies() {}
|
||||
|
||||
companion object {
|
||||
fun new(
|
||||
title: String?,
|
||||
status: SyncStatus,
|
||||
image: Url?,
|
||||
emoji: String?,
|
||||
isProfile: Boolean = false
|
||||
) = DocMenuBottomSheet().apply {
|
||||
arguments = bundleOf(
|
||||
TITLE_KEY to title,
|
||||
STATUS_KEY to status.name,
|
||||
IMAGE_KEY to image,
|
||||
EMOJI_KEY to emoji,
|
||||
IS_PROFILE_KEY to isProfile
|
||||
)
|
||||
}
|
||||
|
||||
private const val TITLE_KEY = "arg.doc-menu-bottom-sheet.title"
|
||||
private const val IMAGE_KEY = "arg.doc-menu-bottom-sheet.image"
|
||||
private const val EMOJI_KEY = "arg.doc-menu-bottom-sheet.emoji"
|
||||
private const val STATUS_KEY = "arg.doc-menu-bottom-sheet.status"
|
||||
private const val IS_PROFILE_KEY = "arg.doc-menu-bottom-sheet.is-profile"
|
||||
}
|
||||
|
||||
interface DocumentMenuActionReceiver {
|
||||
fun onArchiveClicked()
|
||||
fun onSearchOnPageClicked()
|
||||
}
|
||||
}
|
17
app/src/main/res/drawable/ic_doc_menu_close.xml
Normal file
17
app/src/main/res/drawable/ic_doc_menu_close.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="29dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="29"
|
||||
android:viewportHeight="28">
|
||||
<path
|
||||
android:fillColor="#EAE9E0"
|
||||
android:pathData="M28.75,14C28.75,21.732 22.482,28 14.75,28C7.018,28 0.75,21.732 0.75,14C0.75,6.268 7.018,0 14.75,0C22.482,0 28.75,6.268 28.75,14Z" />
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M9.5429,8.7929C9.9334,8.4024 10.5666,8.4024 10.9571,8.7929L19.9571,17.7929C20.3476,18.1834 20.3476,18.8166 19.9571,19.2071C19.5666,19.5976 18.9334,19.5976 18.5429,19.2071L9.5429,10.2071C9.1524,9.8166 9.1524,9.1834 9.5429,8.7929Z" />
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M9.5429,19.2071C9.1524,18.8166 9.1524,18.1834 9.5429,17.7929L18.5429,8.7929C18.9334,8.4024 19.5666,8.4024 19.9571,8.7929C20.3476,9.1834 20.3476,9.8166 19.9571,10.2071L10.9571,19.2071C10.5666,19.5976 9.9334,19.5976 9.5429,19.2071Z" />
|
||||
</vector>
|
8
app/src/main/res/drawable/rectangle_doc_menu_bottom.xml
Normal file
8
app/src/main/res/drawable/rectangle_doc_menu_bottom.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/white" />
|
||||
<corners
|
||||
android:bottomLeftRadius="10dp"
|
||||
android:bottomRightRadius="10dp" />
|
||||
</shape>
|
6
app/src/main/res/drawable/rectangle_doc_menu_default.xml
Normal file
6
app/src/main/res/drawable/rectangle_doc_menu_default.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/white" />
|
||||
<corners android:radius="10dp" />
|
||||
</shape>
|
8
app/src/main/res/drawable/rectangle_doc_menu_top.xml
Normal file
8
app/src/main/res/drawable/rectangle_doc_menu_top.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/white" />
|
||||
<corners
|
||||
android:topLeftRadius="10dp"
|
||||
android:topRightRadius="10dp" />
|
||||
</shape>
|
160
app/src/main/res/layout/fragment_doc_menu_bottom_sheet.xml
Normal file
160
app/src/main/res/layout/fragment_doc_menu_bottom_sheet.xml
Normal file
|
@ -0,0 +1,160 @@
|
|||
<?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="wrap_content"
|
||||
android:paddingBottom="12dp"
|
||||
android:background="@drawable/rectangle_doc_menu_background">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/iconContainer"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="12dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_gravity="center"
|
||||
tools:src="@drawable/circle_solid_default" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.widgets.AvatarWidget
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/circle_solid_default"
|
||||
android:visibility="invisible" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/closeButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="18dp"
|
||||
android:src="@drawable/ic_doc_menu_close"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/iconContainer"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/iconContainer" />
|
||||
|
||||
<View
|
||||
android:id="@+id/badge"
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="3dp"
|
||||
android:background="@drawable/circle_solid_default"
|
||||
android:backgroundTint="@color/white"
|
||||
app:layout_constraintStart_toEndOf="@+id/iconContainer"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tvTitle"
|
||||
tools:background="@drawable/circle_solid_default"
|
||||
tools:backgroundTint="@color/anytype_text_red" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="@font/inter_medium"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/closeButton"
|
||||
app:layout_constraintStart_toEndOf="@+id/iconContainer"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Spaceship Earth" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSubtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:textSize="13sp"
|
||||
android:lineHeight="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/badge"
|
||||
app:layout_constraintStart_toEndOf="@+id/badge"
|
||||
app:layout_constraintTop_toTopOf="@+id/badge"
|
||||
tools:text="Offline" />
|
||||
|
||||
<View
|
||||
android:background="#DFDDD0"
|
||||
android:id="@+id/statusDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="12dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/iconContainer" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/searchOnPageContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:background="@drawable/rectangle_doc_menu_top"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/statusDivider">
|
||||
|
||||
<TextView
|
||||
style="@style/DocMenuOptionTextStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="@string/search_on_page" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:src="@drawable/ic_doc_menu_search" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/optionDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/searchOnPageContainer" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/archiveContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/rectangle_doc_menu_bottom"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/optionDivider">
|
||||
|
||||
<TextView
|
||||
style="@style/DocMenuOptionTextStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="@string/archive" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:src="@drawable/ic_doc_menu_move_to_bin" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -143,5 +143,6 @@ Do the computation of an expensive paragraph of text on a background thread:
|
|||
<string name="telegram_app">https://t.me/joinchat/BsRYeRURhKcXDOyDA7_NLw</string>
|
||||
<string name="telegram_web">http://www.telegram.me/joinchat/BsRYeRURhKcXDOyDA7_NLw</string>
|
||||
<string name="fetching_your_account">Fetching your account…</string>
|
||||
<string name="search_on_page">Search on page</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -179,4 +179,9 @@
|
|||
<item name="cornerSizeBottomLeft">0dp</item>
|
||||
</style>
|
||||
|
||||
<style name="DocMenuOptionTextStyle">
|
||||
<item name="android:textSize">17sp</item>
|
||||
<item name="android:textColor">@color/black</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,35 @@
|
|||
package com.anytypeio.anytype.core_ui.extensions
|
||||
|
||||
import android.widget.ImageView
|
||||
import com.anytypeio.anytype.emojifier.Emojifier
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import timber.log.Timber
|
||||
|
||||
fun ImageView.setEmojiOrNull(unicode: String?) {
|
||||
if (unicode != null)
|
||||
try {
|
||||
Glide
|
||||
.with(this)
|
||||
.load(Emojifier.uri(unicode))
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.into(this)
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "Error while setting emoji icon for: $unicode")
|
||||
}
|
||||
else
|
||||
setImageDrawable(null)
|
||||
}
|
||||
|
||||
fun ImageView.setImageOrNull(image: String?) {
|
||||
if (image != null) {
|
||||
Glide
|
||||
.with(this)
|
||||
.load(image)
|
||||
.centerInside()
|
||||
.circleCrop()
|
||||
.into(this)
|
||||
} else {
|
||||
setImageDrawable(null)
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@ package com.anytypeio.anytype.core_ui.widgets
|
|||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
|
@ -13,7 +12,6 @@ import com.anytypeio.anytype.core_utils.ext.invisible
|
|||
import com.anytypeio.anytype.core_utils.ext.visible
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.widget_avatar.view.*
|
||||
import java.io.ByteArrayInputStream
|
||||
|
||||
class AvatarWidget : FrameLayout {
|
||||
|
||||
|
@ -56,7 +54,7 @@ class AvatarWidget : FrameLayout {
|
|||
|
||||
fun bind(name: String, color: Int? = null) {
|
||||
initials.visible()
|
||||
initials.text = if (name.isNotEmpty()) name.first().toUpperCase().toString() else ""
|
||||
initials.text = if (name.isNotEmpty()) name.first().toUpperCase().toString() else name
|
||||
icon.invisible()
|
||||
backgroundTintList = ColorStateList.valueOf(color ?: randomColor(name))
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ class DocumentTopToolbar : ConstraintLayout {
|
|||
val title: TextView get() = toolbarTitle
|
||||
val emoji: TextView get() = toolbarEmojiIcon
|
||||
val image: ImageView get() = toolbarImageIcon
|
||||
val undo: ImageView get() = btnUndo
|
||||
val redo: ImageView get() = btnRedo
|
||||
|
||||
constructor(
|
||||
context: Context
|
||||
|
@ -41,4 +43,14 @@ class DocumentTopToolbar : ConstraintLayout {
|
|||
private fun inflate() {
|
||||
LayoutInflater.from(context).inflate(R.layout.widget_document_top_toolbar, this)
|
||||
}
|
||||
|
||||
fun setUndoState(isEnabled: Boolean) {
|
||||
//btnUndo.alpha = if (isEnabled) 1.0f else 0.3f
|
||||
//btnUndo.isEnabled = isEnabled
|
||||
}
|
||||
|
||||
fun setRedoState(isEnabled: Boolean) {
|
||||
//btnRedo.alpha = if (isEnabled) 1.0f else 0.3f
|
||||
//btnRedo.isEnabled = isEnabled
|
||||
}
|
||||
}
|
25
core-ui/src/main/res/drawable/ic_doc_menu_move_to_bin.xml
Normal file
25
core-ui/src/main/res/drawable/ic_doc_menu_move_to_bin.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M8.75,3C8.75,2.3096 9.3096,1.75 10,1.75H14C14.6904,1.75 15.25,2.3096 15.25,3V5.25H8.75V3Z"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeColor="#ACA996" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M5.6577,19.5743L5.2705,5.25H18.7268L18.291,19.5836C18.2459,21.0691 17.0285,22.25 15.5423,22.25H8.4067C6.9168,22.25 5.6979,21.0636 5.6577,19.5743Z"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeColor="#ACA996" />
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:pathData="M9.3125,8.001H10.8125V19.501H9.3125V8.001Z" />
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:pathData="M13.3125,8.001H14.8125V19.501H13.3125V8.001Z" />
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:pathData="M3.2485,6C2.8343,6 2.5,5.6642 2.5,5.25C2.5,4.8358 2.8358,4.5 3.25,4.5L20.75,4.5C21.1642,4.5 21.5,4.8358 21.5,5.25C21.5,5.6642 21.1668,6 20.7526,6C17.5914,6 6.2745,6 3.2485,6Z" />
|
||||
</vector>
|
14
core-ui/src/main/res/drawable/ic_doc_menu_search.xml
Normal file
14
core-ui/src/main/res/drawable/ic_doc_menu_search.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M14.7929,14.7929C15.1834,14.4024 15.8166,14.4024 16.2071,14.7929L21.7071,20.2929C22.0976,20.6834 22.0976,21.3166 21.7071,21.7071C21.3166,22.0976 20.6834,22.0976 20.2929,21.7071L14.7929,16.2071C14.4024,15.8166 14.4024,15.1834 14.7929,14.7929Z" />
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M10,16.5C13.5899,16.5 16.5,13.5899 16.5,10C16.5,6.4102 13.5899,3.5 10,3.5C6.4102,3.5 3.5,6.4102 3.5,10C3.5,13.5899 6.4102,16.5 10,16.5ZM10,18C14.4183,18 18,14.4183 18,10C18,5.5817 14.4183,2 10,2C5.5817,2 2,5.5817 2,10C2,14.4183 5.5817,18 10,18Z" />
|
||||
</vector>
|
21
core-ui/src/main/res/drawable/ic_redo.xml
Normal file
21
core-ui/src/main/res/drawable/ic_redo.xml
Normal file
|
@ -0,0 +1,21 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M15.2196,2.4697C14.9267,2.7626 14.9267,3.2374 15.2196,3.5303L18.6892,7L15.2196,10.4697C14.9267,10.7626 14.9267,11.2374 15.2196,11.5303C15.5125,11.8232 15.9873,11.8232 16.2802,11.5303L20.8105,7L16.2802,2.4697C15.9873,2.1768 15.5125,2.1768 15.2196,2.4697Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M10,19C6.6863,19 4,16.3137 4,13C4,9.6863 6.6863,7 10,7"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeColor="#ACA996" />
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:pathData="M10,6.25h9v1.5h-9z" />
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:pathData="M10,18.25h2v1.5h-2z" />
|
||||
</vector>
|
21
core-ui/src/main/res/drawable/ic_undo.xml
Normal file
21
core-ui/src/main/res/drawable/ic_undo.xml
Normal file
|
@ -0,0 +1,21 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M8.7804,2.4697C9.0733,2.7626 9.0733,3.2374 8.7804,3.5303L5.3108,7L8.7804,10.4697C9.0733,10.7626 9.0733,11.2374 8.7804,11.5303C8.4875,11.8232 8.0127,11.8232 7.7198,11.5303L3.1895,7L7.7198,2.4697C8.0127,2.1768 8.4875,2.1768 8.7804,2.4697Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M14,19C17.3137,19 20,16.3137 20,13C20,9.6863 17.3137,7 14,7"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeColor="#ACA996" />
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:pathData="M14,6.25l-9,0l-0,1.5l9,0z" />
|
||||
<path
|
||||
android:fillColor="#ACA996"
|
||||
android:pathData="M14,18.25l-2,0l-0,1.5l2,0z" />
|
||||
</vector>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#F3F2EC" />
|
||||
<corners
|
||||
android:topLeftRadius="8dp"
|
||||
android:topRightRadius="8dp" />
|
||||
</shape>
|
|
@ -53,7 +53,7 @@
|
|||
android:textSize="15sp"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/toolbarIconContainer"
|
||||
app:layout_constraintEnd_toStartOf="@+id/toolbarMenu"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btnUndo"
|
||||
app:layout_constraintStart_toEndOf="@+id/toolbarIconContainer"
|
||||
app:layout_constraintTop_toTopOf="@+id/toolbarIconContainer"
|
||||
tools:text="Documents title" />
|
||||
|
@ -80,4 +80,24 @@
|
|||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:backgroundTint="@color/anytype_text_green" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btnUndo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:src="@drawable/ic_undo"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btnRedo"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btnRedo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:src="@drawable/ic_redo"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/toolbarMenu"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</merge>
|
|
@ -242,4 +242,10 @@
|
|||
<item quantity="other">%d pages selected</item>
|
||||
</plurals>
|
||||
|
||||
<string name="sync_status_unknown">Preparing…</string>
|
||||
<string name="sync_status_offline">No connection</string>
|
||||
<string name="sync_status_syncing">Syncing…</string>
|
||||
<string name="sync_status_synced">Synced</string>
|
||||
<string name="sync_status_failed">Not synced</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package com.anytypeio.anytype.core_utils.ext
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
inline fun <reified T> Fragment.arg(key: String): T {
|
||||
return requireArguments().get(key) as T
|
||||
}
|
||||
|
||||
inline fun <reified T> Fragment.argOrNull(key: String): T? {
|
||||
return requireArguments().get(key) as T?
|
||||
}
|
|
@ -121,6 +121,9 @@ class PageViewModel(
|
|||
|
||||
val syncStatus = MutableStateFlow<SyncStatus?>(null)
|
||||
|
||||
val isUndoEnabled = MutableStateFlow(false)
|
||||
val isRedoEnabled = MutableStateFlow(false)
|
||||
|
||||
val searchResultScrollPosition = MutableStateFlow(NO_SEARCH_RESULT_POSITION)
|
||||
|
||||
private val session = MutableStateFlow(Session.IDLE)
|
||||
|
@ -929,14 +932,58 @@ class PageViewModel(
|
|||
check(content is Content.Smart)
|
||||
when (content.type) {
|
||||
Content.Smart.Type.PROFILE -> {
|
||||
dispatch(command = Command.OpenProfileMenu)
|
||||
val details = orchestrator.stores.details.current().details
|
||||
dispatch(
|
||||
command = Command.OpenProfileMenu(
|
||||
status = syncStatus.value ?: SyncStatus.UNKNOWN,
|
||||
title = try {
|
||||
blocks.title().content<Content.Text>().text
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
},
|
||||
emoji = details[context]?.iconEmoji?.let { name ->
|
||||
if (name.isNotEmpty())
|
||||
name
|
||||
else
|
||||
null
|
||||
},
|
||||
image = details[context]?.iconImage?.let { name ->
|
||||
if (name.isNotEmpty())
|
||||
urlBuilder.image(name)
|
||||
else
|
||||
null
|
||||
}
|
||||
)
|
||||
)
|
||||
viewModelScope.sendEvent(
|
||||
analytics = analytics,
|
||||
eventName = POPUP_PROFILE_MENU
|
||||
)
|
||||
}
|
||||
Content.Smart.Type.PAGE -> {
|
||||
dispatch(command = Command.OpenDocumentMenu)
|
||||
val details = orchestrator.stores.details.current().details
|
||||
dispatch(
|
||||
command = Command.OpenDocumentMenu(
|
||||
status = syncStatus.value ?: SyncStatus.UNKNOWN,
|
||||
title = try {
|
||||
blocks.title().content<Content.Text>().text
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
},
|
||||
emoji = details[context]?.iconEmoji?.let { name ->
|
||||
if (name.isNotEmpty())
|
||||
name
|
||||
else
|
||||
null
|
||||
},
|
||||
image = details[context]?.iconImage?.let { name ->
|
||||
if (name.isNotEmpty())
|
||||
urlBuilder.image(name)
|
||||
else
|
||||
null
|
||||
}
|
||||
)
|
||||
)
|
||||
viewModelScope.sendEvent(
|
||||
analytics = analytics,
|
||||
eventName = POPUP_DOCUMENT_MENU
|
||||
|
@ -1438,7 +1485,9 @@ class PageViewModel(
|
|||
viewModelScope.launch {
|
||||
orchestrator.proxies.intents.send(
|
||||
Intent.Document.Undo(
|
||||
context = context
|
||||
context = context,
|
||||
onSuccessSideEffect = { isUndoEnabled.value = true },
|
||||
onFailureSideEffect = { isUndoEnabled.value = false }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -1448,7 +1497,9 @@ class PageViewModel(
|
|||
viewModelScope.launch {
|
||||
orchestrator.proxies.intents.send(
|
||||
Intent.Document.Redo(
|
||||
context = context
|
||||
context = context,
|
||||
onSuccessSideEffect = { isRedoEnabled.value = true },
|
||||
onFailureSideEffect = { isRedoEnabled.value = false }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.anytypeio.anytype.presentation.page.editor
|
|||
|
||||
import com.anytypeio.anytype.domain.common.Id
|
||||
import com.anytypeio.anytype.domain.common.Url
|
||||
import com.anytypeio.anytype.domain.status.SyncStatus
|
||||
import com.anytypeio.anytype.presentation.page.editor.model.BlockView
|
||||
|
||||
sealed class Command {
|
||||
|
@ -65,9 +66,19 @@ sealed class Command {
|
|||
val url: Url
|
||||
) : Command()
|
||||
|
||||
object OpenDocumentMenu : Command()
|
||||
data class OpenDocumentMenu(
|
||||
val status: SyncStatus,
|
||||
val title: String?,
|
||||
val emoji: String?,
|
||||
val image: String?
|
||||
) : Command()
|
||||
|
||||
object OpenProfileMenu : Command()
|
||||
data class OpenProfileMenu(
|
||||
val status: SyncStatus,
|
||||
val title: String?,
|
||||
val emoji: String?,
|
||||
val image: String?
|
||||
) : Command()
|
||||
|
||||
object AlertDialog : Command()
|
||||
|
||||
|
|
|
@ -9,11 +9,15 @@ sealed class Intent {
|
|||
sealed class Document : Intent() {
|
||||
|
||||
class Undo(
|
||||
val context: Id
|
||||
val context: Id,
|
||||
val onSuccessSideEffect: () -> Unit,
|
||||
val onFailureSideEffect: () -> Unit
|
||||
) : Document()
|
||||
|
||||
class Redo(
|
||||
val context: Id
|
||||
val context: Id,
|
||||
val onSuccessSideEffect: () -> Unit,
|
||||
val onFailureSideEffect: () -> Unit
|
||||
) : Document()
|
||||
|
||||
class UpdateTitle(
|
||||
|
|
|
@ -425,7 +425,7 @@ class Orchestrator(
|
|||
context = intent.context
|
||||
)
|
||||
).proceed(
|
||||
failure = defaultOnError,
|
||||
failure = defaultOnError.also { intent.onFailureSideEffect },
|
||||
success = { payload ->
|
||||
val event = EventAnalytics.Anytype(
|
||||
name = BLOCK_REDO,
|
||||
|
@ -435,7 +435,9 @@ class Orchestrator(
|
|||
middleware = System.currentTimeMillis()
|
||||
)
|
||||
)
|
||||
defaultPayloadWithEvent(Pair(payload, event))
|
||||
defaultPayloadWithEvent(Pair(payload, event)).also {
|
||||
intent.onSuccessSideEffect()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -446,7 +448,7 @@ class Orchestrator(
|
|||
context = intent.context
|
||||
)
|
||||
).proceed(
|
||||
failure = defaultOnError,
|
||||
failure = defaultOnError.also { intent.onFailureSideEffect() },
|
||||
success = { payload ->
|
||||
val event = EventAnalytics.Anytype(
|
||||
name = BLOCK_UNDO,
|
||||
|
@ -456,7 +458,9 @@ class Orchestrator(
|
|||
middleware = System.currentTimeMillis()
|
||||
)
|
||||
)
|
||||
defaultPayloadWithEvent(Pair(payload, event))
|
||||
defaultPayloadWithEvent(Pair(payload, event)).also {
|
||||
intent.onSuccessSideEffect()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
<?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">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView4"
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
|
@ -31,7 +30,7 @@
|
|||
android:textSize="18sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView4" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView3"
|
||||
|
@ -48,7 +47,7 @@
|
|||
app:layout_constraintTop_toBottomOf="@+id/textView2" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView5"
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue