mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
fix services in React
This commit is contained in:
parent
50d21828c2
commit
dbea46ba32
9 changed files with 94 additions and 38 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import React, { JSX, useCallback, useEffect, useState } from 'react'
|
||||
import { marked, MarkedToken, Token } from 'marked'
|
||||
import { BlockCode } from './BlockCode.js'
|
||||
import { useService } from '../util/contextForServices.js'
|
||||
|
||||
|
||||
enum CopyButtonState {
|
||||
|
|
@ -13,6 +14,7 @@ const COPY_FEEDBACK_TIMEOUT = 1000 // amount of time to say 'Copied!'
|
|||
|
||||
const CodeButtonsOnHover = ({ diffRepr: text }: { diffRepr: string }) => {
|
||||
const [copyButtonState, setCopyButtonState] = useState(CopyButtonState.Copy)
|
||||
const inlineDiffService = useService('inlineDiffService')
|
||||
|
||||
useEffect(() => {
|
||||
if (copyButtonState !== CopyButtonState.Copy) {
|
||||
|
|
@ -43,7 +45,8 @@ const CodeButtonsOnHover = ({ diffRepr: text }: { diffRepr: string }) => {
|
|||
<button
|
||||
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
||||
onClick={async () => {
|
||||
getVSCodeAPI().postMessage({ type: "applyChanges", diffRepr: text })
|
||||
|
||||
inlineDiffService.startStreaming('ctrl+l', text)
|
||||
}}
|
||||
>
|
||||
Apply
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ import { useSidebarState } from '../util/contextForServices.js';
|
|||
|
||||
import '../styles.css'
|
||||
import { SidebarThreadSelector } from './SidebarThreadSelector.js';
|
||||
import { SidebarChat } from './SidebarChat.js';
|
||||
|
||||
const Sidebar = () => {
|
||||
const [sidebarState, sidebarStateService] = useSidebarState()
|
||||
const sidebarState = useSidebarState()
|
||||
const { isHistoryOpen, currentTab: tab } = sidebarState
|
||||
|
||||
return <div className='@@void-scope'>
|
||||
|
|
@ -27,7 +28,7 @@ const Sidebar = () => {
|
|||
</div>
|
||||
|
||||
<div className={`${tab === 'chat' ? '' : 'hidden'}`}>
|
||||
{/* <SidebarChat /> */}
|
||||
<SidebarChat />
|
||||
</div>
|
||||
|
||||
<div className={`${tab === 'settings' ? '' : 'hidden'}`}>
|
||||
|
|
|
|||
|
|
@ -119,17 +119,16 @@ export const SidebarChat = () => {
|
|||
|
||||
// ----- HIGHER STATE -----
|
||||
// sidebar state
|
||||
const [sidebarState, sidebarStateService] = useSidebarState()
|
||||
const { isHistoryOpen, currentTab: tab } = sidebarState
|
||||
const sidebarStateService = useService('sidebarStateService')
|
||||
sidebarStateService.onDidFocusChat(() => { chatInputRef.current?.focus() })
|
||||
sidebarStateService.onDidBlurChat(() => { chatInputRef.current?.blur() })
|
||||
|
||||
// config state
|
||||
const [configState, configStateService] = useConfigState()
|
||||
const configState = useConfigState()
|
||||
const { voidConfig } = configState
|
||||
|
||||
// threads state
|
||||
const [threadsState, threadsStateService] = useThreadsState()
|
||||
const threadsStateService = useService('threadsStateService')
|
||||
|
||||
// ----- SIDEBAR CHAT state (local) -----
|
||||
// state of current message
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useConfigState } from '../util/contextForServices.js';
|
||||
import { useConfigState, useService } from '../util/contextForServices.js';
|
||||
import { IVoidConfigStateService, nonDefaultConfigFields, PartialVoidConfig, VoidConfig, VoidConfigField, VoidConfigInfo, SetFieldFnType, ConfigState } from '../../../registerConfig.js';
|
||||
|
||||
|
||||
|
|
@ -61,7 +61,9 @@ const SettingOfFieldAndParam = ({ field, param, configState, configStateService
|
|||
|
||||
export const SidebarSettings = () => {
|
||||
|
||||
const [configState, configStateService] = useConfigState()
|
||||
const configState = useConfigState()
|
||||
const configStateService = useService('configStateService')
|
||||
|
||||
const { voidConfig } = configState
|
||||
const current_field = voidConfig.default['whichApi'] as VoidConfigField
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// import React from "react";
|
||||
import { useSidebarState, useThreadsState } from '../util/contextForServices.js';
|
||||
import React from "react";
|
||||
import { useService, useThreadsState } from '../util/contextForServices.js';
|
||||
|
||||
|
||||
const truncate = (s: string) => {
|
||||
|
|
@ -12,9 +12,9 @@ const truncate = (s: string) => {
|
|||
|
||||
|
||||
export const SidebarThreadSelector = () => {
|
||||
const [threadsState, threadsStateService] = useThreadsState()
|
||||
|
||||
const [sidebarState, sidebarStateService] = useSidebarState()
|
||||
const threadsState = useThreadsState()
|
||||
const threadsStateService = useService('threadsStateService')
|
||||
const sidebarStateService = useService('sidebarStateService')
|
||||
|
||||
const { allThreads } = threadsState
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
import React, { createContext, useContext, useEffect, useState } from 'react'
|
||||
import { ReactServicesType, VoidSidebarState } from '../../../registerSidebar.js';
|
||||
import { ConfigState } from '../../../registerConfig.js';
|
||||
import { ThreadsState } from '../../../registerThreads.js';
|
||||
|
||||
const AccessorContext = createContext<ReactServicesType | undefined>(undefined)
|
||||
|
||||
export const AccessorProvider = ({ children, services }: { children: React.ReactNode; services: ReactServicesType }) => {
|
||||
registerStateListeners(services)
|
||||
return <AccessorContext.Provider value={services}>
|
||||
{children}
|
||||
</AccessorContext.Provider>
|
||||
}
|
||||
|
||||
|
||||
// -- services --
|
||||
|
||||
const useServices = (): ReactServicesType => {
|
||||
const context = useContext(AccessorContext)
|
||||
if (context === undefined) {
|
||||
|
|
@ -18,33 +23,76 @@ const useServices = (): ReactServicesType => {
|
|||
return context;
|
||||
}
|
||||
|
||||
// -- these use useServices() --
|
||||
export const useService = <T extends keyof ReactServicesType,>(serviceName: T) => {
|
||||
const services = useServices()
|
||||
return services[serviceName] as ReactServicesType[T]
|
||||
}
|
||||
|
||||
// -- state of services --
|
||||
// normally to do this you'd use a useEffect that calls .onDidChangeState(), but here, useEffect mounts too late and misses initial state changes
|
||||
let sidebarState: VoidSidebarState | null = null
|
||||
let configState: ConfigState | null = null
|
||||
let threadsState: ThreadsState | null = null
|
||||
|
||||
const sidebarStateListeners: Set<(s: VoidSidebarState) => void> = new Set()
|
||||
const configStateListeners: Set<(s: ConfigState) => void> = new Set()
|
||||
const threadsStateListeners: Set<(s: ThreadsState) => void> = new Set()
|
||||
|
||||
let isRegistered = false
|
||||
const registerStateListeners = (context: ReactServicesType) => {
|
||||
if (isRegistered) return
|
||||
isRegistered = true
|
||||
|
||||
const { sidebarStateService, configStateService, threadsStateService, } = context
|
||||
|
||||
sidebarState = sidebarStateService.state
|
||||
sidebarStateService.onDidChangeState(() => {
|
||||
sidebarState = sidebarStateService.state
|
||||
sidebarStateListeners.forEach(l => l(sidebarState))
|
||||
})
|
||||
|
||||
configState = configStateService.state
|
||||
configStateService.onDidChangeState(() => {
|
||||
configState = configStateService.state
|
||||
configStateListeners.forEach(l => l(configState))
|
||||
})
|
||||
|
||||
threadsState = threadsStateService.state
|
||||
threadsStateService.onDidChangeCurrentThread(() => {
|
||||
threadsState = threadsStateService.state
|
||||
threadsStateListeners.forEach(l => l(threadsState))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
// track the config state using React state so visual updates happen
|
||||
export const useSidebarState = () => {
|
||||
const { sidebarStateService } = useServices()
|
||||
const [sidebarState, setSideBarState] = useState<VoidSidebarState>(sidebarStateService.state)
|
||||
useEffect(() => { sidebarStateService.onDidChangeState(() => setSideBarState(sidebarStateService.state)) }, [sidebarStateService])
|
||||
return [sidebarState, sidebarStateService] as const
|
||||
const [s, ss] = useState(sidebarState)
|
||||
useEffect(() => {
|
||||
ss(sidebarState)
|
||||
sidebarStateListeners.add(ss)
|
||||
return () => { sidebarStateListeners.delete(ss) }
|
||||
}, [ss])
|
||||
return s
|
||||
}
|
||||
|
||||
export const useConfigState = () => {
|
||||
const { configStateService } = useServices()
|
||||
const [configState, setConfigState] = useState<ConfigState>(configStateService.state)
|
||||
useEffect(() => { configStateService.onDidChangeState(() => setConfigState(configStateService.state)) }, [configStateService])
|
||||
return [configState, configStateService] as const
|
||||
const [s, ss] = useState(configState)
|
||||
useEffect(() => {
|
||||
ss(configState)
|
||||
configStateListeners.add(ss)
|
||||
return () => { configStateListeners.delete(ss) }
|
||||
}, [ss])
|
||||
return s
|
||||
}
|
||||
|
||||
export const useThreadsState = () => {
|
||||
const { threadsStateService } = useServices()
|
||||
const [threadsState, setThreadsState] = useState(threadsStateService.state)
|
||||
useEffect(() => { threadsStateService.onDidChangeCurrentThread(() => setThreadsState(threadsStateService.state)) }, [threadsStateService])
|
||||
return [threadsState, threadsStateService] as const
|
||||
const [s, ss] = useState(threadsState)
|
||||
useEffect(() => {
|
||||
ss(threadsState)
|
||||
threadsStateListeners.add(ss)
|
||||
return () => { threadsStateListeners.delete(ss) }
|
||||
}, [ss])
|
||||
return s
|
||||
}
|
||||
|
||||
// -- other services --
|
||||
type PublicServiceName = 'fileService'
|
||||
export const useService = (serviceName: Extract<keyof ReactServicesType, PublicServiceName>) => {
|
||||
const services = useServices()
|
||||
return services[serviceName]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
|
||||
import posthog from 'posthog-js';
|
||||
|
||||
|
||||
export { posthog }
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@ type StreamingState = {
|
|||
|
||||
export interface IInlineDiffsService {
|
||||
readonly _serviceBrand: undefined;
|
||||
startStreaming(type: 'ctrl+k' | 'ctrl+l', userMessage: string): void;
|
||||
|
||||
}
|
||||
|
||||
export const IInlineDiffsService = createDecorator<IInlineDiffsService>('inlineDiffsService');
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import mountFn from './react/out/sidebar-tsx/Sidebar.js';
|
|||
|
||||
import { IVoidConfigStateService } from './registerConfig.js';
|
||||
import { IFileService } from '../../../../platform/files/common/files.js';
|
||||
import { IInlineDiffsService } from './registerInlineDiffs.js';
|
||||
// import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
|
||||
|
||||
|
||||
|
|
@ -59,6 +60,7 @@ export type ReactServicesType = {
|
|||
configStateService: IVoidConfigStateService;
|
||||
threadsStateService: IThreadHistoryService;
|
||||
fileService: IFileService;
|
||||
inlineDiffService: IInlineDiffsService;
|
||||
}
|
||||
|
||||
// ---------- Define viewpane ----------
|
||||
|
|
@ -101,6 +103,7 @@ class VoidSidebarViewPane extends ViewPane {
|
|||
sidebarStateService: accessor.get(IVoidSidebarStateService),
|
||||
threadsStateService: accessor.get(IThreadHistoryService),
|
||||
fileService: accessor.get(IFileService),
|
||||
inlineDiffService: accessor.get(IInlineDiffsService),
|
||||
}
|
||||
mountFn(root, services);
|
||||
});
|
||||
|
|
@ -288,7 +291,7 @@ registerAction2(class extends Action2 {
|
|||
}
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const stateService = accessor.get(IVoidSidebarStateService)
|
||||
stateService.setState({ isHistoryOpen: !stateService.state.isHistoryOpen })
|
||||
stateService.setState({ isHistoryOpen: !stateService.state.isHistoryOpen, currentTab: 'chat' })
|
||||
stateService.fireBlurChat()
|
||||
}
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue