Skip to content
This repository has been archived by the owner on Sep 23, 2024. It is now read-only.

Commit

Permalink
Merge pull request #160 from keigomichi/kototo522/issue15
Browse files Browse the repository at this point in the history
feature: 検索機能の実装
  • Loading branch information
kototo522 authored Mar 2, 2024
2 parents 16031e4 + f826b30 commit 5bf5a6d
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import com.example.tsundokun.R.string
import com.example.tsundokun.ui.destinations.SearchScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator

/*
Expand All @@ -21,7 +22,7 @@ fun TopAppHomeBar(navigator: DestinationsNavigator) {
CenterAlignedTopAppBar(
title = { Text(stringResource(id = string.app_name)) },
navigationIcon = {
IconButton(onClick = { /* 検索する */ }) {
IconButton(onClick = { navigator.navigate(SearchScreenDestination()) }) {
Icon(
imageVector = Filled.Search,
contentDescription = stringResource(string.button_search_description),
Expand Down
90 changes: 90 additions & 0 deletions app/src/main/java/com/example/tsundokun/ui/search/SearchScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.example.tsundokun.ui.search

import android.os.Build.VERSION_CODES
import androidx.annotation.RequiresApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.Icons.AutoMirrored.Filled
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Cancel
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SearchBar
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.example.tsundokun.R.string
import com.example.tsundokun.ui.home.component.webPageList.TsundokuListScreen
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator

@OptIn(ExperimentalMaterial3Api::class)
@RequiresApi(VERSION_CODES.O)
@RootNavGraph(start = false)
@Destination
@Composable
fun SearchScreen(navigator: DestinationsNavigator) {
val viewModel: SearchViewModel = hiltViewModel()
val searchUiState by viewModel.uiState.collectAsState()

var searchQuery by rememberSaveable { mutableStateOf(searchUiState.searchQuery) }
var active by remember { mutableStateOf(false) }

Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background,
) {
Column {
SearchBar(
query = searchQuery,
onQueryChange = { newQuery -> searchQuery = newQuery },
onSearch = {
if (searchQuery.isNotEmpty()) {
viewModel.search(searchQuery)
}
},
active = active,
onActiveChange = {
active = it
},
content = {
TsundokuListScreen(selectedCategoryTsundokuList = searchUiState.searchResults, navigator = navigator)
},
leadingIcon = {
Icon(
imageVector = Filled.ArrowBack,
contentDescription = stringResource(string.button_close),
modifier = Modifier.clickable { navigator.popBackStack() },
)
},
trailingIcon = {
if (active) {
Icon(
imageVector = Icons.Default.Cancel,
contentDescription = null,
modifier = Modifier
.padding(start = 16.dp)
.clickable { searchQuery = "" },
)
}
},
placeholder = { Text(stringResource(id = string.search)) },
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.example.tsundokun.ui.search

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.tsundokun.core.model.Tsundoku
import com.example.tsundokun.data.repository.TsundokuRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class SearchViewModel @Inject constructor(
tsundokuRepository: TsundokuRepository,
) : ViewModel() {
private val _uiState = MutableStateFlow(SearchUiState())
val uiState = _uiState.asStateFlow()

private val tsundokuList = tsundokuRepository.observeAll()

fun search(query: String) {
viewModelScope.launch {
try {
tsundokuList.map { tsundokuList ->
tsundokuList.filter { tsundoku ->
tsundoku.title?.contains(query, ignoreCase = true) == true
}
}.collect { filteredList ->
_uiState.value = _uiState.value.copy(searchResults = filteredList)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}

data class SearchUiState(
var searchQuery: String = "",
var searchResults: List<Tsundoku> = emptyList(),
)
}
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@
<string name="loading_error">情報を取得する際にエラーが発生しました</string>
<string name="back_home">ホームに戻る</string>
<string name="share_link">リンクを共有</string>
<string name="search">検索</string>
</resources>

0 comments on commit 5bf5a6d

Please sign in to comment.