feat: implement user settings syncing for selfhost (#59)

This commit is contained in:
Akash K
2023-04-01 17:42:11 +05:30
committed by GitHub
parent 86a12e2d28
commit 2b44ede92b
9 changed files with 194 additions and 9 deletions

View File

@@ -0,0 +1,5 @@
mutation CreateUserSettings($properties: String!) {
createUserSettings(properties: $properties) {
id
}
}

View File

@@ -0,0 +1,5 @@
mutation UpdateUserSettings($properties: String!) {
updateUserSettings(properties: $properties) {
id
}
}

View File

@@ -0,0 +1,8 @@
query GetUserSettings {
me {
settings {
id
properties
}
}
}

View File

@@ -0,0 +1,6 @@
subscription UserSettingsUpdated {
userSettingsUpdated {
id
properties
}
}

View File

@@ -30,7 +30,7 @@ export const getSyncInitFunction = <T extends DispatchingStore<any, any>>(
store: T,
storeSyncDefinition: StoreSyncDefinitionOf<T>,
shouldSyncValue: () => boolean,
shouldSyncObservable: Observable<boolean>
shouldSyncObservable?: Observable<boolean>
) => {
let startSubscriptions: () => () => void | undefined
let stopSubscriptions: () => void | undefined
@@ -38,15 +38,16 @@ export const getSyncInitFunction = <T extends DispatchingStore<any, any>>(
let oldSyncStatus = shouldSyncValue()
// Start and stop the subscriptions according to the sync settings from profile
shouldSyncObservable.subscribe((newSyncStatus) => {
if (oldSyncStatus === true && newSyncStatus === false) {
stopListeningToSubscriptions()
} else if (oldSyncStatus === false && newSyncStatus === true) {
startListeningToSubscriptions()
}
shouldSyncObservable &&
shouldSyncObservable.subscribe((newSyncStatus) => {
if (oldSyncStatus === true && newSyncStatus === false) {
stopListeningToSubscriptions()
} else if (oldSyncStatus === false && newSyncStatus === true) {
startListeningToSubscriptions()
}
oldSyncStatus = newSyncStatus
})
oldSyncStatus = newSyncStatus
})
function startStoreSync() {
store.dispatches$.subscribe((actionParams) => {

View File

@@ -2,11 +2,13 @@ import { createHoppApp } from "@hoppscotch/common"
import { def as authDef } from "./platform/auth"
import { def as environmentsDef } from "./platform/environments/environments.platform"
import { def as collectionsDef } from "./platform/collections/collections.platform"
import { def as settingsDef } from "./platform/settings/settings.platform"
createHoppApp("#app", {
auth: authDef,
sync: {
environments: environmentsDef,
collections: collectionsDef,
settings: settingsDef,
},
})

View File

@@ -0,0 +1,49 @@
import {
runGQLQuery,
runGQLSubscription,
runMutation,
} from "@hoppscotch/common/helpers/backend/GQLClient"
import {
CreateUserSettingsDocument,
CreateUserSettingsMutation,
CreateUserSettingsMutationVariables,
GetUserSettingsDocument,
GetUserSettingsQuery,
GetUserSettingsQueryVariables,
UpdateUserSettingsDocument,
UpdateUserSettingsMutation,
UpdateUserSettingsMutationVariables,
UserSettingsUpdatedDocument,
} from "../../api/generated/graphql"
export const getUserSettings = () =>
runGQLQuery<
GetUserSettingsQuery,
GetUserSettingsQueryVariables,
"user_settings/not_found"
>({
query: GetUserSettingsDocument,
})
export const createUserSettings = (properties: string) =>
runMutation<
CreateUserSettingsMutation,
CreateUserSettingsMutationVariables,
""
>(CreateUserSettingsDocument, {
properties,
})()
export const updateUserSettings = (properties: string) =>
runMutation<
UpdateUserSettingsMutation,
UpdateUserSettingsMutationVariables,
""
>(UpdateUserSettingsDocument, {
properties,
})()
export const runUserSettingsUpdatedSubscription = () =>
runGQLSubscription({
query: UserSettingsUpdatedDocument,
})

View File

@@ -0,0 +1,88 @@
import { SettingsPlatformDef } from "@hoppscotch/common/platform/settings"
import { settingsSyncer } from "./settings.sync"
import { authEvents$, def as platformAuth } from "@platform/auth"
import {
createUserSettings,
getUserSettings,
runUserSettingsUpdatedSubscription,
} from "./settings.api"
import * as E from "fp-ts/Either"
import { runGQLSubscription } from "@hoppscotch/common/helpers/backend/GQLClient"
import {
bulkApplySettings,
defaultSettings,
} from "@hoppscotch/common/newstore/settings"
import { runDispatchWithOutSyncing } from "@lib/sync"
function initSettingsSync() {
const currentUser$ = platformAuth.getCurrentUserStream()
settingsSyncer.startStoreSync()
settingsSyncer.setupSubscriptions(setupSubscriptions)
// load the settings
loadUserSettings()
currentUser$.subscribe(async (user) => {
if (user) {
// load the settings
loadUserSettings()
}
})
authEvents$.subscribe((event) => {
if (event.event == "login" || event.event == "token_refresh") {
settingsSyncer.startListeningToSubscriptions()
}
if (event.event == "logout") {
settingsSyncer.stopListeningToSubscriptions()
}
})
}
async function loadUserSettings() {
const res = await getUserSettings()
// create user settings if it doesn't exist
E.isLeft(res) &&
res.left.error == "user_settings/not_found" &&
(await createUserSettings(JSON.stringify(defaultSettings)))
if (E.isRight(res)) {
runDispatchWithOutSyncing(() => {
bulkApplySettings(JSON.parse(res.right.me.settings.properties))
})
}
}
function setupSubscriptions() {
let subs: ReturnType<typeof runGQLSubscription>[1][] = []
const userSettingsUpdatedSub = setupUserSettingsUpdatedSubscription()
subs = [userSettingsUpdatedSub]
return () => {
subs.forEach((sub) => sub.unsubscribe())
}
}
function setupUserSettingsUpdatedSubscription() {
const [userSettingsUpdated$, userSettingsUpdatedSub] =
runUserSettingsUpdatedSubscription()
userSettingsUpdated$.subscribe((res) => {
if (E.isRight(res)) {
runDispatchWithOutSyncing(() => {
bulkApplySettings(JSON.parse(res.right.userSettingsUpdated.properties))
})
}
})
return userSettingsUpdatedSub
}
export const def: SettingsPlatformDef = {
initSettingsSync,
}

View File

@@ -0,0 +1,21 @@
import { settingsStore } from "@hoppscotch/common/newstore/settings"
import { getSyncInitFunction } from "../../lib/sync"
import { StoreSyncDefinitionOf } from "../../lib/sync"
import { updateUserSettings } from "./settings.api"
export const settingsSyncDefinition: StoreSyncDefinitionOf<
typeof settingsStore
> = {
applySetting() {
updateUserSettings(JSON.stringify(settingsStore.value))
},
}
export const settingsSyncer = getSyncInitFunction(
settingsStore,
settingsSyncDefinition,
() => true
)