fix: subscription streams

This commit is contained in:
liyasthomas
2021-12-12 06:19:58 +05:30
parent 67002a204e
commit 534fe8030f
4 changed files with 78 additions and 52 deletions

View File

@@ -30,6 +30,7 @@ import { GQLLanguage } from "@hoppscotch/codemirror-lang-graphql"
import { pipe } from "fp-ts/function"
import * as O from "fp-ts/Option"
import { isJSONContentType } from "../utils/contenttypes"
import { useStreamSubscriber } from "../utils/composables"
import { Completer } from "./completion"
import { LinterDefinition } from "./linting/linter"
import { basicSetup, baseTheme, baseHighlightStyle } from "./themes/baseTheme"
@@ -152,6 +153,7 @@ export function useCodemirror(
value: Ref<string>,
options: CodeMirrorOptions
): { cursor: Ref<{ line: number; ch: number }> } {
const { subscribeToStream } = useStreamSubscriber()
const language = new Compartment()
const lineWrapping = new Compartment()
const placeholderConfig = new Compartment()
@@ -178,7 +180,7 @@ export function useCodemirror(
basicSetup,
baseTheme,
baseHighlightStyle,
environmentTooltip,
environmentTooltip(subscribeToStream),
environmentHighlightStyle,
ViewPlugin.fromClass(
class {

View File

@@ -1,56 +1,64 @@
import { Extension } from "@codemirror/state"
import { hoverTooltip } from "@codemirror/tooltip"
import { Decoration, MatchDecorator, ViewPlugin } from "@codemirror/view"
import { useReadonlyStream } from "~/helpers/utils/composables"
import {
StreamSubscriberFunc,
useReadonlyStream,
} from "~/helpers/utils/composables"
import { aggregateEnvs$ } from "~/newstore/environments"
const cursorTooltipField = hoverTooltip((view, pos, side) => {
const { from, to, text } = view.state.doc.lineAt(pos)
let start = pos
let end = pos
const cursorTooltipField = (subscribeToStream: StreamSubscriberFunc) =>
hoverTooltip((view, pos, side) => {
const { from, to, text } = view.state.doc.lineAt(pos)
let start = pos
let end = pos
while (start > from && /\w/.test(text[start - from - 1])) start--
while (end < to && /\w/.test(text[end - from])) end++
while (start > from && /\w/.test(text[start - from - 1])) start--
while (end < to && /\w/.test(text[end - from])) end++
if (
(start === pos && side < 0) ||
(end === pos && side > 0) ||
!/(<<\w+>>)/g.test(text.slice(start - from - 2, end - from + 2))
)
return null
if (
(start === pos && side < 0) ||
(end === pos && side > 0) ||
!/(<<\w+>>)/g.test(text.slice(start - from - 2, end - from + 2))
)
return null
const aggregateEnvs = useReadonlyStream(aggregateEnvs$, null)
const envName = getEnvName(
aggregateEnvs.value?.find(
(env: { key: string }) => env.key === text.slice(start - from, end - from)
)?.sourceEnv
)
const envValue = getEnvValue(
aggregateEnvs.value?.find(
(env: { key: string }) => env.key === text.slice(start - from, end - from)
)?.value
)
const textContent = `${envName} <kbd>${envValue}</kbd>`
let textContent: string
subscribeToStream(aggregateEnvs$, (envs) => {
const envName = getEnvName(
envs.find(
(env: { key: string }) =>
env.key === text.slice(start - from, end - from)
)?.sourceEnv
)
const envValue = getEnvValue(
envs.find(
(env: { key: string }) =>
env.key === text.slice(start - from, end - from)
)?.value
)
textContent = `${envName} <kbd>${envValue}</kbd>`
})
return {
pos: start,
end,
above: true,
create() {
const dom = document.createElement("span")
dom.innerHTML = textContent
dom.className = "tooltip-theme"
return { dom }
},
}
})
return {
pos: start,
end,
above: true,
create() {
const dom = document.createElement("span")
dom.innerHTML = textContent
dom.className = "tooltip-theme"
return { dom }
},
}
})
function getEnvName(name: any) {
if (name) return name
return "choose an environment"
}
function getEnvValue(value: string) {
function getEnvValue(value: string | undefined) {
if (value) return value.replace(/"/g, "&quot;")
// it does not filter special characters before adding them to HTML.
return "not found"
@@ -63,8 +71,9 @@ function checkEnv(env: string) {
const envNotFound = "bg-red-400 text-red-50 hover:bg-red-600"
const aggregateEnvs = useReadonlyStream(aggregateEnvs$, null)
const className =
aggregateEnvs.value.find((k: { key: string }) => k.key === env.slice(2, -2))
?.value === undefined
aggregateEnvs.value?.find(
(k: { key: string }) => k.key === env.slice(2, -2)
)?.value === undefined
? envNotFound
: envFound
return Decoration.mark({
@@ -89,7 +98,8 @@ export const environmentHighlightStyle = ViewPlugin.define(
}
)
export const environmentTooltip: Extension = [
cursorTooltipField,
environmentHighlightStyle,
]
export const environmentTooltip: (
subscribeToStream: StreamSubscriberFunc
) => Extension = (subscribeToStream: StreamSubscriberFunc) => {
return [cursorTooltipField(subscribeToStream), environmentHighlightStyle]
}

View File

@@ -100,11 +100,20 @@ export function pluckMultipleFromRef<T, K extends Array<keyof T>>(
return Object.fromEntries(keys.map((x) => [x, pluckRef(sourceRef, x)])) as any
}
export type StreamSubscriberFunc = <T>(
stream: Observable<T>,
next?: ((value: T) => void) | undefined,
error?: ((e: any) => void) | undefined,
complete?: (() => void) | undefined
) => void
/**
* A composable that provides the ability to run streams
* and subscribe to them and respect the component lifecycle.
*/
export function useStreamSubscriber() {
export function useStreamSubscriber(): {
subscribeToStream: StreamSubscriberFunc
} {
const subs: Subscription[] = []
const runAndSubscribe = <T>(

View File

@@ -1,6 +1,6 @@
import { cloneDeep } from "lodash"
import isEqual from "lodash/isEqual"
import { combineLatest } from "rxjs"
import { combineLatest, Observable } from "rxjs"
import { distinctUntilChanged, map, pluck } from "rxjs/operators"
import DispatchingStore, {
defineDispatchers,
@@ -285,17 +285,22 @@ export const currentEnvironment$ = combineLatest([
})
)
type AggregateEnvironment = {
key: string
value: string
sourceEnv: string
}
/**
* Stream returning all the environment variables accessible in
* the current state (Global + The Selected Environment).
* NOTE: The source environment attribute will be "Global" for Global Env as source.
*/
export const aggregateEnvs$ = combineLatest([
currentEnvironment$,
globalEnv$,
]).pipe(
export const aggregateEnvs$: Observable<AggregateEnvironment[]> = combineLatest(
[currentEnvironment$, globalEnv$]
).pipe(
map(([selectedEnv, globalVars]) => {
const results: { key: string; value: string; sourceEnv: string }[] = []
const results: AggregateEnvironment[] = []
selectedEnv.variables.forEach(({ key, value }) =>
results.push({ key, value, sourceEnv: selectedEnv.name })