([])
+ // const [storagePlugins, setStoragePlugins] = useLocalStorage('newActivePlugins')
function pluginChangeHandler (formProps: P, value: FormStateProps[P]) {
setPlugin({ ...plugin, [formProps]: value })
@@ -45,6 +47,11 @@ function RootView ({ pluginComponent }: RootViewProps) {
const closeModal = () => setVisible(true)
// <-- End Modal Visibility States -->
+ const reRender = () => {
+ pluginComponent.getAndFilterPlugins()
+ console.log('Called rerender after deactivating a plugin')
+ }
+
useEffect(() => {
pluginComponent.getAndFilterPlugins(filterPlugins)
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -57,7 +64,8 @@ function RootView ({ pluginComponent }: RootViewProps) {
if (pluginComponent.inactivePlugins && pluginComponent.inactivePlugins.length) {
setInactiveP(pluginComponent.inactivePlugins)
}
- }, [pluginComponent.activePlugins, pluginComponent.inactivePlugins, activeP, inactiveP])
+ console.log('contents of appManager', pluginComponent.appManager)
+ }, [pluginComponent.activePlugins, pluginComponent.inactivePlugins, activeP, inactiveP, pluginComponent.activeProfiles, pluginComponent])
return (
@@ -69,8 +77,6 @@ function RootView ({ pluginComponent }: RootViewProps) {
okLabel="OK"
okFn={() => {
const profile = JSON.parse(localStorage.getItem('plugins/local')) || plugin
- console.log('profile from local storage looks like this', profile)
-
if (!profile) return
if (pluginComponent.appManager.getIds().includes(profile.pname)) {
throw new Error('This name has already been used')
@@ -79,7 +85,6 @@ function RootView ({ pluginComponent }: RootViewProps) {
if (!profile.pname) throw new Error('Plugin should have a name')
if (!profile.url) throw new Error('Plugin should have an URL')
const localPlugin = profile.type === 'iframe' ? new IframePlugin(profile) : new WebsocketPlugin(profile)
- debugger
localPlugin.profile.hash = `local-${profile.pname}`
localStorage.setItem('plugins/local', JSON.stringify(localPlugin))
pluginComponent.engine.register(localPlugin)
@@ -222,10 +227,11 @@ function RootView ({ pluginComponent }: RootViewProps) {
{activeP &&
{activeP.map((profile) => (
-
))}
@@ -234,7 +240,7 @@ function RootView ({ pluginComponent }: RootViewProps) {
{inactiveP &&
{inactiveP.map((profile) => (
- >({})
-
-function PluginManagerContextProvider ({ children, props }) {
- return (
-
- {children}
-
- )
-}
-
-export default PluginManagerContextProvider
diff --git a/libs/remix-ui/plugin-manager/src/lib/useLocalStorage.ts b/libs/remix-ui/plugin-manager/src/lib/useLocalStorage.ts
new file mode 100644
index 0000000000..4409b4e35a
--- /dev/null
+++ b/libs/remix-ui/plugin-manager/src/lib/useLocalStorage.ts
@@ -0,0 +1,61 @@
+import { useRef, useEffect, useState } from 'react'
+
+interface EventHandler {
+ (e: T): void;
+}
+
+interface WindowEventHook {
+ (
+ eventName: K,
+ handler: EventHandler
+ ): void;
+}
+
+export const useWindowEvent: WindowEventHook = (eventName, handler) => {
+ // optimization: using useRef here helps us guarantee that this function is
+ // is only mutated during effect lifecycles, adding some assurance that the
+ // function invoked by the event listener is the same function passed to the
+ // hook.
+ const handlerRef = useRef()
+
+ useEffect(() => {
+ handlerRef.current = handler
+ }, [handler])
+
+ useEffect(() => {
+ const eventListener: typeof handler = event => handlerRef.current(event)
+ window.addEventListener(eventName, eventListener)
+
+ return () => {
+ window.removeEventListener(eventName, eventListener)
+ }
+ }, [eventName, handler])
+}
+
+export const useLocalStorage = (key: string) => {
+ // initialize the value from localStorage
+ const [currentValue, setCurrentValue] = useState(() =>
+ localStorage.getItem(key)
+ )
+
+ const handler = (e: StorageEvent) => {
+ if (
+ e.storageArea === localStorage &&
+ e.key === key &&
+ e.newValue !== currentValue
+ ) {
+ setCurrentValue(e.newValue)
+ }
+ }
+
+ // set up the event listener
+ useWindowEvent('storage', handler)
+
+ // update localStorage when the currentValue changes via setCurrentValue
+ useEffect(() => {
+ localStorage.setItem(key, currentValue)
+ }, [key, currentValue])
+
+ // use as const to tell TypeScript this is a tuple
+ return [currentValue, setCurrentValue] as const
+}
diff --git a/libs/remix-ui/plugin-manager/src/pluginManagerStateMachine.ts b/libs/remix-ui/plugin-manager/src/pluginManagerStateMachine.ts
new file mode 100644
index 0000000000..615e2acd10
--- /dev/null
+++ b/libs/remix-ui/plugin-manager/src/pluginManagerStateMachine.ts
@@ -0,0 +1,75 @@
+import { Profile } from '@remixproject/plugin-utils'
+import { PluginManagerComponent } from './types'
+
+const defaultActivatedPlugins = [
+ 'manager',
+ 'contentImport',
+ 'theme',
+ 'editor',
+ 'fileManager',
+ 'compilerMetadata',
+ 'compilerArtefacts',
+ 'network',
+ 'web3Provider',
+ 'offsetToLineColumnConverter',
+ 'mainPanel',
+ 'menuicons',
+ 'tabs',
+ 'sidePanel',
+ 'home',
+ 'hiddenPanel',
+ 'contextualListener',
+ 'terminal',
+ 'fetchAndCompile',
+ 'pluginManager',
+ 'filePanel',
+ 'settings',
+ 'udapp'
+]
+
+/**
+ * Compares default enabled plugins of remix ide
+ * and tracks newly activated plugins and manages
+ * their state by persisting in local storage
+ * @param newPlugin Profile of a Plugin
+ * @param pluginComponent PluginManagerComponent Instance
+ */
+export async function PersistActivatedPlugin (pluginComponent: PluginManagerComponent, newPlugin: Profile) {
+ const persisted = localStorage.getItem('newActivePlugins')
+ const activatedPlugins: Profile[] = JSON.parse(persisted)
+
+ const newlyActivatedPlugins: Array = []
+ if (newPlugin) {
+ if (defaultActivatedPlugins.includes(newPlugin.name) === false) {
+ // if this is true then we are sure that the profile name is in localStorage/workspace
+ if (activatedPlugins.length > 0 && !activatedPlugins.includes(newPlugin)) {
+ await FetchAndPersistPlugin(pluginComponent, newPlugin, activatedPlugins)
+ localStorage.setItem('newActivePlugins', JSON.stringify(activatedPlugins))
+ } else {
+ await FetchAndPersistPlugin(pluginComponent, newPlugin, newlyActivatedPlugins)
+ localStorage.setItem('newActivePlugins', JSON.stringify(newlyActivatedPlugins))
+ }
+ }
+ }
+}
+
+async function FetchAndPersistPlugin (pluginComponent: PluginManagerComponent, newPlugin: Profile, newlyActivatedPlugins: Profile[]) {
+ try {
+ const targetProfile = await pluginComponent.appManager.getProfile(newPlugin.name)
+ if (targetProfile !== null || targetProfile !== undefined) newlyActivatedPlugins.push(targetProfile)
+ } catch {
+ throw new Error('Could not fetch and persist target profile!')
+ }
+}
+
+/**
+ * Remove a deactivated plugin from persistence (localStorage)
+ * @param pluginName Name of plugin profile to be removed
+ */
+export function RemoveActivatedPlugin (pluginName: string) {
+ // eslint-disable-next-line no-debugger
+ debugger
+ const getWorkspacePlugins = JSON.parse(localStorage.getItem('newActivePlugins'))
+ const removeExisting = getWorkspacePlugins.filter(target => target.name === pluginName)
+ localStorage.setItem('newActivePlugins', JSON.stringify(removeExisting))
+}
diff --git a/libs/remix-ui/plugin-manager/src/types.d.ts b/libs/remix-ui/plugin-manager/src/types.d.ts
index d37dcfae2c..de5727e14f 100644
--- a/libs/remix-ui/plugin-manager/src/types.d.ts
+++ b/libs/remix-ui/plugin-manager/src/types.d.ts
@@ -77,6 +77,7 @@ export class PluginManagerComponent extends ViewPlugin extends Plugin implements
constructor(appManager: RemixAppManager, engine: Engine)
appManager: RemixAppManager
pluginSettings: PluginManagerSettings
+ app: PluginApi
engine: Engine
htmlElement: HTMLDivElement
views: { root: null, items: {} }
@@ -92,8 +93,10 @@ export class PluginManagerComponent extends ViewPlugin extends Plugin implements
renderComponent(): void
openLocalPlugin(): Promise
render(): HTMLDivElement
- filterPlugins({ target }: { target: any }) : void
getAndFilterPlugins: (filter?: string) => void
+ triggerEngineEventListener: () => void
+ activeProfiles: string[]
+ _paq: any
}
// eslint-disable-next-line no-use-before-define