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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<string name="enter_without_register">Enter without register</string>
<string name="register">Register</string>
<string name="search">Search</string>
<string name="search_no_results">0 results</string>
<string name="home">Home</string>
<string name="favorites">Favorites</string>
<string name="settings">Settings</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<string name="enter_without_register">Entrar without register</string>
<string name="register">Registrar</string>
<string name="search">Pesquisar</string>
<string name="search_no_results">0 resultados</string>
<string name="home">Home</string>
<string name="favorites">Favoritos</string>
<string name="settings">Configurações</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<string name="enter_without_register">Enter without register</string>
<string name="register">Register</string>
<string name="search">Search</string>
<string name="search_no_results">0 results</string>
<string name="home">Home</string>
<string name="favorites">Favorites</string>
<string name="settings">Settings</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import writeopia.application.core.resources.generated.resources.recent
import writeopia.application.core.resources.generated.resources.repeat_password
import writeopia.application.core.resources.generated.resources.retry
import writeopia.application.core.resources.generated.resources.search
import writeopia.application.core.resources.generated.resources.search_no_results
import writeopia.application.core.resources.generated.resources.settings
import writeopia.application.core.resources.generated.resources.sign_in
import writeopia.application.core.resources.generated.resources.sign_in_account
Expand Down Expand Up @@ -112,6 +113,9 @@ object WrStrings {
@Composable
fun search() = stringResource(Res.string.search)

@Composable
fun searchNoResults() = stringResource(Res.string.search_no_results)

@Composable
fun home() = stringResource(Res.string.home)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import io.writeopia.common.utils.icons.all.CalendarArrowDown
import io.writeopia.common.utils.icons.all.ChartScatter
import io.writeopia.common.utils.icons.all.ChevronDown
import io.writeopia.common.utils.icons.all.ChevronRight
import io.writeopia.common.utils.icons.all.ChevronUp
import io.writeopia.common.utils.icons.all.CircleArrowLeft
import io.writeopia.common.utils.icons.all.CircleArrowRight
import io.writeopia.common.utils.icons.all.CirclePlus
Expand Down Expand Up @@ -131,6 +132,8 @@ object WrIcons {

val smallArrowDown: ImageVector = ChevronDown

val smallArrowUp: ImageVector = ChevronUp

val undo: ImageVector = Undo2

val redo: ImageVector = Redo2
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.writeopia.common.utils.icons.all
/*
* Converted using https://composables.com/svgtocompose
*/

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PathFillType
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.StrokeJoin
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.path
import androidx.compose.ui.unit.dp

internal val ChevronUp: ImageVector
get() {
if (_ChevronUp != null) {
return _ChevronUp!!
}
_ChevronUp = ImageVector.Builder(
name = "io.writeopia.common.utils.icons.all.getChevronUp",
defaultWidth = 24.dp,
defaultHeight = 24.dp,
viewportWidth = 24f,
viewportHeight = 24f
).apply {
path(
fill = null,
fillAlpha = 1.0f,
stroke = SolidColor(Color(0xFF000000)),
strokeAlpha = 1.0f,
strokeLineWidth = 2f,
strokeLineCap = StrokeCap.Round,
strokeLineJoin = StrokeJoin.Round,
strokeLineMiter = 1.0f,
pathFillType = PathFillType.NonZero
) {
moveTo(18f, 15f)
lineToRelative(-6f, -6f)
lineToRelative(-6f, 6f)
}
}.build()
return _ChevronUp!!
}

private var _ChevronUp: ImageVector? = null
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Lock
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
Expand All @@ -34,13 +36,15 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import io.writeopia.common.utils.icons.WrIcons
import io.writeopia.commonui.dialogs.confirmation.DeleteConfirmationDialog
import io.writeopia.editor.features.editor.ui.desktop.edit.menu.SideEditorOptions
import io.writeopia.editor.features.editor.ui.folders.FolderSelectionDialog
import io.writeopia.editor.features.editor.viewmodel.NoteEditorViewModel
import io.writeopia.editor.features.editor.viewmodel.SideMenuTab
import io.writeopia.resources.WrStrings
import io.writeopia.theme.WriteopiaTheme
import io.writeopia.ui.drawer.factory.DrawersFactory

Expand Down Expand Up @@ -100,6 +104,8 @@ fun DesktopNoteEditorScreen(

val textState by noteEditorViewModel.searchText.collectAsState()
val showSearch by noteEditorViewModel.showSearchState.collectAsState()
val currentResultIndex by noteEditorViewModel.currentSearchIndexState.collectAsState()
val totalResults by noteEditorViewModel.totalSearchResultsState.collectAsState()
val shape = MaterialTheme.shapes.medium

AnimatedVisibility(
Expand All @@ -110,21 +116,55 @@ fun DesktopNoteEditorScreen(
Box(modifier = Modifier.padding(6.dp)) {
val focusRequester = remember { FocusRequester() }

BasicTextField(
value = textState,
onValueChange = noteEditorViewModel::searchInDocument,
modifier = Modifier.defaultMinSize(minWidth = 160.dp)
.focusRequester(focusRequester)
Row(
modifier = Modifier
.padding(12.dp)
.background(WriteopiaTheme.colorScheme.cardBg, shape)
.border(1.dp, MaterialTheme.colorScheme.outline, shape)
.padding(8.dp),
singleLine = true,
textStyle = MaterialTheme.typography.bodySmall.copy(
color = MaterialTheme.colorScheme.onBackground
),
cursorBrush = SolidColor(MaterialTheme.colorScheme.onBackground),
)
.border(1.dp, MaterialTheme.colorScheme.outline, shape),
verticalAlignment = Alignment.CenterVertically
) {
BasicTextField(
value = textState,
onValueChange = noteEditorViewModel::searchInDocument,
modifier = Modifier.defaultMinSize(minWidth = 160.dp)
.focusRequester(focusRequester)
.padding(8.dp),
singleLine = true,
textStyle = MaterialTheme.typography.bodySmall.copy(
color = MaterialTheme.colorScheme.onBackground
),
cursorBrush = SolidColor(MaterialTheme.colorScheme.onBackground),
)

Text(
modifier = Modifier.padding(4.dp).width(100.dp),
text = if (totalResults == 0) WrStrings.searchNoResults() else "${currentResultIndex + 1}/$totalResults",
textAlign = TextAlign.Right,
style = MaterialTheme.typography.bodySmall.copy(
color = MaterialTheme.colorScheme.onBackground
)
)

Icon(
imageVector = WrIcons.smallArrowUp,
contentDescription = "Previous result",
tint = MaterialTheme.colorScheme.onBackground,
modifier = Modifier
.size(32.dp)
.clickable(onClick = noteEditorViewModel::previousSearchResult)
.padding(4.dp)
)

Icon(
imageVector = WrIcons.smallArrowDown,
contentDescription = "Next result",
tint = MaterialTheme.colorScheme.onBackground,
modifier = Modifier
.size(32.dp)
.clickable(onClick = noteEditorViewModel::nextSearchResult)
.padding(4.dp)
)
}

Icon(
imageVector = WrIcons.close,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
Expand Down Expand Up @@ -142,9 +143,13 @@ class NoteEditorKmpViewModel(

private val _showSearch = MutableStateFlow(false)
private val _searchText = MutableStateFlow("")
private val _currentSearchIndex = MutableStateFlow(0)
private val _totalSearchResults = MutableStateFlow(0)

override val showSearchState: StateFlow<Boolean> = _showSearch.asStateFlow()
override val searchText: StateFlow<String> = _searchText.asStateFlow()
override val currentSearchIndexState: StateFlow<Int> = _currentSearchIndex.asStateFlow()
override val totalSearchResultsState: StateFlow<Int> = _totalSearchResults.asStateFlow()

private val hasLinesSelection = writeopiaManager.onEditPositions
.map { it.isNotEmpty() }
Expand Down Expand Up @@ -272,12 +277,15 @@ class NoteEditorKmpViewModel(
writeopiaManager.toDraw,
findsOfSearch,
searchText,
_showSearch
) { drawState, finds, query, showSearch ->
_showSearch,
_currentSearchIndex
) { drawState, finds, query, showSearch, currentSearchIndex ->
if (finds.isEmpty() && showSearch) return@combine drawState.copy(focus = null)
if (finds.isEmpty()) return@combine drawState

val mutableStories = drawState.stories.toMutableList()
val activeFindPosition = if (finds.size > currentSearchIndex) finds.elementAt(currentSearchIndex) else null
_totalSearchResults.value = finds.size

finds.forEach { position ->
val realPosition = minOf(position * 2, mutableStories.lastIndex)
Expand All @@ -286,7 +294,12 @@ class NoteEditorKmpViewModel(

val findSpans = FindInText.findInText(story.text ?: "", query)
.map { (start, end) ->
SpanInfo.create(start, end, Span.HIGHLIGHT_YELLOW)
val span = if (position == activeFindPosition) {
Span.HIGHLIGHT_GREEN
} else {
Span.HIGHLIGHT_YELLOW
}
SpanInfo.create(start, end, span)
}

mutableStories[realPosition] =
Expand Down Expand Up @@ -718,11 +731,45 @@ class NoteEditorKmpViewModel(
_showSearch.value = false
delay(100)
_searchText.value = ""
_currentSearchIndex.value = 0
}
}

override fun searchInDocument(query: String) {
_searchText.value = query
viewModelScope.launch {
_searchText.value = query
val finds = findsOfSearch.first().toList().sorted()
if (finds.isEmpty()) {
_currentSearchIndex.value = 0
_totalSearchResults.value = 0
return@launch
}

val currentPosition = writeopiaManager.currentStory.value.focus ?: writeopiaManager.currentStory.value.selection.position
val newIndex = finds.indexOfFirst { it >= currentPosition }

_currentSearchIndex.value = if (newIndex != -1) newIndex else 0
}
}

override fun previousSearchResult() {
viewModelScope.launch {
val total = findsOfSearch.first().size
if (total == 0) return@launch

val currentIndex = _currentSearchIndex.value
_currentSearchIndex.value = (currentIndex - 1 + total) % total
}
}

override fun nextSearchResult() {
viewModelScope.launch {
val total = findsOfSearch.first().size
if (total == 0) return@launch

val currentIndex = _currentSearchIndex.value
_currentSearchIndex.value = (currentIndex + 1) % total
}
}

override fun titleClick(tag: Tag) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ interface NoteEditorViewModel : BackstackInform, BackstackHandler {

val searchText: StateFlow<String>

val currentSearchIndexState: StateFlow<Int>

val totalSearchResultsState: StateFlow<Int>

val hasSelectedLines: StateFlow<Boolean>

val selectionMetadataState: StateFlow<Set<SelectionMetadata>>
Expand All @@ -66,6 +70,10 @@ interface NoteEditorViewModel : BackstackInform, BackstackHandler {

fun searchInDocument(query: String)

fun previousSearchResult()

fun nextSearchResult()

fun toggleEditable()

fun deleteSelection()
Expand Down
Loading