Skip to content
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

feat(ui): persist all data to localStorage #144

Merged
merged 85 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
ad2f164
feat(ui): persist all data to localStorage
mattyg May 25, 2023
a3d1f7b
fix(ui): cache feed mews by agent key
mattyg May 25, 2023
54de039
refactor(ui): cache vue-request data to pinia notifications store, pe…
mattyg May 25, 2023
d313d51
chore: bump flake lock
mattyg May 25, 2023
b1e64f0
fix(ui): remove abstraction for defining tag routes, fix HashtagMewsF…
mattyg May 25, 2023
207c63d
feat(dna): add crate hc_link_pagination for keeping pagination logic,…
mattyg May 26, 2023
b4f9cab
feat(dna): pagination for getting mews by hashtag, cashtag, mention &…
mattyg May 26, 2023
755de8f
chore(ui): use base64 hashes in vue-request cache key
mattyg May 26, 2023
a760e15
feat(ui): infinite scroll, 10 items per page, on hashtag, cashtag, me…
mattyg May 26, 2023
6b82cc3
Merge branch 'feat/notifications-page' into feat/persist-localstorage
mattyg May 26, 2023
cdd5d82
feat(ui): if user's mews feed is empty, redirect to /discover and rem…
mattyg May 27, 2023
1af8097
chore: flake lock
mattyg May 27, 2023
f266041
feat(ui): specify 2s timeout for error toasts
mattyg May 27, 2023
3364e0e
Merge branch 'feat/persist-localstorage' into feat/time-indexing-pagi…
mattyg May 27, 2023
524dc42
chore(ui): remove unused prop
mattyg May 27, 2023
7536596
fix(dna): ensure less than full pages are returned in hash pagination
mattyg May 27, 2023
a61993e
fix(ui): use base64 hash for cache keys
mattyg May 28, 2023
4bb8357
refactor(ui): replace vue-request with @tanstack/query, infinite scro…
mattyg May 28, 2023
4e2d15e
feat: timestamp-based pagination, infinite scroll of notifications pa…
mattyg May 29, 2023
e84c79b
chore(dna): fmt + clippy
mattyg May 29, 2023
4b99739
chore(dna): remove unnecessary variable
mattyg May 29, 2023
e2b3765
refactor(ui): use @tanstack/query for DiscoverCreators page
mattyg May 29, 2023
cda44b0
chore(ui): rename more dumb render components to Base- as per vue con…
mattyg May 29, 2023
1d8f7c2
refactor(ui): use @tanstack/query for AgentProfile page
mattyg May 29, 2023
6d7a8b4
feat(ui): add new page for all agent mews with infinite scrolling, li…
mattyg May 29, 2023
a865d5f
feat(ui): remove FolloweesList / FollowersList components & move logi…
mattyg May 30, 2023
e292b57
feat(dna): specify sort direction in hash pagination and timestamp pa…
mattyg May 30, 2023
b1376dd
feat(ui): infinite scroll and pagination of MewsFeed
mattyg May 30, 2023
2614e21
feat(ui): adjust default cache & stale time
mattyg May 30, 2023
5fbedf9
feat(ui): make refetch on mount, reconnect and window focus the defau…
mattyg May 30, 2023
a74b996
feat(ui): use @tanstack/query on MewYarn page with infinite scrolling…
mattyg May 30, 2023
8d5b187
feat(ui): fix reactively updating replies list in MewYarn
mattyg May 30, 2023
af47c0e
fix(ui): clear query cache when client pubkey changes
mattyg May 30, 2023
79a5c31
feat(ui): finish removing vue-request
mattyg May 30, 2023
d61a1af
chore(dna): clippy + fmt
mattyg May 30, 2023
85dab2d
Merge pull request #145 from GeekGene/fix/tag-page-routes
mattyg May 30, 2023
8b85885
feat(ui): default cache max persistance max aget to 2 hours
mattyg May 30, 2023
4ebe34f
chore: bump flake lock
mattyg May 30, 2023
15346b1
fix(ui): disable refetch on mount, focus, reconnect for DiscoverCreat…
mattyg May 30, 2023
7a3c90e
refactor(ui): remove newUser store, instead use simple helper functio…
mattyg May 30, 2023
49ec33b
fix(ui): show success message on create mew success only
mattyg May 30, 2023
7ca5d8a
fix(ui): reactively update notifications page when mews within it are…
mattyg May 30, 2023
e3544d0
fix(ui): prevent query cache collisions
mattyg May 30, 2023
47bae7f
chore(ui): remove log
mattyg May 30, 2023
dfbe0ba
fix(ui): prevent console errors when query data not loaded yet
mattyg May 30, 2023
81532e9
fix(ui): fetch random tags again on mount of DiscoverCreators, only i…
mattyg May 30, 2023
bc6a8f9
revert(ui): remove regex typeahead (used to *not* match urls starting…
mattyg May 30, 2023
6af9a66
feat(ui): visually distinguish 'pin' and 'delete' icons from the rest…
mattyg May 30, 2023
1457dd6
Merge branch 'feat/persist-localstorage' into feat/time-indexing-pagi…
mattyg May 31, 2023
b736934
fix(ui): quirk where mewsfeed does not re-fetch after creating a mew …
mattyg Jun 6, 2023
1f6f22d
fix(ui): avoid console error on agent profile page when no authored mews
mattyg Jun 6, 2023
03f0580
fix(ui): display empty message on discover creators page when no mews
mattyg Jun 6, 2023
8e4b361
chore: bump flake lock
mattyg Jun 6, 2023
d197200
fix(test): default pagination sort order to descending, fix tags-to-m…
mattyg Jun 12, 2023
9eaf91f
fix(test): follower-to-creators tests
mattyg Jun 12, 2023
b24c37b
fix(test): failing tests due to switching to default descending order
mattyg Jun 13, 2023
4e33b1b
chore(dna): rename functions for clarity
mattyg Jun 13, 2023
7c5c862
chore: bump flake lock
mattyg Jun 13, 2023
0daf973
chore(ui): remove debugging console logs
mattyg Jun 13, 2023
636a4c8
fix(ui): only display bio + location fields if they have values, ensu…
mattyg Jun 13, 2023
92c1d09
fix(ui): missing import
mattyg Jun 13, 2023
bf0286f
feat(ui): add 'back' link to all list view pages
mattyg Jun 13, 2023
be80f53
fix(ui): add horizontal padding to follower / creator list items
mattyg Jun 13, 2023
7566257
build(ui): bump vite to resolve security vulnerability
mattyg Jun 13, 2023
9e44a77
fix(ui): persist notificationsRead to localstorage
mattyg Jun 13, 2023
355ad3c
build: bump vue, pinia
mattyg Jun 13, 2023
9977acc
refactor(ui): replace QSelect-based search input with headlessui-base…
mattyg Jun 14, 2023
b0c3085
test: fix test that was not testing the correct function and uncommen…
mattyg Jun 15, 2023
e29be9a
chore(dna): delete unused functions & tests
mattyg Jun 15, 2023
e4b0b0b
build: bump flake lock
mattyg Jun 15, 2023
128bca3
test: fix erroring test
mattyg Jun 15, 2023
c787fc0
build(ui): bump vite for security fix
mattyg Jun 15, 2023
a424083
chore(dna): remove unused import
mattyg Jun 15, 2023
c4bc6e0
feat(ui): delete all but 1st cached infinite query pages upon leaving…
mattyg Jun 15, 2023
4522055
Merge branch 'feat/time-indexing-pagination' into fix/minor-quirks
mattyg Jun 15, 2023
0547034
fix(ui): make search everything input placeholder white in webkit bro…
mattyg Jun 15, 2023
078bbdc
fix(ui): make searcheverythinginput results display as selected when …
mattyg Jun 15, 2023
2360454
refactor(ui): extract back button into its own component
mattyg Jun 15, 2023
d02abd4
chore: fmt
mattyg Jun 16, 2023
581ba2a
test: attempt to fix flapping test
mattyg Jun 16, 2023
9db9e68
Merge pull request #146 from GeekGene/feat/time-indexing-pagination
mattyg Jun 16, 2023
42c75e5
Merge branch 'develop' into feat/persist-localstorage
mattyg Jun 16, 2023
25f21da
Merge branch 'feat/persist-localstorage' into fix/minor-quirks
mattyg Jun 16, 2023
fc839e9
test: extend pause for flapping tests
mattyg Jun 16, 2023
edfe3a6
Merge pull request #148 from GeekGene/fix/minor-quirks
mattyg Jun 16, 2023
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
6 changes: 3 additions & 3 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"lodash": "^4.17.21",
"material-icons": "^1.13.6",
"pinia": "^2.1.3",
"pinia-plugin-persistedstate": "^3.1.0",
"quasar": "^2.12.0",
"rollup-plugin-node-polyfills": "^0.2.1",
"vue": "^3.2.40",
Expand Down
2 changes: 2 additions & 0 deletions ui/src/components/AgentProfilesList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { showError } from "@/utils/toasts";
import { watch } from "vue";
import BaseAgentProfilesList from "./BaseAgentProfilesList.vue";
import { useRequest } from "vue-request";
import { localStorageCacheSettings } from "@/utils/requests";

const props = defineProps<{
fetchFn: () => Promise<AgentProfile[]>;
Expand All @@ -23,6 +24,7 @@ const { data, loading, error } = useRequest(props.fetchFn, {
refreshOnWindowFocus: true,
refocusTimespan: 25000, // 25 seconds between window focus to trigger refresh
loadingDelay: 1000,
...localStorageCacheSettings,
});
watch(error, showError);
</script>
31 changes: 16 additions & 15 deletions ui/src/components/MewList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { useRequest } from "vue-request";
import BaseMewList from "@/components/BaseMewList.vue";
import CreateMewField from "@/components/CreateMewField.vue";
import { ActionHash } from "@holochain/client";
import { localStorageCacheSettings } from "@/utils/requests";

const props = withDefaults(
defineProps<{
Expand All @@ -58,25 +59,25 @@ const props = withDefaults(
);
const emit = defineEmits(["mew-pinned", "mew-unpinned"]);

const { data, loading, error, mutate } = useRequest(
props.fetchFn,
{
cacheKey: props.cacheKey,
const { data, loading, error, mutate } = useRequest(props.fetchFn, {
cacheKey: props.cacheKey,

// run request again every 2m
pollingInterval: 2 * 60 * 1000,
// run request again every 2m
pollingInterval: 2 * 60 * 1000,

// 10s between window focus to trigger refresh
refocusTimespan: 10 * 1000,
refreshOnWindowFocus: true,
// 10s between window focus to trigger refresh
refocusTimespan: 10 * 1000,
refreshOnWindowFocus: true,

// wait for response for 10s before loading = true
loadingDelay: 1000,
// wait for response for 10s before loading = true
loadingDelay: 1000,

// Overwrite options with provided prop requestOptions
...props.requestOptions,
}
);
// cache to local storage
...localStorageCacheSettings,

// Overwrite options with provided prop requestOptions
...props.requestOptions,
});
watch(error, showError);

const onCreateMew = async (feedMew: FeedMew) => {
Expand Down
2 changes: 2 additions & 0 deletions ui/src/components/MewListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import BaseMewListItem from "@/components/BaseMewListItem.vue";
import MewListItemSkeleton from "@/components/MewListItemSkeleton.vue";
import { useRequest } from "vue-request";
import { FeedMew } from "@/types/types";
import { localStorageCacheSettings } from "@/utils/requests";

const client = (inject("client") as ComputedRef<AppAgentClient>).value;

Expand All @@ -48,6 +49,7 @@ const {
} = useRequest(fetchMew, {
cacheKey: `mews/get_mew_with_context/${props.actionHash}`,
loadingDelay: 1000,
...localStorageCacheSettings,
});
watch(error, showError);
watch(mew, (newMew) => {
Expand Down
2 changes: 2 additions & 0 deletions ui/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { createPinia } from "pinia";
import VueObserveVisibility from "vue-observe-visibility";
import App from "./App.vue";
import { router } from "./router";
import piniaPluginPersistedState from "pinia-plugin-persistedstate";

// Shoelace
import "@shoelace-style/shoelace/dist/themes/light.css";
Expand All @@ -21,6 +22,7 @@ setBasePath("shoelace");

const app = createApp(App);
const pinia = createPinia();
pinia.use(piniaPluginPersistedState);
app.use(pinia);
app.use(router);
app.use(Quasar, {
Expand Down
2 changes: 2 additions & 0 deletions ui/src/pages/AgentProfile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ import { useRoute, useRouter } from "vue-router";
import MewList from "@/components/MewList.vue";
import { AppAgentClient } from "@holochain/client";
import { useRequest } from "vue-request";
import { localStorageCacheSettings } from "@/utils/requests";

const profilesStore = (inject("profilesStore") as ComputedRef<ProfilesStore>)
.value;
Expand Down Expand Up @@ -205,6 +206,7 @@ const {
refreshOnWindowFocus: true,
refocusTimespan: 25000, // 25 seconds between window focus to trigger refresh
loadingDelay: 1000,
...localStorageCacheSettings,
});
watch(error, showError);

Expand Down
2 changes: 2 additions & 0 deletions ui/src/pages/DiscoverCreators.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import { AppAgentClient } from "@holochain/client";
import MewList from "@/components/MewList.vue";
import { FeedMew } from "@/types/types";
import { useRequest } from "vue-request";
import { localStorageCacheSettings } from "@/utils/requests";

const client = (inject("client") as ComputedRef<AppAgentClient>).value;
const forceReloadMewsListsKey = ref(0);
Expand All @@ -82,6 +83,7 @@ const fetchRandomTags = (): Promise<string[]> =>

const { data: tags, run: runFetchRandomTags } = useRequest(fetchRandomTags, {
cacheKey: `get_random_tags`,
...localStorageCacheSettings,
});

const fetchMewsWithRandomTag = (tag: string) => {
Expand Down
12 changes: 8 additions & 4 deletions ui/src/pages/MewsFeed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
:show-create-mew-field="true"
:fetch-fn="fetchMewsFeed"
title="Your Mews Feed"
:cache-key="`mews/get_my_followed_creators_mews_with_context`"
:cache-key="`mews/get_followed_creators_mews_with_context/${encodeHashToBase64(
client.myPubKey
)}`"
:insert-responses="true"
/>
</QPage>
Expand All @@ -14,14 +16,15 @@
<script setup lang="ts">
import { QPage } from "quasar";
import { pageHeightCorrection } from "@/utils/page-layout";
import { AppAgentClient } from "@holochain/client";
import { AppAgentClient, encodeHashToBase64 } from "@holochain/client";
import { ComputedRef, inject, watch, ref } from "vue";
import { FeedMew } from "@/types/types";
import MewList from "@/components/MewList.vue";
import { useRouter } from "vue-router";
import { ROUTES } from "@/router";
import { useRequest } from "vue-request";
import { Profile } from "@holochain-open-dev/profiles";
import { localStorageCacheSettings } from "@/utils/requests";

const client = (inject("client") as ComputedRef<AppAgentClient>).value;
const myProfile = (inject("myProfile") as ComputedRef<Profile>).value;
Expand All @@ -33,8 +36,8 @@ const fetchMewsFeed = (): Promise<FeedMew[]> =>
client.callZome({
role_name: "mewsfeed",
zome_name: "mews",
fn_name: "get_my_followed_creators_mews_with_context",
payload: null,
fn_name: "get_followed_creators_mews_with_context",
payload: client.myPubKey,
});

const fetchFollowedCreators = (): Promise<FeedMew[]> =>
Expand All @@ -48,6 +51,7 @@ const fetchFollowedCreators = (): Promise<FeedMew[]> =>
const { data } = useRequest(fetchFollowedCreators, {
cacheKey: `follows/get_creators_for_follower/${client.myPubKey}`,
loadingDelay: 1000,
...localStorageCacheSettings,
});

watch(data, (val) => {
Expand Down
146 changes: 93 additions & 53 deletions ui/src/stores/notifications.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { AppAgentClient, encodeHashToBase64 } from "@holochain/client";
import {
AgentPubKey,
AppAgentClient,
decodeHashFromBase64,
encodeHashToBase64,
} from "@holochain/client";
import { defineStore } from "pinia";
import { computed, ref } from "vue";
import { useRequest } from "vue-request";
import { Notification } from "@/types/types";
import { encode } from "@msgpack/msgpack";
import { CacheData, Notification } from "@/types/types";
import { encode, decode } from "@msgpack/msgpack";
import { PersistedStateOptions } from "pinia-plugin-persistedstate";

const notificationToKey = (notification: Notification) => {
const keyObj = {
Expand All @@ -19,59 +25,93 @@ const notificationToKey = (notification: Notification) => {
};

export const makeUseNotificationsStore = (client: AppAgentClient) =>
defineStore("notifications", () => {
const allNotifications = ref<{
[key: string]: { notification: Notification; read: boolean };
}>({});
const notificationKeys = ref<string[]>([]);
const unreadCount = computed(
() => Object.values(allNotifications.value).filter((n) => !n.read).length
);
const notifications = computed(() =>
notificationKeys.value.map(
(key) => allNotifications.value[key].notification
)
);

const fetchNotifications = (): Promise<Notification[]> =>
client.callZome({
role_name: "mewsfeed",
zome_name: "mews",
fn_name: "get_my_notifications",
payload: null,
defineStore(
"notifications",
() => {
const myPubKey = ref<AgentPubKey>(client.myPubKey);
const notificationsCache = ref<CacheData<Notification[]>>({
data: [],
params: null,
time: -1,
});
const notificationsRead = ref<{
[key: string]: boolean;
}>({});
const unreadCount = computed(
() =>
notificationsCache.value.data
.map((n) => notificationsRead.value[notificationToKey(n)])
.filter((r) => !r).length
);
const notifications = computed(() => notificationsCache.value.data);
const myPubKeyB64 = computed(() => encodeHashToBase64(myPubKey.value));

const { loading, error, runAsync } = useRequest(fetchNotifications, {
cacheKey: `mews/get_my_notifications`,
pollingInterval: 30 * 1000, // 30 seconds polling
refreshOnWindowFocus: true,
refocusTimespan: 0, // 0 seconds between window focus to trigger refresh
loadingDelay: 1000,
onSuccess: (data) => {
data.map((notification: Notification) => {
const key = notificationToKey(notification);
// @todo not sure if this will actually work
if (client.myPubKey !== myPubKey.value) {
notificationsCache.value.data = [];
notificationsRead.value = {};
myPubKey.value = client.myPubKey;
}

if (!notificationKeys.value.includes(key)) {
notificationKeys.value.push(key);
allNotifications.value[key] = { notification, read: false };
}
const fetchNotifications = (): Promise<Notification[]> =>
client.callZome({
role_name: "mewsfeed",
zome_name: "mews",
fn_name: "get_notifications_for_agent",
payload: client.myPubKey,
});
},
});

function markRead(notification: Notification) {
const key = notificationToKey(notification);
allNotifications.value[key].read = true;
}
const { loading, error, runAsync } = useRequest(fetchNotifications, {
cacheKey: `mews/get_notifications_for_agent/${myPubKeyB64.value}`,
pollingInterval: 30 * 1000, // 30 seconds polling
refreshOnWindowFocus: true,
refocusTimespan: 0, // 0 seconds between window focus to trigger refresh
loadingDelay: 1000,
setCache: (
_cacheKey: string,
cacheData: CacheData<Notification[]>
): void => {
notificationsCache.value = cacheData;

(cacheData.data as Notification[]).forEach(
(notification: Notification) => {
const key = notificationToKey(notification);
if (!Object.keys(notificationsRead.value).includes(key)) {
notificationsRead.value[key] = false;
}
}
);
},
getCache: (): CacheData<Notification[]> => {
return notificationsCache.value;
},
});

function markRead(notification: Notification) {
const key = notificationToKey(notification);
notificationsRead.value[key] = true;
}

return {
notificationKeys,
allNotifications,
notifications,
unreadCount,
loading,
error,
markRead,
runAsync,
};
});
return {
notificationsCache,
notifications,
unreadCount,
loading,
error,
markRead,
runAsync,
};
},
{
persist: {
serializer: {
serialize(value) {
return encodeHashToBase64(encode(value));
},
deserialize(value) {
return decode(decodeHashFromBase64(value));
},
},
} as PersistedStateOptions,
}
);
6 changes: 6 additions & 0 deletions ui/src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,9 @@ export type NotificationType =
| { [NotificationTypeName.MyAgentFollowed]: null }
| { [NotificationTypeName.MyAgentUnfollowed]: null }
| { [NotificationTypeName.FollowedYarnResponded]: null };

export declare type CacheData<R = any, P = any> = {
data: R;
params: P;
time: number;
};
Loading