From 59c1f66a4ba27e25d42a15fb95af1a044c70b26f Mon Sep 17 00:00:00 2001 From: Willie Koomson Date: Sat, 24 Jun 2023 09:13:09 -0700 Subject: [PATCH 01/10] Add local search history to the community list screen This change adds a search history Dao/Repository/ViewModel so that we can save user searches in Room, and display them on the community list screen. Users can click the history item to search it, or click the "X" button to delete it. --- app/src/main/java/com/jerboa/MainActivity.kt | 29 +++++ app/src/main/java/com/jerboa/db/AppDB.kt | 47 +++++++- .../community/list/CommunityListActivity.kt | 112 +++++++++++++++--- .../community/list/CommunityListViewModel.kt | 76 ++++++++++-- 4 files changed, 231 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/com/jerboa/MainActivity.kt b/app/src/main/java/com/jerboa/MainActivity.kt index 124826bc3..dddc1c917 100644 --- a/app/src/main/java/com/jerboa/MainActivity.kt +++ b/app/src/main/java/com/jerboa/MainActivity.kt @@ -15,6 +15,7 @@ import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideOutHorizontally import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf @@ -39,8 +40,11 @@ import com.jerboa.db.AppDB import com.jerboa.db.AppSettingsRepository import com.jerboa.db.AppSettingsViewModel import com.jerboa.db.AppSettingsViewModelFactory +import com.jerboa.db.SearchHistoryRepository import com.jerboa.ui.components.comment.edit.CommentEditActivity +import com.jerboa.ui.components.comment.edit.CommentEditViewModel import com.jerboa.ui.components.comment.reply.CommentReplyActivity +import com.jerboa.ui.components.comment.reply.CommentReplyViewModel import com.jerboa.ui.components.comment.reply.ReplyItem import com.jerboa.ui.components.common.CommentEditDeps import com.jerboa.ui.components.common.MarkdownHelper @@ -55,17 +59,24 @@ import com.jerboa.ui.components.common.takeDepsFromRoot import com.jerboa.ui.components.community.CommunityActivity import com.jerboa.ui.components.community.CommunityViewModel import com.jerboa.ui.components.community.list.CommunityListActivity +import com.jerboa.ui.components.community.list.CommunityListViewModel +import com.jerboa.ui.components.community.list.CommunityListViewModelFactory import com.jerboa.ui.components.community.sidebar.CommunitySidebarActivity import com.jerboa.ui.components.home.BottomNavActivity import com.jerboa.ui.components.home.SiteViewModel import com.jerboa.ui.components.home.sidebar.SiteSidebarActivity import com.jerboa.ui.components.inbox.InboxActivity +import com.jerboa.ui.components.inbox.InboxViewModel import com.jerboa.ui.components.login.LoginActivity import com.jerboa.ui.components.person.PersonProfileActivity +import com.jerboa.ui.components.person.PersonProfileViewModel import com.jerboa.ui.components.post.PostActivity import com.jerboa.ui.components.post.create.CreatePostActivity +import com.jerboa.ui.components.post.create.CreatePostViewModel import com.jerboa.ui.components.post.edit.PostEditActivity +import com.jerboa.ui.components.post.edit.PostEditViewModel import com.jerboa.ui.components.privatemessage.PrivateMessageReplyActivity +import com.jerboa.ui.components.report.CreateReportViewModel import com.jerboa.ui.components.report.comment.CreateCommentReportActivity import com.jerboa.ui.components.report.post.CreatePostReportActivity import com.jerboa.ui.components.settings.SettingsActivity @@ -80,10 +91,22 @@ class JerboaApplication : Application() { private val database by lazy { AppDB.getDatabase(this) } val accountRepository by lazy { AccountRepository(database.accountDao()) } val appSettingsRepository by lazy { AppSettingsRepository(database.appSettingsDao()) } + val searchHistoryRepository by lazy { SearchHistoryRepository(database.searchHistoryDao()) } } class MainActivity : AppCompatActivity() { private val siteViewModel by viewModels() + private val communityViewModel by viewModels() + private val personProfileViewModel by viewModels() + private val inboxViewModel by viewModels() + private val communityListViewModel by viewModels() { + CommunityListViewModelFactory((application as JerboaApplication).searchHistoryRepository) + } + private val createPostViewModel by viewModels() + private val commentReplyViewModel by viewModels() + private val commentEditViewModel by viewModels() + private val postEditViewModel by viewModels() + private val createReportViewModel by viewModels() private val accountSettingsViewModel by viewModels { AccountSettingsViewModelFactory((application as JerboaApplication).accountRepository) } @@ -350,6 +373,12 @@ class MainActivity : AppCompatActivity() { ), ) { val args = Route.CommunityListArgs(it) + // Whenever navigating here, reset the list with your followed communities + SideEffect { + communityListViewModel.setCommunityListFromFollowed(siteViewModel) + communityListViewModel.resetSearch() + } + CommunityListActivity( navController = navController, accountViewModel = accountViewModel, diff --git a/app/src/main/java/com/jerboa/db/AppDB.kt b/app/src/main/java/com/jerboa/db/AppDB.kt index 989b75b7c..cca49b1f9 100644 --- a/app/src/main/java/com/jerboa/db/AppDB.kt +++ b/app/src/main/java/com/jerboa/db/AppDB.kt @@ -14,6 +14,7 @@ import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch @@ -144,6 +145,23 @@ val APP_SETTINGS_DEFAULT = AppSettings( blurNSFW = true, ) +@Entity +data class SearchHistory( + @PrimaryKey @ColumnInfo(name = "text") val text: String, +) + +@Dao +interface SearchHistoryDao { + @Query("SELECT * FROM SearchHistory") + fun history(): Flow> + + @Insert(onConflict = OnConflictStrategy.IGNORE, entity = SearchHistory::class) + suspend fun insert(item: SearchHistory) + + @Delete(entity = SearchHistory::class) + suspend fun delete(item: SearchHistory) +} + @Dao interface AccountDao { @Query("SELECT * FROM account") @@ -224,6 +242,16 @@ class AccountRepository(private val accountDao: AccountDao) { } } +class SearchHistoryRepository( + private val searchHistoryDao: SearchHistoryDao, +) { + fun history(): Flow> = searchHistoryDao.history() + + suspend fun insert(item: SearchHistory) = searchHistoryDao.insert(item) + + suspend fun delete(item: SearchHistory) = searchHistoryDao.delete(item) +} + // Declares the DAO as a private property in the constructor. Pass in the DAO // instead of the whole database, because you only need access to the DAO class AppSettingsRepository( @@ -469,14 +497,28 @@ val MIGRATION_16_17 = object : Migration(16, 17) { } } +val MIGRATION_17_18 = object : Migration(17, 18) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(UPDATE_APP_CHANGELOG_UNVIEWED) + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS SearchHistory( + history TEXT PRIMARY KEY NOT NULL + ) + """, + ) + } +} + @Database( - version = 17, - entities = [Account::class, AppSettings::class], + version = 18, + entities = [Account::class, AppSettings::class, SearchHistory::class], exportSchema = true, ) abstract class AppDB : RoomDatabase() { abstract fun accountDao(): AccountDao abstract fun appSettingsDao(): AppSettingsDao + abstract fun searchHistoryDao(): SearchHistoryDao companion object { @Volatile @@ -511,6 +553,7 @@ abstract class AppDB : RoomDatabase() { MIGRATION_14_15, MIGRATION_15_16, MIGRATION_16_17, + MIGRATION_17_18, ) // Necessary because it can't insert data on creation .addCallback(object : Callback() { diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt index 11eeff76b..c6d00cefe 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt @@ -1,12 +1,21 @@ package com.jerboa.ui.components.community.list import android.util.Log +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding +import androidx.compose.material.Icon +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Close +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold 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.rememberCoroutineScope @@ -15,13 +24,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController -import com.jerboa.DEBOUNCE_DELAY import com.jerboa.api.ApiState -import com.jerboa.datatypes.types.Search -import com.jerboa.datatypes.types.SearchType -import com.jerboa.datatypes.types.SortType import com.jerboa.db.AccountViewModel -import com.jerboa.ui.components.common.ApiEmptyText import com.jerboa.ui.components.common.ApiErrorText import com.jerboa.ui.components.common.InitializeRoute import com.jerboa.ui.components.common.LoadingBar @@ -30,7 +34,6 @@ import com.jerboa.ui.components.common.getCurrentAccount import com.jerboa.ui.components.common.toCommunity import com.jerboa.ui.components.home.SiteViewModel import kotlinx.coroutines.Job -import kotlinx.coroutines.delay import kotlinx.coroutines.launch private var fetchCommunitiesJob: Job? = null @@ -69,24 +72,97 @@ fun CommunityListActivity( search = search, onSearchChange = { search = it - fetchCommunitiesJob?.cancel() - fetchCommunitiesJob = scope.launch { - delay(DEBOUNCE_DELAY) - communityListViewModel.searchCommunities( - form = Search( - q = search, - type_ = SearchType.Communities, - sort = SortType.TopAll, - auth = account?.jwt, - ), - ) + if (it.isEmpty()) { + communityListViewModel.resetSearch() + return@CommunityListHeader + } + scope.launch { + communityListViewModel.searchAllCommunities(search, account?.jwt, true) } }, ) }, content = { padding -> when (val communitiesRes = communityListViewModel.searchRes) { - ApiState.Empty -> ApiEmptyText() + ApiState.Empty -> { + Column( + modifier = Modifier + .padding(padding) + .imePadding(), + ) { + val history by communityListViewModel.searchHistory.collectAsState( + emptyList(), + ) + if (history.isNotEmpty()) { + ListItem( + headlineContent = { + Text( + text = "Recent searches", + color = MaterialTheme.colorScheme.onBackground, + style = MaterialTheme.typography.labelLarge, + ) + }, + ) + } + history.forEach { + ListItem( + modifier = Modifier.clickable { + scope.launch { + search = it.text + communityListViewModel.searchAllCommunities( + it.text, + account?.jwt, + ) + } + }, + headlineContent = { + Text( + text = it.text, + color = MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.bodyLarge, + ) + }, + trailingContent = { + IconButton( + onClick = { + scope.launch { + communityListViewModel.deleteSearchHistory(it) + } + }, + content = { + Icon( + Icons.Rounded.Close, + contentDescription = "Delete ${it.text}", + tint = MaterialTheme.colorScheme.surfaceTint, + ) + }, + ) + }, + ) + } + ListItem( + headlineContent = { + Text( + text = "Communities", + color = MaterialTheme.colorScheme.onBackground, + style = MaterialTheme.typography.labelLarge, + ) + }, + ) + CommunityListings( + communities = communityListViewModel.communities, + onClickCommunity = { cs -> + if (selectMode) { + communityListViewModel.selectCommunity(cs) + navController.navigateUp() + } else { + navController.navigate(route = "community/${cs.id}") + } + }, + blurNSFW = blurNSFW, + ) + } + } is ApiState.Failure -> ApiErrorText(communitiesRes.msg) ApiState.Loading -> { LoadingBar(padding) diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt index faad504f5..a37ab8faa 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt @@ -4,34 +4,92 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import com.jerboa.DEBOUNCE_DELAY import com.jerboa.api.API import com.jerboa.api.ApiState import com.jerboa.api.apiWrapper +import com.jerboa.datatypes.types.Community import com.jerboa.datatypes.types.CommunityAggregates import com.jerboa.datatypes.types.CommunityView import com.jerboa.datatypes.types.Search import com.jerboa.datatypes.types.SearchResponse import com.jerboa.datatypes.types.SearchType +import com.jerboa.datatypes.types.SortType import com.jerboa.datatypes.types.SubscribedType +import com.jerboa.db.SearchHistory +import com.jerboa.db.SearchHistoryRepository import com.jerboa.serializeToMap import com.jerboa.ui.components.common.Initializable import com.jerboa.ui.components.home.SiteViewModel +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch -class CommunityListViewModel : ViewModel(), Initializable { - override var initialized by mutableStateOf(false) +class CommunityListViewModelFactory( + private val repository: SearchHistoryRepository, +) : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(CommunityListViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return CommunityListViewModel(repository) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} +class CommunityListViewModel( + private val searchHistoryRepository: SearchHistoryRepository, +) : ViewModel(), Initializable { + override var initialized by mutableStateOf(false) var searchRes: ApiState by mutableStateOf(ApiState.Empty) private set - fun searchCommunities(form: Search) { - viewModelScope.launch { + var selectedCommunity: Community? by mutableStateOf(null) + private set + + val searchHistory: Flow> = searchHistoryRepository.history() + + var communities: List by mutableStateOf(emptyList()) + + private var fetchCommunitiesJob: Job? = null + fun searchCommunities(form: Search, debounce: Boolean = false) { + fetchCommunitiesJob?.cancel() + fetchCommunitiesJob = viewModelScope.launch { + if (debounce) delay(DEBOUNCE_DELAY) searchRes = ApiState.Loading searchRes = apiWrapper(API.getInstance().search(form.serializeToMap())) + form.q.takeIf { it.isNotEmpty() }?.let { + searchHistoryRepository.insert(SearchHistory(it)) + } } } + fun searchAllCommunities(query: String, jwt: String? = null, debounce: Boolean = false) { + searchCommunities( + Search( + q = query, + type_ = SearchType.Communities, + sort = SortType.TopAll, + auth = jwt, + ), + debounce, + ) + } + + fun resetSearch() { + searchRes = ApiState.Empty + } + suspend fun deleteSearchHistory(item: SearchHistory) { + searchHistoryRepository.delete(item) + } + + fun selectCommunity(community: Community) { + selectedCommunity = community + } + fun setCommunityListFromFollowed(siteViewModel: SiteViewModel) { when (val siteRes = siteViewModel.siteRes) { is ApiState.Success -> { @@ -59,15 +117,7 @@ class CommunityListViewModel : ViewModel(), Initializable { ) } - searchRes = ApiState.Success( - SearchResponse( - type_ = SearchType.Communities, - communities = followsIntoCommunityViews, - comments = emptyList(), - posts = emptyList(), - users = emptyList(), - ), - ) + communities = followsIntoCommunityViews } } From 75629e45264b6dce4c72f1f3d7e646ab4c193c0f Mon Sep 17 00:00:00 2001 From: Willie Koomson Date: Sat, 24 Jun 2023 12:12:56 -0700 Subject: [PATCH 02/10] Sort searches in reverse-chronological order, add saveSearchHistory setting --- app/src/main/java/com/jerboa/MainActivity.kt | 4 ++- app/src/main/java/com/jerboa/db/AppDB.kt | 11 ++++++ .../community/list/CommunityListViewModel.kt | 34 ++++++++++++++++--- .../lookandfeel/LookAndFeelActivity.kt | 10 ++++++ 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/jerboa/MainActivity.kt b/app/src/main/java/com/jerboa/MainActivity.kt index dddc1c917..715b23927 100644 --- a/app/src/main/java/com/jerboa/MainActivity.kt +++ b/app/src/main/java/com/jerboa/MainActivity.kt @@ -100,7 +100,9 @@ class MainActivity : AppCompatActivity() { private val personProfileViewModel by viewModels() private val inboxViewModel by viewModels() private val communityListViewModel by viewModels() { - CommunityListViewModelFactory((application as JerboaApplication).searchHistoryRepository) + (application as JerboaApplication).let { app -> + CommunityListViewModelFactory(app.searchHistoryRepository, app.appSettingsRepository) + } } private val createPostViewModel by viewModels() private val commentReplyViewModel by viewModels() diff --git a/app/src/main/java/com/jerboa/db/AppDB.kt b/app/src/main/java/com/jerboa/db/AppDB.kt index cca49b1f9..110b4f57d 100644 --- a/app/src/main/java/com/jerboa/db/AppDB.kt +++ b/app/src/main/java/com/jerboa/db/AppDB.kt @@ -124,6 +124,11 @@ data class AppSettings( defaultValue = "1", ) val blurNSFW: Boolean, + @ColumnInfo( + name = "save_search_history", + defaultValue = "1", + ) + val saveSearchHistory: Boolean, ) val APP_SETTINGS_DEFAULT = AppSettings( @@ -143,11 +148,13 @@ val APP_SETTINGS_DEFAULT = AppSettings( usePrivateTabs = false, secureWindow = false, blurNSFW = true, + saveSearchHistory = true, ) @Entity data class SearchHistory( @PrimaryKey @ColumnInfo(name = "text") val text: String, + @ColumnInfo(name = "timestamp") val timestamp: Long, ) @Dao @@ -500,10 +507,14 @@ val MIGRATION_16_17 = object : Migration(16, 17) { val MIGRATION_17_18 = object : Migration(17, 18) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL(UPDATE_APP_CHANGELOG_UNVIEWED) + database.execSQL( + "ALTER TABLE AppSettings add column save_search_history INTEGER NOT NULL default 1", + ) database.execSQL( """ CREATE TABLE IF NOT EXISTS SearchHistory( history TEXT PRIMARY KEY NOT NULL + timestamp INTEGER NOT NULL ) """, ) diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt index a37ab8faa..783865d92 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt @@ -18,6 +18,7 @@ import com.jerboa.datatypes.types.SearchResponse import com.jerboa.datatypes.types.SearchType import com.jerboa.datatypes.types.SortType import com.jerboa.datatypes.types.SubscribedType +import com.jerboa.db.AppSettingsRepository import com.jerboa.db.SearchHistory import com.jerboa.db.SearchHistoryRepository import com.jerboa.serializeToMap @@ -26,15 +27,20 @@ import com.jerboa.ui.components.home.SiteViewModel import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch +import java.time.Instant class CommunityListViewModelFactory( - private val repository: SearchHistoryRepository, + private val searchHistoryRepository: SearchHistoryRepository, + private val appSettingsRepository: AppSettingsRepository, ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { if (modelClass.isAssignableFrom(CommunityListViewModel::class.java)) { @Suppress("UNCHECKED_CAST") - return CommunityListViewModel(repository) as T + return CommunityListViewModel(searchHistoryRepository, appSettingsRepository) as T } throw IllegalArgumentException("Unknown ViewModel class") } @@ -42,8 +48,14 @@ class CommunityListViewModelFactory( class CommunityListViewModel( private val searchHistoryRepository: SearchHistoryRepository, + private val appSettingsRepository: AppSettingsRepository, ) : ViewModel(), Initializable { override var initialized by mutableStateOf(false) + init { + viewModelScope.launch { + appSettingsRepository.appSettings.observeForever { appSettings.value = it } + } + } var searchRes: ApiState by mutableStateOf(ApiState.Empty) private set @@ -51,9 +63,13 @@ class CommunityListViewModel( private set val searchHistory: Flow> = searchHistoryRepository.history() + .filter { appSettings.value?.saveSearchHistory == true } + .map { history -> history.sortedByDescending { it.timestamp } } var communities: List by mutableStateOf(emptyList()) + private val appSettings = MutableStateFlow(appSettingsRepository.appSettings.value) + private var fetchCommunitiesJob: Job? = null fun searchCommunities(form: Search, debounce: Boolean = false) { fetchCommunitiesJob?.cancel() @@ -61,13 +77,21 @@ class CommunityListViewModel( if (debounce) delay(DEBOUNCE_DELAY) searchRes = ApiState.Loading searchRes = apiWrapper(API.getInstance().search(form.serializeToMap())) - form.q.takeIf { it.isNotEmpty() }?.let { - searchHistoryRepository.insert(SearchHistory(it)) + form.q.takeIf { query -> + appSettings.value?.saveSearchHistory == true && query.isNotEmpty() + }?.let { query -> + searchHistoryRepository.insert( + SearchHistory(query, Instant.now().epochSecond), + ) } } } - fun searchAllCommunities(query: String, jwt: String? = null, debounce: Boolean = false) { + fun searchAllCommunities( + query: String, + jwt: String? = null, + debounce: Boolean = false, + ) { searchCommunities( Search( q = query, diff --git a/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt b/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt index fdf9a75a3..1dc8c5513 100644 --- a/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt @@ -89,6 +89,8 @@ fun LookAndFeelActivity( val secureWindowState = rememberBooleanSettingState(settings.secureWindow) val blurNSFW = rememberBooleanSettingState(settings.blurNSFW) + val saveSearchHistoryState = rememberBooleanSettingState(settings.saveSearchHistory) + val snackbarHostState = remember { SnackbarHostState() } val scrollState = rememberScrollState() @@ -112,6 +114,7 @@ fun LookAndFeelActivity( usePrivateTabs = usePrivateTabsState.value, secureWindow = secureWindowState.value, blurNSFW = blurNSFW.value, + saveSearchHistory = saveSearchHistoryState.value, ), ) } @@ -287,6 +290,13 @@ fun LookAndFeelActivity( }, onCheckedChange = { updateAppSettings() }, ) + SettingsCheckbox( + state = saveSearchHistoryState, + title = { + Text(text = "Save search history") + }, + onCheckedChange = { updateAppSettings() }, + ) } }, ) From a3c3e2ae449b6c9ea52d258ec205cc4272973066 Mon Sep 17 00:00:00 2001 From: Willie Koomson Date: Sat, 24 Jun 2023 14:04:46 -0700 Subject: [PATCH 03/10] Clear all search history when the option is switched off Also, do not insert search history items when saveSearchHistory is false. --- app/src/main/java/com/jerboa/MainActivity.kt | 12 ++++---- app/src/main/java/com/jerboa/db/AppDB.kt | 15 +++++++++- .../community/list/CommunityListViewModel.kt | 28 ++++--------------- 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/com/jerboa/MainActivity.kt b/app/src/main/java/com/jerboa/MainActivity.kt index 715b23927..a49ead912 100644 --- a/app/src/main/java/com/jerboa/MainActivity.kt +++ b/app/src/main/java/com/jerboa/MainActivity.kt @@ -90,8 +90,12 @@ import com.jerboa.ui.theme.JerboaTheme class JerboaApplication : Application() { private val database by lazy { AppDB.getDatabase(this) } val accountRepository by lazy { AccountRepository(database.accountDao()) } - val appSettingsRepository by lazy { AppSettingsRepository(database.appSettingsDao()) } - val searchHistoryRepository by lazy { SearchHistoryRepository(database.searchHistoryDao()) } + val appSettingsRepository by lazy { + AppSettingsRepository(database.appSettingsDao(), database.searchHistoryDao()) + } + val searchHistoryRepository by lazy { + SearchHistoryRepository(database.searchHistoryDao(), database.appSettingsDao()) + } } class MainActivity : AppCompatActivity() { @@ -100,9 +104,7 @@ class MainActivity : AppCompatActivity() { private val personProfileViewModel by viewModels() private val inboxViewModel by viewModels() private val communityListViewModel by viewModels() { - (application as JerboaApplication).let { app -> - CommunityListViewModelFactory(app.searchHistoryRepository, app.appSettingsRepository) - } + CommunityListViewModelFactory((application as JerboaApplication).searchHistoryRepository) } private val createPostViewModel by viewModels() private val commentReplyViewModel by viewModels() diff --git a/app/src/main/java/com/jerboa/db/AppDB.kt b/app/src/main/java/com/jerboa/db/AppDB.kt index 110b4f57d..4618cb011 100644 --- a/app/src/main/java/com/jerboa/db/AppDB.kt +++ b/app/src/main/java/com/jerboa/db/AppDB.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import okhttp3.HttpUrl.Companion.toHttpUrl @@ -167,6 +168,9 @@ interface SearchHistoryDao { @Delete(entity = SearchHistory::class) suspend fun delete(item: SearchHistory) + + @Query("DELETE FROM SearchHistory") + suspend fun clear() } @Dao @@ -198,6 +202,9 @@ interface AppSettingsDao { @Query("SELECT * FROM AppSettings limit 1") fun getSettings(): LiveData + @Query("SELECT * FROM AppSettings limit 1") + fun settings(): Flow + @Update suspend fun updateAppSettings(appSettings: AppSettings) @@ -251,10 +258,14 @@ class AccountRepository(private val accountDao: AccountDao) { class SearchHistoryRepository( private val searchHistoryDao: SearchHistoryDao, + private val appSettingsDao: AppSettingsDao, ) { fun history(): Flow> = searchHistoryDao.history() - suspend fun insert(item: SearchHistory) = searchHistoryDao.insert(item) + suspend fun insert(item: SearchHistory) { + if (appSettingsDao.settings().first().saveSearchHistory) + searchHistoryDao.insert(item) + } suspend fun delete(item: SearchHistory) = searchHistoryDao.delete(item) } @@ -263,6 +274,7 @@ class SearchHistoryRepository( // instead of the whole database, because you only need access to the DAO class AppSettingsRepository( private val appSettingsDao: AppSettingsDao, + private val searchHistoryDao: SearchHistoryDao, private val httpClient: OkHttpClient = OkHttpClient(), private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO, ) { @@ -277,6 +289,7 @@ class AppSettingsRepository( @WorkerThread suspend fun update(appSettings: AppSettings) { appSettingsDao.updateAppSettings(appSettings) + if (!appSettings.saveSearchHistory) searchHistoryDao.clear() } @WorkerThread diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt index 783865d92..51b258b40 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt @@ -27,20 +27,16 @@ import com.jerboa.ui.components.home.SiteViewModel import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import java.time.Instant -class CommunityListViewModelFactory( - private val searchHistoryRepository: SearchHistoryRepository, - private val appSettingsRepository: AppSettingsRepository, -) : ViewModelProvider.Factory { +class CommunityListViewModelFactory(private val repository: SearchHistoryRepository) + : ViewModelProvider.Factory { override fun create(modelClass: Class): T { if (modelClass.isAssignableFrom(CommunityListViewModel::class.java)) { @Suppress("UNCHECKED_CAST") - return CommunityListViewModel(searchHistoryRepository, appSettingsRepository) as T + return CommunityListViewModel(repository) as T } throw IllegalArgumentException("Unknown ViewModel class") } @@ -48,14 +44,8 @@ class CommunityListViewModelFactory( class CommunityListViewModel( private val searchHistoryRepository: SearchHistoryRepository, - private val appSettingsRepository: AppSettingsRepository, ) : ViewModel(), Initializable { override var initialized by mutableStateOf(false) - init { - viewModelScope.launch { - appSettingsRepository.appSettings.observeForever { appSettings.value = it } - } - } var searchRes: ApiState by mutableStateOf(ApiState.Empty) private set @@ -63,13 +53,10 @@ class CommunityListViewModel( private set val searchHistory: Flow> = searchHistoryRepository.history() - .filter { appSettings.value?.saveSearchHistory == true } .map { history -> history.sortedByDescending { it.timestamp } } var communities: List by mutableStateOf(emptyList()) - private val appSettings = MutableStateFlow(appSettingsRepository.appSettings.value) - private var fetchCommunitiesJob: Job? = null fun searchCommunities(form: Search, debounce: Boolean = false) { fetchCommunitiesJob?.cancel() @@ -77,12 +64,9 @@ class CommunityListViewModel( if (debounce) delay(DEBOUNCE_DELAY) searchRes = ApiState.Loading searchRes = apiWrapper(API.getInstance().search(form.serializeToMap())) - form.q.takeIf { query -> - appSettings.value?.saveSearchHistory == true && query.isNotEmpty() - }?.let { query -> - searchHistoryRepository.insert( - SearchHistory(query, Instant.now().epochSecond), - ) + form.q.takeIf { it.isNotEmpty() }?.let { query -> + + searchHistoryRepository.insert(SearchHistory(query, Instant.now().epochSecond)) } } } From a673e55fe234be8fc8f136cff486bf09771f79b7 Mon Sep 17 00:00:00 2001 From: Willie Koomson Date: Sat, 24 Jun 2023 14:23:25 -0700 Subject: [PATCH 04/10] Fix formatting --- app/src/main/java/com/jerboa/db/AppDB.kt | 3 ++- .../ui/components/community/list/CommunityListViewModel.kt | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/jerboa/db/AppDB.kt b/app/src/main/java/com/jerboa/db/AppDB.kt index 4618cb011..13295fcc4 100644 --- a/app/src/main/java/com/jerboa/db/AppDB.kt +++ b/app/src/main/java/com/jerboa/db/AppDB.kt @@ -263,8 +263,9 @@ class SearchHistoryRepository( fun history(): Flow> = searchHistoryDao.history() suspend fun insert(item: SearchHistory) { - if (appSettingsDao.settings().first().saveSearchHistory) + if (appSettingsDao.settings().first().saveSearchHistory) { searchHistoryDao.insert(item) + } } suspend fun delete(item: SearchHistory) = searchHistoryDao.delete(item) diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt index 51b258b40..8a86a55aa 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt @@ -31,8 +31,8 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import java.time.Instant -class CommunityListViewModelFactory(private val repository: SearchHistoryRepository) - : ViewModelProvider.Factory { +class CommunityListViewModelFactory(private val repository: SearchHistoryRepository) : + ViewModelProvider.Factory { override fun create(modelClass: Class): T { if (modelClass.isAssignableFrom(CommunityListViewModel::class.java)) { @Suppress("UNCHECKED_CAST") From 1d7a380e1c3c632af20da390bbb8aa9dcbe658bc Mon Sep 17 00:00:00 2001 From: Willie Koomson Date: Tue, 27 Jun 2023 20:08:06 -0700 Subject: [PATCH 05/10] Add strings for search history UI --- .../community/list/CommunityListActivity.kt | 11 ++++++++--- .../community/list/CommunityListViewModel.kt | 1 - app/src/main/res/values/strings.xml | 3 +++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt index c6d00cefe..8d47e4df0 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt @@ -22,8 +22,10 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController +import com.jerboa.R import com.jerboa.api.ApiState import com.jerboa.db.AccountViewModel import com.jerboa.ui.components.common.ApiErrorText @@ -97,7 +99,7 @@ fun CommunityListActivity( ListItem( headlineContent = { Text( - text = "Recent searches", + text = stringResource(R.string.community_list_recent_searches), color = MaterialTheme.colorScheme.onBackground, style = MaterialTheme.typography.labelLarge, ) @@ -132,7 +134,10 @@ fun CommunityListActivity( content = { Icon( Icons.Rounded.Close, - contentDescription = "Delete ${it.text}", + contentDescription = stringResource( + R.string.community_list_delete_search_item, + it.text, + ), tint = MaterialTheme.colorScheme.surfaceTint, ) }, @@ -143,7 +148,7 @@ fun CommunityListActivity( ListItem( headlineContent = { Text( - text = "Communities", + text = stringResource(R.string.community_list_title), color = MaterialTheme.colorScheme.onBackground, style = MaterialTheme.typography.labelLarge, ) diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt index 8a86a55aa..c5b6d86f6 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt @@ -18,7 +18,6 @@ import com.jerboa.datatypes.types.SearchResponse import com.jerboa.datatypes.types.SearchType import com.jerboa.datatypes.types.SortType import com.jerboa.datatypes.types.SubscribedType -import com.jerboa.db.AppSettingsRepository import com.jerboa.db.SearchHistory import com.jerboa.db.SearchHistoryRepository import com.jerboa.serializeToMap diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 828f77515..d9d8fe939 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -110,6 +110,9 @@ %1$s users / month Back Search... + Communities + Recent searches + Delete %1$s Pending Refresh Sort by From d09a79d78bccc2a82e6142d1b0773db9897b05ae Mon Sep 17 00:00:00 2001 From: Willie Koomson Date: Tue, 27 Jun 2023 20:08:06 -0700 Subject: [PATCH 06/10] Add strings for search history UI --- app/src/main/java/com/jerboa/MainActivity.kt | 23 ++----------------- app/src/main/java/com/jerboa/db/AppDB.kt | 2 +- .../community/list/CommunityListActivity.kt | 14 +++++++---- .../community/list/CommunityListViewModel.kt | 1 - .../ui/components/home/BottomNavActivity.kt | 3 +++ .../lookandfeel/LookAndFeelActivity.kt | 2 +- app/src/main/res/values/strings.xml | 4 ++++ 7 files changed, 20 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/jerboa/MainActivity.kt b/app/src/main/java/com/jerboa/MainActivity.kt index a49ead912..c5d7b1f70 100644 --- a/app/src/main/java/com/jerboa/MainActivity.kt +++ b/app/src/main/java/com/jerboa/MainActivity.kt @@ -15,7 +15,6 @@ import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideOutHorizontally import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf @@ -42,9 +41,7 @@ import com.jerboa.db.AppSettingsViewModel import com.jerboa.db.AppSettingsViewModelFactory import com.jerboa.db.SearchHistoryRepository import com.jerboa.ui.components.comment.edit.CommentEditActivity -import com.jerboa.ui.components.comment.edit.CommentEditViewModel import com.jerboa.ui.components.comment.reply.CommentReplyActivity -import com.jerboa.ui.components.comment.reply.CommentReplyViewModel import com.jerboa.ui.components.comment.reply.ReplyItem import com.jerboa.ui.components.common.CommentEditDeps import com.jerboa.ui.components.common.MarkdownHelper @@ -66,17 +63,12 @@ import com.jerboa.ui.components.home.BottomNavActivity import com.jerboa.ui.components.home.SiteViewModel import com.jerboa.ui.components.home.sidebar.SiteSidebarActivity import com.jerboa.ui.components.inbox.InboxActivity -import com.jerboa.ui.components.inbox.InboxViewModel import com.jerboa.ui.components.login.LoginActivity import com.jerboa.ui.components.person.PersonProfileActivity -import com.jerboa.ui.components.person.PersonProfileViewModel import com.jerboa.ui.components.post.PostActivity import com.jerboa.ui.components.post.create.CreatePostActivity -import com.jerboa.ui.components.post.create.CreatePostViewModel import com.jerboa.ui.components.post.edit.PostEditActivity -import com.jerboa.ui.components.post.edit.PostEditViewModel import com.jerboa.ui.components.privatemessage.PrivateMessageReplyActivity -import com.jerboa.ui.components.report.CreateReportViewModel import com.jerboa.ui.components.report.comment.CreateCommentReportActivity import com.jerboa.ui.components.report.post.CreatePostReportActivity import com.jerboa.ui.components.settings.SettingsActivity @@ -100,17 +92,9 @@ class JerboaApplication : Application() { class MainActivity : AppCompatActivity() { private val siteViewModel by viewModels() - private val communityViewModel by viewModels() - private val personProfileViewModel by viewModels() - private val inboxViewModel by viewModels() private val communityListViewModel by viewModels() { CommunityListViewModelFactory((application as JerboaApplication).searchHistoryRepository) } - private val createPostViewModel by viewModels() - private val commentReplyViewModel by viewModels() - private val commentEditViewModel by viewModels() - private val postEditViewModel by viewModels() - private val createReportViewModel by viewModels() private val accountSettingsViewModel by viewModels { AccountSettingsViewModelFactory((application as JerboaApplication).accountRepository) } @@ -204,6 +188,7 @@ class MainActivity : AppCompatActivity() { siteViewModel = siteViewModel, appSettingsViewModel = appSettingsViewModel, appSettings = appSettings, + communityListViewModel = communityListViewModel, ) } @@ -377,15 +362,11 @@ class MainActivity : AppCompatActivity() { ), ) { val args = Route.CommunityListArgs(it) - // Whenever navigating here, reset the list with your followed communities - SideEffect { - communityListViewModel.setCommunityListFromFollowed(siteViewModel) - communityListViewModel.resetSearch() - } CommunityListActivity( navController = navController, accountViewModel = accountViewModel, + communityListViewModel = communityListViewModel, siteViewModel = siteViewModel, selectMode = args.select, blurNSFW = appSettings.blurNSFW, diff --git a/app/src/main/java/com/jerboa/db/AppDB.kt b/app/src/main/java/com/jerboa/db/AppDB.kt index 13295fcc4..3be9ef103 100644 --- a/app/src/main/java/com/jerboa/db/AppDB.kt +++ b/app/src/main/java/com/jerboa/db/AppDB.kt @@ -527,7 +527,7 @@ val MIGRATION_17_18 = object : Migration(17, 18) { database.execSQL( """ CREATE TABLE IF NOT EXISTS SearchHistory( - history TEXT PRIMARY KEY NOT NULL + history TEXT PRIMARY KEY NOT NULL, timestamp INTEGER NOT NULL ) """, diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt index c6d00cefe..38c61deb7 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt @@ -22,8 +22,9 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.compose.ui.res.stringResource import androidx.navigation.NavController +import com.jerboa.R import com.jerboa.api.ApiState import com.jerboa.db.AccountViewModel import com.jerboa.ui.components.common.ApiErrorText @@ -46,6 +47,7 @@ object CommunityListReturn { fun CommunityListActivity( navController: NavController, accountViewModel: AccountViewModel, + communityListViewModel: CommunityListViewModel, selectMode: Boolean = false, siteViewModel: SiteViewModel, blurNSFW: Boolean, @@ -54,7 +56,6 @@ fun CommunityListActivity( val account = getCurrentAccount(accountViewModel = accountViewModel) - val communityListViewModel: CommunityListViewModel = viewModel() InitializeRoute(communityListViewModel) { // Whenever navigating here, reset the list with your followed communities communityListViewModel.setCommunityListFromFollowed(siteViewModel) @@ -97,7 +98,7 @@ fun CommunityListActivity( ListItem( headlineContent = { Text( - text = "Recent searches", + text = stringResource(R.string.community_list_recent_searches), color = MaterialTheme.colorScheme.onBackground, style = MaterialTheme.typography.labelLarge, ) @@ -132,7 +133,10 @@ fun CommunityListActivity( content = { Icon( Icons.Rounded.Close, - contentDescription = "Delete ${it.text}", + contentDescription = stringResource( + R.string.community_list_delete_search_item, + it.text, + ), tint = MaterialTheme.colorScheme.surfaceTint, ) }, @@ -143,7 +147,7 @@ fun CommunityListActivity( ListItem( headlineContent = { Text( - text = "Communities", + text = stringResource(R.string.community_list_title), color = MaterialTheme.colorScheme.onBackground, style = MaterialTheme.typography.labelLarge, ) diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt index 8a86a55aa..c5b6d86f6 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListViewModel.kt @@ -18,7 +18,6 @@ import com.jerboa.datatypes.types.SearchResponse import com.jerboa.datatypes.types.SearchType import com.jerboa.datatypes.types.SortType import com.jerboa.datatypes.types.SubscribedType -import com.jerboa.db.AppSettingsRepository import com.jerboa.db.SearchHistory import com.jerboa.db.SearchHistoryRepository import com.jerboa.serializeToMap diff --git a/app/src/main/java/com/jerboa/ui/components/home/BottomNavActivity.kt b/app/src/main/java/com/jerboa/ui/components/home/BottomNavActivity.kt index 8d77fe460..deaf3ea13 100644 --- a/app/src/main/java/com/jerboa/ui/components/home/BottomNavActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/home/BottomNavActivity.kt @@ -38,6 +38,7 @@ import com.jerboa.ui.components.common.BottomAppBarAll import com.jerboa.ui.components.common.InitializeRoute import com.jerboa.ui.components.common.getCurrentAccount import com.jerboa.ui.components.community.list.CommunityListActivity +import com.jerboa.ui.components.community.list.CommunityListViewModel import com.jerboa.ui.components.inbox.InboxActivity import com.jerboa.ui.components.person.PersonProfileActivity @@ -59,6 +60,7 @@ fun BottomNavActivity( siteViewModel: SiteViewModel, appSettingsViewModel: AppSettingsViewModel, appSettings: AppSettings, + communityListViewModel: CommunityListViewModel, ) { val account = getCurrentAccount(accountViewModel) val ctx = LocalContext.current @@ -154,6 +156,7 @@ fun BottomNavActivity( selectMode = false, siteViewModel = siteViewModel, blurNSFW = appSettings.blurNSFW, + communityListViewModel = communityListViewModel, ) } diff --git a/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt b/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt index 1dc8c5513..8a7f967a1 100644 --- a/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt @@ -293,7 +293,7 @@ fun LookAndFeelActivity( SettingsCheckbox( state = saveSearchHistoryState, title = { - Text(text = "Save search history") + Text(text = stringResource(R.string.save_search_history)) }, onCheckedChange = { updateAppSettings() }, ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 828f77515..0f6f375e4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -110,6 +110,9 @@ %1$s users / month Back Search... + Communities + Recent searches + Delete %1$s Pending Refresh Sort by @@ -335,4 +338,5 @@ Close Language Blur NSFW images + Save search history From 9045040daf59533292f2efd7ed26b7abb0723554 Mon Sep 17 00:00:00 2001 From: Willie Koomson Date: Tue, 11 Jul 2023 20:06:10 -0700 Subject: [PATCH 07/10] in progress --- app/src/main/java/com/jerboa/MainActivity.kt | 11 ++- app/src/main/java/com/jerboa/db/AppDB.kt | 88 ++++++++++++++++++- app/src/main/java/com/jerboa/db/Migrations.kt | 20 +++++ .../community/list/CommunityList.kt | 74 ++++++++++++++++ .../community/list/CommunityListActivity.kt | 50 ++++++++++- 5 files changed, 237 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/jerboa/MainActivity.kt b/app/src/main/java/com/jerboa/MainActivity.kt index 18a9ea1f8..800d27fac 100644 --- a/app/src/main/java/com/jerboa/MainActivity.kt +++ b/app/src/main/java/com/jerboa/MainActivity.kt @@ -43,6 +43,9 @@ import com.jerboa.db.AppDB import com.jerboa.db.AppSettingsRepository import com.jerboa.db.AppSettingsViewModel import com.jerboa.db.AppSettingsViewModelFactory +import com.jerboa.db.SearchHistoryRepository +import com.jerboa.db.SearchHistoryViewModel +import com.jerboa.db.SearchHistoryViewModelFactory import com.jerboa.model.AccountSettingsViewModel import com.jerboa.model.AccountSettingsViewModelFactory import com.jerboa.model.CommunityViewModel @@ -90,7 +93,10 @@ import com.jerboa.util.ShowConfirmationDialog class JerboaApplication : Application() { private val database by lazy { AppDB.getDatabase(this) } val accountRepository by lazy { AccountRepository(database.accountDao()) } - val appSettingsRepository by lazy { AppSettingsRepository(database.appSettingsDao()) } + val appSettingsRepository by lazy { + AppSettingsRepository(database.appSettingsDao(), database.searchHistoryDao()) + } + val searchHistoryRepository by lazy { SearchHistoryRepository(database.searchHistoryDao()) } } class MainActivity : AppCompatActivity() { @@ -104,6 +110,9 @@ class MainActivity : AppCompatActivity() { private val appSettingsViewModel: AppSettingsViewModel by viewModels { AppSettingsViewModelFactory((application as JerboaApplication).appSettingsRepository) } + private val searchHistoryViewModel: SearchHistoryViewModel by viewModels { + SearchHistoryViewModelFactory((application as JerboaApplication).searchHistoryRepository) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/jerboa/db/AppDB.kt b/app/src/main/java/com/jerboa/db/AppDB.kt index b7cd7980f..32f2c8f6c 100644 --- a/app/src/main/java/com/jerboa/db/AppDB.kt +++ b/app/src/main/java/com/jerboa/db/AppDB.kt @@ -8,6 +8,7 @@ import androidx.annotation.WorkerThread import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.map import androidx.lifecycle.viewModelScope import androidx.room.* import androidx.sqlite.db.SupportSQLiteDatabase @@ -131,6 +132,11 @@ data class AppSettings( defaultValue = "1", ) val backConfirmationMode: Int, + @ColumnInfo( + name = "save_search_history", + defaultValue = "1", + ) + val saveSearchHistory: Boolean, ) val APP_SETTINGS_DEFAULT = AppSettings( @@ -152,6 +158,22 @@ val APP_SETTINGS_DEFAULT = AppSettings( blurNSFW = true, showTextDescriptionsInNavbar = true, backConfirmationMode = 1, + saveSearchHistory = true, +) + +@Entity( + foreignKeys = [ForeignKey( + entity = Account::class, + parentColumns = ["id"], + childColumns = ["account_id"], + onDelete = ForeignKey.CASCADE, + onUpdate = ForeignKey.CASCADE, + )], +) +data class SearchHistory( + @PrimaryKey(autoGenerate = true) val id: Int, + @ColumnInfo(name = "account_id") val accountId: Int, + @ColumnInfo(name = "search_term") val searchTerm: String, ) @Dao @@ -193,6 +215,21 @@ interface AppSettingsDao { suspend fun updatePostViewMode(postViewMode: Int) } +@Dao +interface SearchHistoryDao { + @Query("SELECT * FROM SearchHistory") + fun history(): LiveData> + + @Insert(onConflict = OnConflictStrategy.REPLACE, entity = SearchHistory::class) + suspend fun insert(item: SearchHistory) + + @Delete(entity = SearchHistory::class) + suspend fun delete(item: SearchHistory) + + @Query("DELETE FROM SearchHistory") + suspend fun clear() +} + // Declares the DAO as a private property in the constructor. Pass in the DAO // instead of the whole database, because you only need access to the DAO class AccountRepository(private val accountDao: AccountDao) { @@ -238,6 +275,7 @@ class AccountRepository(private val accountDao: AccountDao) { // instead of the whole database, because you only need access to the DAO class AppSettingsRepository( private val appSettingsDao: AppSettingsDao, + private val searchHistoryDao: SearchHistoryDao, private val httpClient: OkHttpClient = OkHttpClient(), private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO, ) { @@ -252,6 +290,9 @@ class AppSettingsRepository( @WorkerThread suspend fun update(appSettings: AppSettings) { appSettingsDao.updateAppSettings(appSettings) + if (!appSettings.saveSearchHistory) { + searchHistoryDao.clear() + } } @WorkerThread @@ -282,14 +323,29 @@ class AppSettingsRepository( } } +class SearchHistoryRepository( + private val searchHistoryDao: SearchHistoryDao, +) { + fun history(): LiveData> = searchHistoryDao.history() + + suspend fun insert(item: SearchHistory) { + if (appSettingsDao.settings().first().saveSearchHistory) { + searchHistoryDao.insert(item) + } + } + + suspend fun delete(item: SearchHistory) = searchHistoryDao.delete(item) +} + @Database( - version = 19, - entities = [Account::class, AppSettings::class], + version = 20, + entities = [Account::class, AppSettings::class, SearchHistory::class], exportSchema = true, ) abstract class AppDB : RoomDatabase() { abstract fun accountDao(): AccountDao abstract fun appSettingsDao(): AppSettingsDao + abstract fun searchHistoryDao(): SearchHistoryDao companion object { @Volatile @@ -399,3 +455,31 @@ class AppSettingsViewModelFactory(private val repository: AppSettingsRepository) throw IllegalArgumentException("Unknown ViewModel class") } } + +class SearchHistoryViewModel(private val repository: SearchHistoryRepository) : ViewModel() { + val searchHistory = repository.history() + .map { history -> + history + .distinctBy { it.searchTerm } + .sortedByDescending { it.id } + } + + suspend fun insert(item: SearchHistory) { + repository.insert(item) + } + + suspend fun delete(item: SearchHistory) { + repository.delete(item) + } +} + +class SearchHistoryViewModelFactory(private val repository: SearchHistoryRepository) : + ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(SearchHistoryViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return SearchHistoryViewModel(repository) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} diff --git a/app/src/main/java/com/jerboa/db/Migrations.kt b/app/src/main/java/com/jerboa/db/Migrations.kt index b8bae7ec3..62d41fe27 100644 --- a/app/src/main/java/com/jerboa/db/Migrations.kt +++ b/app/src/main/java/com/jerboa/db/Migrations.kt @@ -220,6 +220,25 @@ val MIGRATION_18_19 = object : Migration(18, 19) { } } +val MIGRATION_19_20 = object : Migration(19, 20) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(UPDATE_APP_CHANGELOG_UNVIEWED) + database.execSQL( + "ALTER TABLE AppSettings ADD COLUMN save_search_history INTEGER NOT NULL DEFAULT 1", + ) + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS SearchHistory( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + account_id INTEGER NOT NULL, + search_term TEXT NOT NULL, + FOREIGN KEY(account_id) REFERENCES Account(id) + ) + """, + ) + } +} + // Don't forget to test your migration with `./gradlew app:connectAndroidTest` val MIGRATIONS_LIST = arrayOf( MIGRATION_1_2, @@ -240,4 +259,5 @@ val MIGRATIONS_LIST = arrayOf( MIGRATION_16_17, MIGRATION_17_18, MIGRATION_18_19, + MIGRATION_19_20, ) diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityList.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityList.kt index 2a452e8da..3ba702386 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityList.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityList.kt @@ -1,32 +1,45 @@ package com.jerboa.ui.components.community.list +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.* +import androidx.compose.material.icons.rounded.Close import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.viewmodel.compose.viewModel import com.jerboa.R import com.jerboa.datatypes.sampleCommunityView import com.jerboa.datatypes.types.* +import com.jerboa.db.SearchHistory +import com.jerboa.db.SearchHistoryViewModel +import com.jerboa.model.CommunityListViewModel import com.jerboa.ui.components.common.simpleVerticalScrollbar import com.jerboa.ui.components.community.CommunityLinkLarger import com.jerboa.ui.components.community.CommunityLinkLargerWithUserCount +import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -161,3 +174,64 @@ fun SearchViewPreview() { onSearchChange = {}, ) } + +@Composable +fun SearchHistoryList( + communityListViewModel: CommunityListViewModel = viewModel(), + searchHistoryViewModel: SearchHistoryViewModel = viewModel(), + history: List, + onHistoryItemClicked: (SearchHistory) -> Unit, + onHistoryItemDeleted: (SearchHistory) -> Unit, +) { + val scope = rememberCoroutineScope() + Column { + if (history.isNotEmpty()) { + ListItem( + headlineContent = { + Text( + text = stringResource(R.string.community_list_recent_searches), + color = MaterialTheme.colorScheme.onBackground, + style = MaterialTheme.typography.labelLarge, + ) + }, + ) + } + history.forEach { + ListItem( + modifier = Modifier.clickable { onHistoryItemClicked(it) }, + headlineContent = { + Text( + text = it.searchTerm, + color = MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.bodyLarge, + ) + }, + trailingContent = { + IconButton( + onClick = { onHistoryItemDeleted(it) }, + content = { + Icon( + Icons.Rounded.Close, + contentDescription = stringResource( + R.string.community_list_delete_search_item, + it.searchTerm, + ), + tint = MaterialTheme.colorScheme.surfaceTint, + ) + }, + ) + }, + ) + } + ListItem( + headlineContent = { + Text( + text = stringResource(R.string.community_list_title), + color = MaterialTheme.colorScheme.onBackground, + style = MaterialTheme.typography.labelLarge, + ) + }, + ) + + } +} diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt index dc328077c..42ab5c905 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt @@ -1,6 +1,7 @@ package com.jerboa.ui.components.community.list import android.util.Log +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.material3.DrawerState @@ -9,7 +10,9 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue @@ -22,6 +25,7 @@ import com.jerboa.datatypes.types.Search import com.jerboa.datatypes.types.SearchType import com.jerboa.datatypes.types.SortType import com.jerboa.db.AccountViewModel +import com.jerboa.db.SearchHistoryViewModel import com.jerboa.model.CommunityListViewModel import com.jerboa.model.SiteViewModel import com.jerboa.ui.components.common.ApiEmptyText @@ -35,8 +39,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch -private var fetchCommunitiesJob: Job? = null - object CommunityListReturn { const val COMMUNITY = "community-list::return(community)" } @@ -60,9 +62,13 @@ fun CommunityListActivity( communityListViewModel.setCommunityListFromFollowed(siteViewModel) } + val searchHistoryViewModel: SearchHistoryViewModel = viewModel() + var search by rememberSaveable { mutableStateOf("") } + val searchHistory by searchHistoryViewModel.searchHistory.observeAsState() val scope = rememberCoroutineScope() + var fetchCommunitiesJob by remember { mutableStateOf(null) } Surface(color = MaterialTheme.colorScheme.background) { Scaffold( @@ -93,7 +99,45 @@ fun CommunityListActivity( }, content = { padding -> when (val communitiesRes = communityListViewModel.searchRes) { - ApiState.Empty -> ApiEmptyText() + ApiState.Empty -> { + Column( + modifier = Modifier + .padding(padding) + .imePadding() + ) { + searchHistory?.let { history -> + SearchHistoryList( + history = history, + onHistoryItemClicked = { + scope.launch { + search = it.searchTerm + communityListViewModel.searchAllCommunities( + it.text, + account?.jwt, + ) + } + }, + onHistoryItemDeleted = { + scope.launch { + searchHistoryViewModel.delete(it) + } + } + ) + } + CommunityListings( + communities = communityListViewModel.communities, + onClickCommunity = { cs -> + if (selectMode) { + communityListViewModel.selectCommunity(cs) + navController.navigateUp() + } else { + navController.navigate(route = "community/${cs.id}") + } + }, + blurNSFW = blurNSFW, + ) + } + } is ApiState.Failure -> ApiErrorText(communitiesRes.msg) ApiState.Loading -> { LoadingBar(padding) From ce1de113336fe1d685c86859f6ac840c41048458 Mon Sep 17 00:00:00 2001 From: Willie Koomson Date: Tue, 11 Jul 2023 21:48:29 -0700 Subject: [PATCH 08/10] Add separate ViewModel for SearchHistory --- app/src/main/java/com/jerboa/MainActivity.kt | 5 +- app/src/main/java/com/jerboa/db/AppDB.kt | 29 ++++--- app/src/main/java/com/jerboa/db/Migrations.kt | 10 ++- .../jerboa/model/CommunityListViewModel.kt | 18 ++--- .../community/list/CommunityList.kt | 13 ---- .../community/list/CommunityListActivity.kt | 75 ++++++++++++++++--- .../ui/components/home/BottomNavActivity.kt | 4 + .../lookandfeel/LookAndFeelActivity.kt | 6 ++ app/src/main/res/values/strings.xml | 4 + 9 files changed, 110 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/com/jerboa/MainActivity.kt b/app/src/main/java/com/jerboa/MainActivity.kt index 800d27fac..440fbb35c 100644 --- a/app/src/main/java/com/jerboa/MainActivity.kt +++ b/app/src/main/java/com/jerboa/MainActivity.kt @@ -240,6 +240,7 @@ class MainActivity : AppCompatActivity() { accountViewModel = accountViewModel, siteViewModel = siteViewModel, appSettingsViewModel = appSettingsViewModel, + searchHistoryViewModel = searchHistoryViewModel, appSettings = appSettings, drawerState = drawerState, ) @@ -422,10 +423,12 @@ class MainActivity : AppCompatActivity() { CommunityListActivity( navController = navController, accountViewModel = accountViewModel, - siteViewModel = siteViewModel, selectMode = args.select, + siteViewModel = siteViewModel, blurNSFW = appSettings.blurNSFW, drawerState = drawerState, + appSettingsViewModel = appSettingsViewModel, + searchHistoryViewModel = searchHistoryViewModel, ) } diff --git a/app/src/main/java/com/jerboa/db/AppDB.kt b/app/src/main/java/com/jerboa/db/AppDB.kt index 32f2c8f6c..a0b482ef1 100644 --- a/app/src/main/java/com/jerboa/db/AppDB.kt +++ b/app/src/main/java/com/jerboa/db/AppDB.kt @@ -171,9 +171,9 @@ val APP_SETTINGS_DEFAULT = AppSettings( )], ) data class SearchHistory( - @PrimaryKey(autoGenerate = true) val id: Int, - @ColumnInfo(name = "account_id") val accountId: Int, - @ColumnInfo(name = "search_term") val searchTerm: String, + @PrimaryKey(autoGenerate = true) val id: Int = 0, + @ColumnInfo(name = "account_id", index = true) val accountId: Int?, + @ColumnInfo(name = "search_term",) val searchTerm: String, ) @Dao @@ -223,8 +223,8 @@ interface SearchHistoryDao { @Insert(onConflict = OnConflictStrategy.REPLACE, entity = SearchHistory::class) suspend fun insert(item: SearchHistory) - @Delete(entity = SearchHistory::class) - suspend fun delete(item: SearchHistory) + @Query("DELETE FROM SearchHistory WHERE account_id = :accountId AND search_term = :searchTerm") + suspend fun delete(accountId: Int?, searchTerm: String) @Query("DELETE FROM SearchHistory") suspend fun clear() @@ -327,14 +327,16 @@ class SearchHistoryRepository( private val searchHistoryDao: SearchHistoryDao, ) { fun history(): LiveData> = searchHistoryDao.history() - - suspend fun insert(item: SearchHistory) { - if (appSettingsDao.settings().first().saveSearchHistory) { - searchHistoryDao.insert(item) + .map { history -> + history + .sortedByDescending { it.id } + .distinctBy { it.searchTerm } } - } - suspend fun delete(item: SearchHistory) = searchHistoryDao.delete(item) + suspend fun insert(item: SearchHistory) = + searchHistoryDao.insert(item) + + suspend fun delete(item: SearchHistory) = searchHistoryDao.delete(item.accountId, item.searchTerm) } @Database( @@ -458,11 +460,6 @@ class AppSettingsViewModelFactory(private val repository: AppSettingsRepository) class SearchHistoryViewModel(private val repository: SearchHistoryRepository) : ViewModel() { val searchHistory = repository.history() - .map { history -> - history - .distinctBy { it.searchTerm } - .sortedByDescending { it.id } - } suspend fun insert(item: SearchHistory) { repository.insert(item) diff --git a/app/src/main/java/com/jerboa/db/Migrations.kt b/app/src/main/java/com/jerboa/db/Migrations.kt index 62d41fe27..511f647d5 100644 --- a/app/src/main/java/com/jerboa/db/Migrations.kt +++ b/app/src/main/java/com/jerboa/db/Migrations.kt @@ -230,12 +230,18 @@ val MIGRATION_19_20 = object : Migration(19, 20) { """ CREATE TABLE IF NOT EXISTS SearchHistory( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - account_id INTEGER NOT NULL, + account_id INTEGER, search_term TEXT NOT NULL, - FOREIGN KEY(account_id) REFERENCES Account(id) + FOREIGN KEY(account_id) + REFERENCES Account(id) + ON DELETE CASCADE + ON UPDATE CASCADE ) """, ) + database.execSQL( + "CREATE INDEX index_SearchHistory_account_id ON SearchHistory(account_id)" + ) } } diff --git a/app/src/main/java/com/jerboa/model/CommunityListViewModel.kt b/app/src/main/java/com/jerboa/model/CommunityListViewModel.kt index 4c88d67b4..071b07614 100644 --- a/app/src/main/java/com/jerboa/model/CommunityListViewModel.kt +++ b/app/src/main/java/com/jerboa/model/CommunityListViewModel.kt @@ -24,6 +24,8 @@ class CommunityListViewModel : ViewModel(), Initializable { var searchRes: ApiState by mutableStateOf(ApiState.Empty) private set + var communities: List by mutableStateOf(emptyList()) + fun searchCommunities(form: Search) { viewModelScope.launch { searchRes = ApiState.Loading @@ -38,7 +40,7 @@ class CommunityListViewModel : ViewModel(), Initializable { val myFollows = myUserInfo.follows // A hack to convert communityFollowerView into CommunityView - val followsIntoCommunityViews = myFollows.map { cfv -> + communities = myFollows.map { cfv -> CommunityView( community = cfv.community, subscribed = SubscribedType.Subscribed, @@ -57,20 +59,14 @@ class CommunityListViewModel : ViewModel(), Initializable { ), ) } - - searchRes = ApiState.Success( - SearchResponse( - type_ = SearchType.Communities, - communities = followsIntoCommunityViews, - comments = emptyList(), - posts = emptyList(), - users = emptyList(), - ), - ) } } else -> {} } } + + fun resetSearch() { + searchRes = ApiState.Empty + } } diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityList.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityList.kt index 3ba702386..8904a6730 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityList.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityList.kt @@ -177,13 +177,10 @@ fun SearchViewPreview() { @Composable fun SearchHistoryList( - communityListViewModel: CommunityListViewModel = viewModel(), - searchHistoryViewModel: SearchHistoryViewModel = viewModel(), history: List, onHistoryItemClicked: (SearchHistory) -> Unit, onHistoryItemDeleted: (SearchHistory) -> Unit, ) { - val scope = rememberCoroutineScope() Column { if (history.isNotEmpty()) { ListItem( @@ -223,15 +220,5 @@ fun SearchHistoryList( }, ) } - ListItem( - headlineContent = { - Text( - text = stringResource(R.string.community_list_title), - color = MaterialTheme.colorScheme.onBackground, - style = MaterialTheme.typography.labelLarge, - ) - }, - ) - } } diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt index 42ab5c905..5dfea5e08 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt @@ -5,9 +5,11 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.material3.DrawerState +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState @@ -17,18 +19,22 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.lifecycle.map import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import com.jerboa.DEBOUNCE_DELAY +import com.jerboa.R import com.jerboa.api.ApiState import com.jerboa.datatypes.types.Search import com.jerboa.datatypes.types.SearchType import com.jerboa.datatypes.types.SortType import com.jerboa.db.AccountViewModel +import com.jerboa.db.AppSettingsViewModel +import com.jerboa.db.SearchHistory import com.jerboa.db.SearchHistoryViewModel import com.jerboa.model.CommunityListViewModel import com.jerboa.model.SiteViewModel -import com.jerboa.ui.components.common.ApiEmptyText import com.jerboa.ui.components.common.ApiErrorText import com.jerboa.ui.components.common.InitializeRoute import com.jerboa.ui.components.common.LoadingBar @@ -51,6 +57,8 @@ fun CommunityListActivity( siteViewModel: SiteViewModel, blurNSFW: Boolean, drawerState: DrawerState, + appSettingsViewModel: AppSettingsViewModel, + searchHistoryViewModel: SearchHistoryViewModel, ) { Log.d("jerboa", "got to community list activity") @@ -62,10 +70,19 @@ fun CommunityListActivity( communityListViewModel.setCommunityListFromFollowed(siteViewModel) } - val searchHistoryViewModel: SearchHistoryViewModel = viewModel() + val saveSearchHistory by remember { + appSettingsViewModel.appSettings + .map { it.saveSearchHistory } + }.observeAsState() + + val searchHistory by remember(account) { + searchHistoryViewModel.searchHistory + .map { history -> history.filter { it.accountId == account?.id }.also { + Log.e("WIDGET", "histories $history") + } } + }.observeAsState() var search by rememberSaveable { mutableStateOf("") } - val searchHistory by searchHistoryViewModel.searchHistory.observeAsState() val scope = rememberCoroutineScope() var fetchCommunitiesJob by remember { mutableStateOf(null) } @@ -82,6 +99,10 @@ fun CommunityListActivity( search = search, onSearchChange = { search = it + if (search.isEmpty()) { + communityListViewModel.resetSearch() + return@CommunityListHeader + } fetchCommunitiesJob?.cancel() fetchCommunitiesJob = scope.launch { delay(DEBOUNCE_DELAY) @@ -93,6 +114,14 @@ fun CommunityListActivity( auth = account?.jwt, ), ) + if (saveSearchHistory == true) { + searchHistoryViewModel.insert( + SearchHistory( + accountId = account?.id, + searchTerm = search.trim(), + ) + ) + } } }, ) @@ -109,12 +138,25 @@ fun CommunityListActivity( SearchHistoryList( history = history, onHistoryItemClicked = { - scope.launch { - search = it.searchTerm - communityListViewModel.searchAllCommunities( - it.text, - account?.jwt, + search = it.searchTerm + fetchCommunitiesJob?.cancel() + fetchCommunitiesJob = scope.launch { + communityListViewModel.searchCommunities( + Search( + q = it.searchTerm, + type_ = SearchType.Communities, + sort = SortType.TopAll, + auth = account?.jwt, + ) ) + if (saveSearchHistory == true) { + searchHistoryViewModel.insert( + SearchHistory( + accountId = account?.id, + searchTerm = search.trim(), + ) + ) + } } }, onHistoryItemDeleted = { @@ -124,14 +166,25 @@ fun CommunityListActivity( } ) } + ListItem( + headlineContent = { + Text( + text = stringResource(R.string.community_list_title), + color = MaterialTheme.colorScheme.onBackground, + style = MaterialTheme.typography.labelLarge, + ) + }, + ) CommunityListings( communities = communityListViewModel.communities, onClickCommunity = { cs -> if (selectMode) { - communityListViewModel.selectCommunity(cs) - navController.navigateUp() + navController.apply { + addReturn(CommunityListReturn.COMMUNITY, cs) + navigateUp() + } } else { - navController.navigate(route = "community/${cs.id}") + navController.toCommunity(id = cs.id) } }, blurNSFW = blurNSFW, diff --git a/app/src/main/java/com/jerboa/ui/components/home/BottomNavActivity.kt b/app/src/main/java/com/jerboa/ui/components/home/BottomNavActivity.kt index 9c701d244..0d31b5a83 100644 --- a/app/src/main/java/com/jerboa/ui/components/home/BottomNavActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/home/BottomNavActivity.kt @@ -43,6 +43,7 @@ import com.jerboa.api.ApiState import com.jerboa.db.AccountViewModel import com.jerboa.db.AppSettings import com.jerboa.db.AppSettingsViewModel +import com.jerboa.db.SearchHistoryViewModel import com.jerboa.fetchHomePosts import com.jerboa.loginFirstToast import com.jerboa.model.HomeViewModel @@ -84,6 +85,7 @@ fun BottomNavActivity( appSettingsViewModel: AppSettingsViewModel, appSettings: AppSettings, drawerState: DrawerState, + searchHistoryViewModel: SearchHistoryViewModel, ) { val account = getCurrentAccount(accountViewModel) val ctx = LocalContext.current @@ -177,6 +179,8 @@ fun BottomNavActivity( CommunityListActivity( navController = navController, accountViewModel = accountViewModel, + appSettingsViewModel = appSettingsViewModel, + searchHistoryViewModel = searchHistoryViewModel, selectMode = false, siteViewModel = siteViewModel, blurNSFW = appSettings.blurNSFW, diff --git a/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt b/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt index 66783c5ec..2eb4c243c 100644 --- a/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt @@ -92,6 +92,7 @@ fun LookAndFeelActivity( val secureWindowState = rememberBooleanSettingState(settings.secureWindow) val blurNSFW = rememberBooleanSettingState(settings.blurNSFW) val backConfirmationMode = rememberIntSettingState(settings.backConfirmationMode) + val saveSearchHistory = rememberBooleanSettingState(settings.saveSearchHistory) val snackbarHostState = remember { SnackbarHostState() } @@ -118,6 +119,7 @@ fun LookAndFeelActivity( showTextDescriptionsInNavbar = showTextDescriptionsInNavbar.value, blurNSFW = blurNSFW.value, backConfirmationMode = backConfirmationMode.value, + saveSearchHistory = saveSearchHistory.value, ), ) } @@ -317,6 +319,10 @@ fun LookAndFeelActivity( ) }, ) + SettingsCheckbox( + title = { Text(text = stringResource(R.string.save_search_history)) }, + state = saveSearchHistory, + onCheckedChange = { updateAppSettings() }) } }, ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eb5611a0c..33cf10a9e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -337,4 +337,8 @@ Show a confirmation dialog on exit Exit Are you sure you want to exit? + Recent searches + Delete %1$s + Communities\n + Save search history\n From 544e92da9b4af396d3d99631806b3179946af4f9 Mon Sep 17 00:00:00 2001 From: Willie Koomson Date: Thu, 13 Jul 2023 08:32:47 -0700 Subject: [PATCH 09/10] Fix formatting and add schema --- app/schemas/com.jerboa.db.AppDB/20.json | 272 ++++++++++++++++++ app/src/main/java/com/jerboa/db/AppDB.kt | 28 +- app/src/main/java/com/jerboa/db/Migrations.kt | 2 +- .../jerboa/model/CommunityListViewModel.kt | 1 - .../community/list/CommunityList.kt | 8 - .../community/list/CommunityListActivity.kt | 14 +- .../lookandfeel/LookAndFeelActivity.kt | 3 +- app/src/main/res/values/strings.xml | 4 +- 8 files changed, 301 insertions(+), 31 deletions(-) create mode 100644 app/schemas/com.jerboa.db.AppDB/20.json diff --git a/app/schemas/com.jerboa.db.AppDB/20.json b/app/schemas/com.jerboa.db.AppDB/20.json new file mode 100644 index 000000000..ba0bff18d --- /dev/null +++ b/app/schemas/com.jerboa.db.AppDB/20.json @@ -0,0 +1,272 @@ +{ + "formatVersion": 1, + "database": { + "version": 20, + "identityHash": "ae4227563fbe4492c88c55e47aa46a0c", + "entities": [ + { + "tableName": "Account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `current` INTEGER NOT NULL, `instance` TEXT NOT NULL, `name` TEXT NOT NULL, `jwt` TEXT NOT NULL, `default_listing_type` INTEGER NOT NULL DEFAULT 0, `default_sort_type` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "instance", + "columnName": "instance", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "jwt", + "columnName": "jwt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "defaultListingType", + "columnName": "default_listing_type", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "defaultSortType", + "columnName": "default_sort_type", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AppSettings", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `font_size` INTEGER NOT NULL DEFAULT 16, `theme` INTEGER NOT NULL DEFAULT 0, `theme_color` INTEGER NOT NULL DEFAULT 0, `viewed_changelog` INTEGER NOT NULL DEFAULT 0, `post_view_mode` INTEGER NOT NULL DEFAULT 0, `show_bottom_nav` INTEGER NOT NULL DEFAULT 1, `show_collapsed_comment_content` INTEGER NOT NULL DEFAULT 0, `show_comment_action_bar_by_default` INTEGER NOT NULL DEFAULT 1, `show_voting_arrows_in_list_view` INTEGER NOT NULL DEFAULT 1, `show_parent_comment_navigation_buttons` INTEGER NOT NULL DEFAULT 1, `navigate_parent_comments_with_volume_buttons` INTEGER NOT NULL DEFAULT 0, `use_custom_tabs` INTEGER NOT NULL DEFAULT 1, `use_private_tabs` INTEGER NOT NULL DEFAULT 0, `secure_window` INTEGER NOT NULL DEFAULT 0, `blur_nsfw` INTEGER NOT NULL DEFAULT 1, `show_text_descriptions_in_navbar` INTEGER NOT NULL DEFAULT 1, `backConfirmationMode` INTEGER NOT NULL DEFAULT 1, `save_search_history` INTEGER NOT NULL DEFAULT 1)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fontSize", + "columnName": "font_size", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "16" + }, + { + "fieldPath": "theme", + "columnName": "theme", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "themeColor", + "columnName": "theme_color", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "viewedChangelog", + "columnName": "viewed_changelog", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "postViewMode", + "columnName": "post_view_mode", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "showBottomNav", + "columnName": "show_bottom_nav", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "showCollapsedCommentContent", + "columnName": "show_collapsed_comment_content", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "showCommentActionBarByDefault", + "columnName": "show_comment_action_bar_by_default", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "showVotingArrowsInListView", + "columnName": "show_voting_arrows_in_list_view", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "showParentCommentNavigationButtons", + "columnName": "show_parent_comment_navigation_buttons", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "navigateParentCommentsWithVolumeButtons", + "columnName": "navigate_parent_comments_with_volume_buttons", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "useCustomTabs", + "columnName": "use_custom_tabs", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "usePrivateTabs", + "columnName": "use_private_tabs", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "secureWindow", + "columnName": "secure_window", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "blurNSFW", + "columnName": "blur_nsfw", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "showTextDescriptionsInNavbar", + "columnName": "show_text_descriptions_in_navbar", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "backConfirmationMode", + "columnName": "backConfirmationMode", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "saveSearchHistory", + "columnName": "save_search_history", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SearchHistory", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account_id` INTEGER, `search_term` TEXT NOT NULL, FOREIGN KEY(`account_id`) REFERENCES `Account`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "account_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchTerm", + "columnName": "search_term", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SearchHistory_account_id", + "unique": false, + "columnNames": [ + "account_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SearchHistory_account_id` ON `${TABLE_NAME}` (`account_id`)" + } + ], + "foreignKeys": [ + { + "table": "Account", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "account_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ae4227563fbe4492c88c55e47aa46a0c')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jerboa/db/AppDB.kt b/app/src/main/java/com/jerboa/db/AppDB.kt index a0b482ef1..e3d49b104 100644 --- a/app/src/main/java/com/jerboa/db/AppDB.kt +++ b/app/src/main/java/com/jerboa/db/AppDB.kt @@ -162,18 +162,26 @@ val APP_SETTINGS_DEFAULT = AppSettings( ) @Entity( - foreignKeys = [ForeignKey( - entity = Account::class, - parentColumns = ["id"], - childColumns = ["account_id"], - onDelete = ForeignKey.CASCADE, - onUpdate = ForeignKey.CASCADE, - )], + foreignKeys = [ + ForeignKey( + entity = Account::class, + parentColumns = ["id"], + childColumns = ["account_id"], + onDelete = ForeignKey.CASCADE, + onUpdate = ForeignKey.CASCADE, + ), + ], ) data class SearchHistory( - @PrimaryKey(autoGenerate = true) val id: Int = 0, - @ColumnInfo(name = "account_id", index = true) val accountId: Int?, - @ColumnInfo(name = "search_term",) val searchTerm: String, + @PrimaryKey(autoGenerate = true) + val id: Int = 0, + @ColumnInfo( + name = "account_id", + index = true, + ) + val accountId: Int?, + @ColumnInfo(name = "search_term") + val searchTerm: String, ) @Dao diff --git a/app/src/main/java/com/jerboa/db/Migrations.kt b/app/src/main/java/com/jerboa/db/Migrations.kt index 511f647d5..1c9698669 100644 --- a/app/src/main/java/com/jerboa/db/Migrations.kt +++ b/app/src/main/java/com/jerboa/db/Migrations.kt @@ -240,7 +240,7 @@ val MIGRATION_19_20 = object : Migration(19, 20) { """, ) database.execSQL( - "CREATE INDEX index_SearchHistory_account_id ON SearchHistory(account_id)" + "CREATE INDEX index_SearchHistory_account_id ON SearchHistory(account_id)", ) } } diff --git a/app/src/main/java/com/jerboa/model/CommunityListViewModel.kt b/app/src/main/java/com/jerboa/model/CommunityListViewModel.kt index 071b07614..9ec8334c5 100644 --- a/app/src/main/java/com/jerboa/model/CommunityListViewModel.kt +++ b/app/src/main/java/com/jerboa/model/CommunityListViewModel.kt @@ -12,7 +12,6 @@ import com.jerboa.datatypes.types.CommunityAggregates import com.jerboa.datatypes.types.CommunityView import com.jerboa.datatypes.types.Search import com.jerboa.datatypes.types.SearchResponse -import com.jerboa.datatypes.types.SearchType import com.jerboa.datatypes.types.SubscribedType import com.jerboa.serializeToMap import com.jerboa.ui.components.common.Initializable diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityList.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityList.kt index 8904a6730..739e6580c 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityList.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityList.kt @@ -2,10 +2,7 @@ package com.jerboa.ui.components.community.list import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.imePadding -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState @@ -23,23 +20,18 @@ import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.tooling.preview.Preview -import androidx.lifecycle.viewmodel.compose.viewModel import com.jerboa.R import com.jerboa.datatypes.sampleCommunityView import com.jerboa.datatypes.types.* import com.jerboa.db.SearchHistory -import com.jerboa.db.SearchHistoryViewModel -import com.jerboa.model.CommunityListViewModel import com.jerboa.ui.components.common.simpleVerticalScrollbar import com.jerboa.ui.components.community.CommunityLinkLarger import com.jerboa.ui.components.community.CommunityLinkLargerWithUserCount -import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt index 5dfea5e08..fcf8df3c3 100644 --- a/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/community/list/CommunityListActivity.kt @@ -77,9 +77,7 @@ fun CommunityListActivity( val searchHistory by remember(account) { searchHistoryViewModel.searchHistory - .map { history -> history.filter { it.accountId == account?.id }.also { - Log.e("WIDGET", "histories $history") - } } + .map { history -> history.filter { it.accountId == account?.id } } }.observeAsState() var search by rememberSaveable { mutableStateOf("") } @@ -119,7 +117,7 @@ fun CommunityListActivity( SearchHistory( accountId = account?.id, searchTerm = search.trim(), - ) + ), ) } } @@ -132,7 +130,7 @@ fun CommunityListActivity( Column( modifier = Modifier .padding(padding) - .imePadding() + .imePadding(), ) { searchHistory?.let { history -> SearchHistoryList( @@ -147,14 +145,14 @@ fun CommunityListActivity( type_ = SearchType.Communities, sort = SortType.TopAll, auth = account?.jwt, - ) + ), ) if (saveSearchHistory == true) { searchHistoryViewModel.insert( SearchHistory( accountId = account?.id, searchTerm = search.trim(), - ) + ), ) } } @@ -163,7 +161,7 @@ fun CommunityListActivity( scope.launch { searchHistoryViewModel.delete(it) } - } + }, ) } ListItem( diff --git a/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt b/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt index 2eb4c243c..802691506 100644 --- a/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/settings/lookandfeel/LookAndFeelActivity.kt @@ -322,7 +322,8 @@ fun LookAndFeelActivity( SettingsCheckbox( title = { Text(text = stringResource(R.string.save_search_history)) }, state = saveSearchHistory, - onCheckedChange = { updateAppSettings() }) + onCheckedChange = { updateAppSettings() }, + ) } }, ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 33cf10a9e..a678b4ba1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -339,6 +339,6 @@ Are you sure you want to exit? Recent searches Delete %1$s - Communities\n - Save search history\n + Communities + Save search history From c08574cb8f4173f4d09693770fd0f618bc9fa318 Mon Sep 17 00:00:00 2001 From: Willie Koomson Date: Thu, 13 Jul 2023 17:18:49 -0700 Subject: [PATCH 10/10] Fix formatting --- app/src/main/java/com/jerboa/db/AppDBContainer.kt | 1 - .../java/com/jerboa/db/repository/SearchHistoryRepository.kt | 2 +- app/src/main/java/com/jerboa/model/SearchHistoryViewModel.kt | 1 - .../java/com/jerboa/ui/components/home/BottomNavActivity.kt | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/jerboa/db/AppDBContainer.kt b/app/src/main/java/com/jerboa/db/AppDBContainer.kt index b608ccf0e..aee43ad34 100644 --- a/app/src/main/java/com/jerboa/db/AppDBContainer.kt +++ b/app/src/main/java/com/jerboa/db/AppDBContainer.kt @@ -1,7 +1,6 @@ package com.jerboa.db import android.content.Context -import com.jerboa.db.entity.SearchHistory import com.jerboa.db.repository.AccountRepository import com.jerboa.db.repository.AppSettingsRepository import com.jerboa.db.repository.SearchHistoryRepository diff --git a/app/src/main/java/com/jerboa/db/repository/SearchHistoryRepository.kt b/app/src/main/java/com/jerboa/db/repository/SearchHistoryRepository.kt index e11f72d66..c27433649 100644 --- a/app/src/main/java/com/jerboa/db/repository/SearchHistoryRepository.kt +++ b/app/src/main/java/com/jerboa/db/repository/SearchHistoryRepository.kt @@ -2,8 +2,8 @@ package com.jerboa.db.repository import androidx.lifecycle.LiveData import androidx.lifecycle.map -import com.jerboa.db.entity.SearchHistory import com.jerboa.db.dao.SearchHistoryDao +import com.jerboa.db.entity.SearchHistory class SearchHistoryRepository( private val searchHistoryDao: SearchHistoryDao, diff --git a/app/src/main/java/com/jerboa/model/SearchHistoryViewModel.kt b/app/src/main/java/com/jerboa/model/SearchHistoryViewModel.kt index a196e713e..52fbf2d91 100644 --- a/app/src/main/java/com/jerboa/model/SearchHistoryViewModel.kt +++ b/app/src/main/java/com/jerboa/model/SearchHistoryViewModel.kt @@ -1,7 +1,6 @@ package com.jerboa.model import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import com.jerboa.db.entity.SearchHistory diff --git a/app/src/main/java/com/jerboa/ui/components/home/BottomNavActivity.kt b/app/src/main/java/com/jerboa/ui/components/home/BottomNavActivity.kt index 5e30fd9d9..a9efe25fa 100644 --- a/app/src/main/java/com/jerboa/ui/components/home/BottomNavActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/home/BottomNavActivity.kt @@ -46,8 +46,8 @@ import com.jerboa.loginFirstToast import com.jerboa.model.AccountViewModel import com.jerboa.model.AppSettingsViewModel import com.jerboa.model.HomeViewModel -import com.jerboa.model.SiteViewModel import com.jerboa.model.SearchHistoryViewModel +import com.jerboa.model.SiteViewModel import com.jerboa.ui.components.common.BottomAppBarAll import com.jerboa.ui.components.common.InitializeRoute import com.jerboa.ui.components.common.getCurrentAccount