From e3696e3ac5b3c0a872dca764f718ad0eafc15f62 Mon Sep 17 00:00:00 2001 From: Trent Larson <trent@trentlarson.com> Date: Wed, 3 Apr 2024 19:54:01 -0600 Subject: [PATCH] feed filter: save the changed values to the DB, go to map if no location chosen, reload if necessary --- project.task.yaml | 1 + src/components/FeedFilters.vue | 115 +++++++++++++++++++++++++++------ src/db/tables/settings.ts | 4 ++ src/views/HomeView.vue | 28 ++++++-- src/views/SearchAreaView.vue | 5 +- 5 files changed, 129 insertions(+), 24 deletions(-) diff --git a/project.task.yaml b/project.task.yaml index 5a8b1cc..d111576 100644 --- a/project.task.yaml +++ b/project.task.yaml @@ -19,6 +19,7 @@ tasks : - .2 don't show a warning on a totally new project when the authorized agent is set - .2 anchor hash into BTC - .2 list the "show more" contacts alphabetically +- .5 add back the explicit wait for browser subscription timing problems? - .5 make Time Safari a share_target for images diff --git a/src/components/FeedFilters.vue b/src/components/FeedFilters.vue index 390205b..669e953 100644 --- a/src/components/FeedFilters.vue +++ b/src/components/FeedFilters.vue @@ -6,7 +6,10 @@ <p class="mb-4 font-bold">Show only activities that are…</p> <div class="grid grid-cols-1 gap-2"> - <div class="flex items-center justify-between cursor-pointer"> + <div + class="flex items-center justify-between cursor-pointer" + @click="toggleContacts()" + > <!-- label --> <div>From my contacts</div> <!-- toggle --> @@ -14,7 +17,7 @@ <!-- input --> <input type="checkbox" - v-model="isFromMyContacts" + v-model="isInMyContacts" name="toggleFilterFromMyContacts" class="sr-only" /> @@ -27,11 +30,18 @@ </div> </div> - <div class="flex items-center justify-between cursor-pointer"> + <div + class="flex items-center justify-between cursor-pointer" + @click=" + hasSearchBox + ? toggleNearby() + : $router.push({ name: 'search-area' }) + " + > <!-- label --> <div>Nearby</div> <!-- toggle --> - <div class="relative ml-2"> + <div v-if="hasSearchBox" class="relative ml-2"> <!-- input --> <input type="checkbox" @@ -46,26 +56,32 @@ class="dot absolute left-1 top-1 bg-slate-400 w-6 h-6 rounded-full transition" ></div> </div> + <div v-else class="relative ml-2"> + <button class="ml-2 px-4 py-2 rounded-md bg-blue-200 text-blue-500"> + Select Location + </button> + </div> </div> </div> - <button - class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mt-4" - @click="confirm" - > - Apply - </button> - <div class="grid grid-cols-1 sm:grid-cols-2 gap-2 mt-2"> + <div class="grid grid-cols-1 sm:grid-cols-3 gap-2 mt-4"> <button class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" + @click="setAll()" + > + Set All + </button> + <button + class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" + @click="clearAll()" > Clear All </button> <button class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" - @click="cancel" + @click="done()" > - Cancel + Done </button> </div> </div> @@ -81,6 +97,9 @@ import { LTileLayer, } from "@vue-leaflet/vue-leaflet"; +import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; +import { db } from "@/db/index"; + @Component({ components: { LRectangle, @@ -90,20 +109,80 @@ import { }, }) export default class FeedFilters extends Vue { - visible = false; - isFromMyContacts = false; + callOnCloseIfChanged = () => {}; + hasSearchBox = false; + isInMyContacts = false; isNearby = false; + settingChanged = false; + visible = false; + + async open(callOnCloseIfChanged: () => void) { + this.callOnCloseIfChanged = callOnCloseIfChanged; + + await db.open(); + const settings = await db.settings.get(MASTER_SETTINGS_KEY); + this.isInMyContacts = !!settings?.filterFeedContacts; + this.isNearby = !!settings?.filterFeedNearby; + if (settings?.searchBoxes && settings.searchBoxes.length > 0) { + this.hasSearchBox = true; + } - async open() { + this.settingChanged = false; this.visible = true; } + toggleContacts() { + this.settingChanged = true; + this.isInMyContacts = !this.isInMyContacts; + db.settings.update(MASTER_SETTINGS_KEY, { + filterFeedContacts: this.isInMyContacts, + }); + } + + toggleNearby() { + this.settingChanged = true; + this.isNearby = !this.isNearby; + db.settings.update(MASTER_SETTINGS_KEY, { + filterFeedNearby: this.isNearby, + }); + } + + async clearAll() { + if (this.isInMyContacts || this.isNearby) { + this.settingChanged = true; + } + + db.settings.update(MASTER_SETTINGS_KEY, { + filterFeedNearby: false, + filterFeedContacts: false, + }); + + this.isInMyContacts = false; + this.isNearby = false; + } + + async setAll() { + if (!this.isInMyContacts || !this.isNearby) { + this.settingChanged = true; + } + + db.settings.update(MASTER_SETTINGS_KEY, { + filterFeedNearby: true, + filterFeedContacts: true, + }); + + this.isInMyContacts = true; + this.isNearby = true; + } + close() { - // close the dialog but don't change values (just in case some actions are added later) + if (this.settingChanged) { + this.callOnCloseIfChanged(); + } this.visible = false; } - cancel() { + done() { this.close(); } } diff --git a/src/db/tables/settings.ts b/src/db/tables/settings.ts index f95b383..479ee63 100644 --- a/src/db/tables/settings.ts +++ b/src/db/tables/settings.ts @@ -16,6 +16,10 @@ export type Settings = { activeDid?: string; // Active Decentralized ID apiServer?: string; // API server URL + + filterFeedNearby?: string; // filter by nearby + filterFeedContacts?: string; // filter by user contacts + firstName?: string; // User's first name isRegistered?: boolean; lastName?: string; // deprecated - put all names in firstName diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index e6172ce..4194151 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -180,9 +180,9 @@ <h2 class="text-xl font-bold">Latest Activity</h2> <button @click="openFeedFilters()" - class="block text-center text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 ml-auto rounded-md" + class="block text-center text-sm uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 ml-auto rounded-md" > - Filter… + {{ resultsAreFiltered() ? "Filtered" : "Unfiltered" }} </button> </div> <InfiniteScroll @reached-bottom="loadMoreGives"> @@ -311,6 +311,8 @@ export default class HomeView extends Vue { feedPreviousOldestId?: string; feedLastViewedClaimId?: string; isCreatingIdentifier = false; + isFeedFilteredByContacts = false; + isFeedFilteredByNearby = false; isFeedLoading = true; isRegistered = false; showShortcutBvc = false; @@ -335,7 +337,7 @@ export default class HomeView extends Vue { return headers; } - async created() { + async mounted() { try { await accountsDB.open(); const allAccounts = await accountsDB.accounts.toArray(); @@ -347,6 +349,8 @@ export default class HomeView extends Vue { this.activeDid = settings?.activeDid || ""; this.allContacts = await db.contacts.toArray(); this.feedLastViewedClaimId = settings?.lastViewedClaimId; + this.isFeedFilteredByContacts = !!settings?.filterFeedContacts; + this.isFeedFilteredByNearby = !!settings?.filterFeedNearby; this.isRegistered = !!settings?.isRegistered; this.showShortcutBvc = !!settings?.showShortcutBvc; @@ -378,6 +382,10 @@ export default class HomeView extends Vue { } } + resultsAreFiltered() { + return this.isFeedFilteredByContacts || this.isFeedFilteredByNearby; + } + notificationsSupported() { return "Notification" in window; } @@ -408,6 +416,18 @@ export default class HomeView extends Vue { return headers; } + // only called when a setting was changed + async reloadFeedOnChange() { + await db.open(); + const settings = (await db.settings.get(MASTER_SETTINGS_KEY)) as Settings; + this.isFeedFilteredByContacts = !!settings?.filterFeedContacts; + this.isFeedFilteredByNearby = !!settings?.filterFeedNearby; + + this.feedData = []; + this.feedPreviousOldestId = undefined; + this.updateAllFeed(); + } + /** * Data loader used by infinite scroller * @param payload is the flag from the InfiniteScroll indicating if it should load @@ -587,7 +607,7 @@ export default class HomeView extends Vue { } openFeedFilters() { - (this.$refs.feedFilters as FeedFilters).open(); + (this.$refs.feedFilters as FeedFilters).open(this.reloadFeedOnChange); } } </script> diff --git a/src/views/SearchAreaView.vue b/src/views/SearchAreaView.vue index 4b3a750..e9c30e8 100644 --- a/src/views/SearchAreaView.vue +++ b/src/views/SearchAreaView.vue @@ -208,9 +208,9 @@ export default class DiscoverView extends Vue { group: "alert", type: "success", title: "Saved", - text: "That has been saved in your preferences.", + text: "That has been saved in your preferences. You can now filter by it on your home screen feed.", }, - -1, + 7000, ); this.$router.back(); } catch (err) { @@ -246,6 +246,7 @@ export default class DiscoverView extends Vue { await db.open(); db.settings.update(MASTER_SETTINGS_KEY, { searchBoxes: [], + filterFeedNearby: false, }); this.searchBox = null; this.localCenterLat = 0;