Skip to content

Handled filtering snippets by language #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 59 additions & 45 deletions app/src/main/java/pl/tkadziolka/snipmeandroid/bridge/Bridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@
package pl.tkadziolka.snipmeandroid.bridge;

import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MessageCodec;
import io.flutter.plugin.common.StandardMessageCodec;

import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.HashMap;

import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MessageCodec;
import io.flutter.plugin.common.StandardMessageCodec;

/**Generated class from Pigeon. */
@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"})
Expand Down Expand Up @@ -562,33 +563,48 @@ public static final class Builder {

/** Generated class from Pigeon that represents data sent in messages. */
public static class SnippetFilter {
private @Nullable SnippetFilterType type;
public @Nullable SnippetFilterType getType() { return type; }
public void setType(@Nullable SnippetFilterType setterArg) {
this.type = setterArg;
private @Nullable List<String> languages;
public @Nullable List<String> getLanguages() { return languages; }
public void setLanguages(@Nullable List<String> setterArg) {
this.languages = setterArg;
}

private @Nullable List<String> selectedLanguages;
public @Nullable List<String> getSelectedLanguages() { return selectedLanguages; }
public void setSelectedLanguages(@Nullable List<String> setterArg) {
this.selectedLanguages = setterArg;
}

public static final class Builder {
private @Nullable SnippetFilterType type;
public @NonNull Builder setType(@Nullable SnippetFilterType setterArg) {
this.type = setterArg;
private @Nullable List<String> languages;
public @NonNull Builder setLanguages(@Nullable List<String> setterArg) {
this.languages = setterArg;
return this;
}
private @Nullable List<String> selectedLanguages;
public @NonNull Builder setSelectedLanguages(@Nullable List<String> setterArg) {
this.selectedLanguages = setterArg;
return this;
}
public @NonNull SnippetFilter build() {
SnippetFilter pigeonReturn = new SnippetFilter();
pigeonReturn.setType(type);
pigeonReturn.setLanguages(languages);
pigeonReturn.setSelectedLanguages(selectedLanguages);
return pigeonReturn;
}
}
@NonNull Map<String, Object> toMap() {
Map<String, Object> toMapResult = new HashMap<>();
toMapResult.put("type", type == null ? null : type.index);
toMapResult.put("languages", languages);
toMapResult.put("selectedLanguages", selectedLanguages);
return toMapResult;
}
static @NonNull SnippetFilter fromMap(@NonNull Map<String, Object> map) {
SnippetFilter pigeonResult = new SnippetFilter();
Object type = map.get("type");
pigeonResult.setType(type == null ? null : SnippetFilterType.values()[(int)type]);
Object languages = map.get("languages");
pigeonResult.setLanguages((List<String>)languages);
Object selectedLanguages = map.get("selectedLanguages");
pigeonResult.setSelectedLanguages((List<String>)selectedLanguages);
return pigeonResult;
}
}
Expand All @@ -613,6 +629,12 @@ public void setData(@Nullable List<Snippet> setterArg) {
this.data = setterArg;
}

private @Nullable SnippetFilter filter;
public @Nullable SnippetFilter getFilter() { return filter; }
public void setFilter(@Nullable SnippetFilter setterArg) {
this.filter = setterArg;
}

private @Nullable String error;
public @Nullable String getError() { return error; }
public void setError(@Nullable String setterArg) {
Expand Down Expand Up @@ -647,6 +669,11 @@ public static final class Builder {
this.data = setterArg;
return this;
}
private @Nullable SnippetFilter filter;
public @NonNull Builder setFilter(@Nullable SnippetFilter setterArg) {
this.filter = setterArg;
return this;
}
private @Nullable String error;
public @NonNull Builder setError(@Nullable String setterArg) {
this.error = setterArg;
Expand All @@ -667,6 +694,7 @@ public static final class Builder {
pigeonReturn.setState(state);
pigeonReturn.setIs_loading(is_loading);
pigeonReturn.setData(data);
pigeonReturn.setFilter(filter);
pigeonReturn.setError(error);
pigeonReturn.setOldHash(oldHash);
pigeonReturn.setNewHash(newHash);
Expand All @@ -678,6 +706,7 @@ public static final class Builder {
toMapResult.put("state", state == null ? null : state.index);
toMapResult.put("is_loading", is_loading);
toMapResult.put("data", data);
toMapResult.put("filter", (filter == null) ? null : filter.toMap());
toMapResult.put("error", error);
toMapResult.put("oldHash", oldHash);
toMapResult.put("newHash", newHash);
Expand All @@ -691,6 +720,8 @@ public static final class Builder {
pigeonResult.setIs_loading((Boolean)is_loading);
Object data = map.get("data");
pigeonResult.setData((List<Snippet>)data);
Object filter = map.get("filter");
pigeonResult.setFilter((filter == null) ? null : SnippetFilter.fromMap((Map)filter));
Object error = map.get("error");
pigeonResult.setError((String)error);
Object oldHash = map.get("oldHash");
Expand Down Expand Up @@ -1186,8 +1217,7 @@ public interface MainModelBridge {
@NonNull MainModelEventData getEvent();
void resetEvent();
void initState();
void loadNextPage();
void filter(@NonNull SnippetFilter filter);
void filterLanguage(@NonNull String language, @NonNull Boolean isSelected);
void logOut();
void refreshSnippetUpdates();

Expand Down Expand Up @@ -1276,38 +1306,22 @@ static void setup(BinaryMessenger binaryMessenger, MainModelBridge api) {
{
BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue();
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.MainModelBridge.loadNextPage", getCodec(), taskQueue);
if (api != null) {
channel.setMessageHandler((message, reply) -> {
Map<String, Object> wrapped = new HashMap<>();
try {
api.loadNextPage();
wrapped.put("result", null);
}
catch (Error | RuntimeException exception) {
wrapped.put("error", wrapError(exception));
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
{
BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue();
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.MainModelBridge.filter", getCodec(), taskQueue);
new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.MainModelBridge.filterLanguage", getCodec(), taskQueue);
if (api != null) {
channel.setMessageHandler((message, reply) -> {
Map<String, Object> wrapped = new HashMap<>();
try {
ArrayList<Object> args = (ArrayList<Object>)message;
assert args != null;
SnippetFilter filterArg = (SnippetFilter)args.get(0);
if (filterArg == null) {
throw new NullPointerException("filterArg unexpectedly null.");
String languageArg = (String)args.get(0);
if (languageArg == null) {
throw new NullPointerException("languageArg unexpectedly null.");
}
Boolean isSelectedArg = (Boolean)args.get(1);
if (isSelectedArg == null) {
throw new NullPointerException("isSelectedArg unexpectedly null.");
}
api.filter(filterArg);
api.filterLanguage(languageArg, isSelectedArg);
wrapped.put("result", null);
}
catch (Error | RuntimeException exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.flow.MutableStateFlow
import pl.tkadziolka.snipmeandroid.bridge.session.SessionModel
import pl.tkadziolka.snipmeandroid.domain.error.exception.*
import pl.tkadziolka.snipmeandroid.domain.filter.FilterSnippetsByLanguageUseCase
import pl.tkadziolka.snipmeandroid.domain.filter.GetLanguageFiltersUseCase
import pl.tkadziolka.snipmeandroid.domain.filter.SNIPPET_LANGUAGE_FILTER_ALL
import pl.tkadziolka.snipmeandroid.domain.filter.UpdateSnippetFiltersLanguageUseCase
import pl.tkadziolka.snipmeandroid.domain.message.ErrorMessages
import pl.tkadziolka.snipmeandroid.domain.snippet.ObserveUpdatedSnippetPageUseCase
import pl.tkadziolka.snipmeandroid.domain.snippet.ResetUpdatedSnippetPageUseCase
import pl.tkadziolka.snipmeandroid.domain.snippets.GetSnippetsUseCase
import pl.tkadziolka.snipmeandroid.domain.snippets.HasMoreSnippetPagesUseCase
import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetScope
import pl.tkadziolka.snipmeandroid.domain.snippets.*
import pl.tkadziolka.snipmeandroid.domain.user.GetSingleUserUseCase
import pl.tkadziolka.snipmeandroid.domain.user.User
import pl.tkadziolka.snipmeandroid.ui.error.ErrorParsable
Expand All @@ -29,23 +31,36 @@ class MainModel(
private val observeUpdatedPage: ObserveUpdatedSnippetPageUseCase,
private val resetUpdatedPage: ResetUpdatedSnippetPageUseCase,
private val hasMore: HasMoreSnippetPagesUseCase,
private val getLanguageFilters: GetLanguageFiltersUseCase,
private val filterSnippetsByLanguage: FilterSnippetsByLanguageUseCase,
private val updateFilterLanguage: UpdateSnippetFiltersLanguageUseCase,
private val session: SessionModel
) : ErrorParsable {
private val disposables = CompositeDisposable()
private var shouldRefresh = false

private val mutableEvent = MutableStateFlow<MainEvent>(Startup)
val event = mutableEvent

private val mutableState = MutableStateFlow<MainViewState>(Loading)
val state = mutableState

private var cachedSnippets = emptyList<Snippet>()
private var shouldRefresh = false
private var filterState = SnippetFilters(
languages = listOf(SNIPPET_LANGUAGE_FILTER_ALL),
selectedLanguages = listOf(SNIPPET_LANGUAGE_FILTER_ALL),
scope = SnippetScope.ALL
)

override fun parseError(throwable: Throwable) {
when (throwable) {
is ConnectionException -> mutableState.value = Error(errorMessages.parse(throwable))
is ContentNotFoundException -> mutableState.value = Error(errorMessages.parse(throwable))
is ForbiddenActionException -> mutableState.value = Error(errorMessages.parse(throwable))
is NetworkNotAvailableException -> mutableState.value = Error(errorMessages.parse(throwable))
is ContentNotFoundException -> mutableState.value =
Error(errorMessages.parse(throwable))
is ForbiddenActionException -> mutableState.value =
Error(errorMessages.parse(throwable))
is NetworkNotAvailableException -> mutableState.value =
Error(errorMessages.parse(throwable))
is NotAuthorizedException -> session.logOut { mutableEvent.value = Logout }
is RemoteException -> mutableState.value = Error(errorMessages.parse(throwable))
is SessionExpiredException -> session.logOut { mutableEvent.value = Logout }
Expand All @@ -54,7 +69,7 @@ class MainModel(
}

fun initState() {
mutableState.value = (Loading)
mutableState.value = Loading
getUser()
.subscribeOn(Schedulers.io())
.subscribeBy(
Expand All @@ -66,23 +81,15 @@ class MainModel(
).also { disposables += it }
}

fun loadNextPage() {
getLoadedState()?.let { state ->
hasMore(state.scope, state.pages)
.subscribeOn(Schedulers.io())
.subscribeBy(
onSuccess = { hasMore ->
if (hasMore) loadSnippets(state.user, pages = state.pages + ONE_PAGE)
},
onError = {
Timber.e("Couldn't check next page, error = $it")
mutableEvent.value = Alert(errorMessages.parse(it))
})
.also { disposables += it }
fun filterLanguage(language: String, isSelected: Boolean) {
getLoadedState()?.let {
filterState = updateFilterLanguage(filterState, language, isSelected)
val filteredSnippets = filterSnippetsByLanguage(cachedSnippets, filterState.selectedLanguages)
state.value = it.copy(snippets = filteredSnippets, filters = filterState)
}
}

fun filter(filter: SnippetFilter) {
fun filterScope(filter: SnippetFilter) {
val scope = filterToScope(filter)
getLoadedState()?.let { state ->
loadSnippets(state.user, pages = ONE_PAGE, scope = scope)
Expand All @@ -109,6 +116,24 @@ class MainModel(
}
}

private fun loadNextPage() {
getLoadedState()?.let { state ->
hasMore(state.filters.scope, state.pages)
.subscribeOn(Schedulers.io())
.subscribeBy(
onSuccess = { hasMore ->
if (hasMore) {
loadSnippets(state.user, pages = state.pages + ONE_PAGE)
}
},
onError = {
Timber.e("Couldn't check next page, error = $it")
mutableEvent.value = Alert(errorMessages.parse(it))
})
.also { disposables += it }
}
}

private fun loadSnippets(
user: User,
pages: Int = 1,
Expand All @@ -118,7 +143,15 @@ class MainModel(
.subscribeOn(Schedulers.io())
.subscribeBy(
onSuccess = {
mutableState.value = (Loaded(user, it, pages, scope))
cachedSnippets = it
val updatedFilters = getLanguageFilters(cachedSnippets)
filterState = filterState.copy(languages = updatedFilters)
mutableState.value = Loaded(
user,
it,
pages,
filterState
)
loadNextPage()
if (shouldRefresh) {
mutableEvent.value = ListRefreshed
Expand All @@ -143,7 +176,7 @@ class MainModel(

private fun getScope(): SnippetScope {
getLoadedState()?.let {
return it.scope
return it.filters.scope
}
return SnippetScope.ALL
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import pl.tkadziolka.snipmeandroid.bridge.Bridge
import pl.tkadziolka.snipmeandroid.bridge.ModelPlugin
import pl.tkadziolka.snipmeandroid.bridge.toModelData
import pl.tkadziolka.snipmeandroid.domain.snippets.Snippet
import pl.tkadziolka.snipmeandroid.domain.snippets.SnippetFilters
import pl.tkadziolka.snipmeandroid.ui.main.*
import pl.tkadziolka.snipmeandroid.util.view.SnippetFilter

Expand Down Expand Up @@ -33,14 +34,8 @@ class MainModelPlugin : ModelPlugin<Bridge.MainModelBridge>(), Bridge.MainModelB
model.initState()
}

override fun loadNextPage() {
model.loadNextPage()
}

override fun filter(filter: Bridge.SnippetFilter) {
val type = (filter.type?.name ?: Bridge.SnippetFilterType.ALL.name).uppercase()
val snippetFilter = SnippetFilter.valueOf(type)
model.filter(snippetFilter)
override fun filterLanguage(language: String, isSelected: Boolean) {
model.filterLanguage(language, isSelected)
}

override fun logOut() {
Expand All @@ -56,6 +51,7 @@ class MainModelPlugin : ModelPlugin<Bridge.MainModelBridge>(), Bridge.MainModelB
state = viewState.toModelState()
is_loading = viewState is Loading
data = (viewState as? Loaded)?.snippets?.toModelData()
filter = (viewState as? Loaded)?.filters?.toModelFilter()
oldHash = oldState?.hashCode()?.toLong()
newHash = viewState.hashCode().toLong()
}.also {
Expand Down Expand Up @@ -89,4 +85,12 @@ class MainModelPlugin : ModelPlugin<Bridge.MainModelBridge>(), Bridge.MainModelB
}

private fun List<Snippet>.toModelData() = map { it.toModelData() }

private fun SnippetFilters.toModelFilter(): Bridge.SnippetFilter {
val it = this
return Bridge.SnippetFilter().apply {
languages = it.languages
selectedLanguages = it.selectedLanguages
}
}
}
Loading