create separate plugincards for active and inactive state. create custom hook for localstorage and method to handle pluginManager state
parent
e3c887f88c
commit
3c951a9cbf
@ -1,29 +0,0 @@ |
|||||||
import React, { useState } from 'react' |
|
||||||
import { PluginManagerComponent } from '../../types' |
|
||||||
|
|
||||||
interface ActivateButtonProps { |
|
||||||
buttonText: string |
|
||||||
pluginName: string |
|
||||||
pluginComponent: PluginManagerComponent |
|
||||||
} |
|
||||||
|
|
||||||
function ActivateButton ({ |
|
||||||
buttonText, |
|
||||||
pluginName, |
|
||||||
pluginComponent |
|
||||||
}: ActivateButtonProps) { |
|
||||||
const [dataId] = useState(`pluginManagerComponent${buttonText}Button${pluginName}`) |
|
||||||
|
|
||||||
return ( |
|
||||||
<button |
|
||||||
onClick={() => { |
|
||||||
pluginComponent.activateP(pluginName) |
|
||||||
}} |
|
||||||
className={buttonText === 'Activate' ? 'btn btn-success btn-sm' : 'btn btn-secondary btn-sm'} |
|
||||||
data-id={dataId} |
|
||||||
> |
|
||||||
{buttonText} |
|
||||||
</button> |
|
||||||
) |
|
||||||
} |
|
||||||
export default ActivateButton |
|
@ -0,0 +1,76 @@ |
|||||||
|
import { Profile } from '@remixproject/plugin-utils' |
||||||
|
import React, { useState } from 'react' |
||||||
|
import { RemoveActivatedPlugin } from '../../pluginManagerStateMachine' |
||||||
|
// import { RemoveActivatedPlugin } from '../../pluginManagerStateMachine'
|
||||||
|
import { PluginManagerComponent } from '../../types' |
||||||
|
import '../remix-ui-plugin-manager.css' |
||||||
|
interface PluginCardProps { |
||||||
|
profile: Profile & { |
||||||
|
icon?: string |
||||||
|
} |
||||||
|
pluginComponent: PluginManagerComponent |
||||||
|
buttonText: string |
||||||
|
reRender: () => void |
||||||
|
} |
||||||
|
|
||||||
|
// eslint-disable-next-line no-empty-pattern
|
||||||
|
function ActivePluginCard ({ |
||||||
|
profile, |
||||||
|
pluginComponent, |
||||||
|
buttonText, |
||||||
|
reRender |
||||||
|
}: PluginCardProps) { |
||||||
|
const [displayName] = useState<string>((profile.displayName) ? profile.displayName : profile.name) |
||||||
|
const [docLink] = useState<JSX.Element>((profile.documentation) ? ( |
||||||
|
<a href={profile.documentation} className="px-1" title="link to documentation" target="_blank" rel="noreferrer"> |
||||||
|
<i aria-hidden="true" className="fas fa-book"/> |
||||||
|
</a> |
||||||
|
) : null) |
||||||
|
|
||||||
|
const [versionWarning] = useState<JSX.Element>((profile.version && profile.version.match(/\b(\w*alpha\w*)\b/g)) ? ( |
||||||
|
<small title="Version Alpha" className="remixui_versionWarning plugin-version">alpha</small> |
||||||
|
) : (profile.version && profile.version.match(/\b(\w*beta\w*)\b/g)) ? ( |
||||||
|
<small title="Version Beta" className="remixui_versionWarning plugin-version">beta</small> |
||||||
|
) : null) |
||||||
|
// const [stateManager] = useState<PluginManagerStateMachine>(new PluginManagerStateMachine(pluginComponent))
|
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="list-group list-group-flush plugins-list-group" data-id="pluginManagerComponentActiveTile"> |
||||||
|
<article className="list-group-item py-1 mb-1 plugins-list-group-item" title={displayName}> |
||||||
|
<div className="remixui_row justify-content-between align-items-center mb-2"> |
||||||
|
<h6 className="remixui_displayName plugin-name"> |
||||||
|
<div> |
||||||
|
{displayName} |
||||||
|
{docLink} |
||||||
|
{versionWarning} |
||||||
|
</div> |
||||||
|
{ |
||||||
|
<button |
||||||
|
onClick={() => { |
||||||
|
// pluginComponent.deactivateP(profile.name)
|
||||||
|
console.log('calling pluginComponent.call directly...') |
||||||
|
pluginComponent.call('manager', 'deactivatePlugin', profile.name) |
||||||
|
console.log('called pluginComponent.call successfully') |
||||||
|
pluginComponent._paq.push(['trackEvent', 'manager', 'deactivate', profile.name]) |
||||||
|
console.log('matomo tracking captured for deactivation successfully') |
||||||
|
RemoveActivatedPlugin(profile.name) |
||||||
|
reRender() |
||||||
|
}} |
||||||
|
className="btn btn-secondary btn-sm" |
||||||
|
data-id={`pluginManagerComponentDeactivateButton${profile.name}`} |
||||||
|
> |
||||||
|
{buttonText} |
||||||
|
</button> |
||||||
|
} |
||||||
|
</h6> |
||||||
|
</div> |
||||||
|
<div className="remixui_description d-flex text-body plugin-text mb-2"> |
||||||
|
{ profile.icon ? <img src={profile.icon} className="mr-1 mt-1 remixui_pluginIcon" alt="profile icon"/> : null } |
||||||
|
<span className="remixui_descriptiontext">{profile.description}</span> |
||||||
|
</div> |
||||||
|
</article> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
export default ActivePluginCard |
@ -0,0 +1,67 @@ |
|||||||
|
import { Profile } from '@remixproject/plugin-utils' |
||||||
|
import React, { useState } from 'react' |
||||||
|
import { PersistActivatedPlugin } from '../../pluginManagerStateMachine' |
||||||
|
import { PluginManagerComponent } from '../../types' |
||||||
|
import '../remix-ui-plugin-manager.css' |
||||||
|
interface PluginCardProps { |
||||||
|
profile: Profile & { |
||||||
|
icon?: string |
||||||
|
} |
||||||
|
pluginComponent: PluginManagerComponent |
||||||
|
buttonText: string |
||||||
|
} |
||||||
|
|
||||||
|
// eslint-disable-next-line no-empty-pattern
|
||||||
|
function InactivePluginCard ({ |
||||||
|
profile, |
||||||
|
pluginComponent, |
||||||
|
buttonText |
||||||
|
}: PluginCardProps) { |
||||||
|
const [displayName] = useState<string>((profile.displayName) ? profile.displayName : profile.name) |
||||||
|
const [docLink] = useState<JSX.Element>((profile.documentation) ? ( |
||||||
|
<a href={profile.documentation} className="px-1" title="link to documentation" target="_blank" rel="noreferrer"> |
||||||
|
<i aria-hidden="true" className="fas fa-book"/> |
||||||
|
</a> |
||||||
|
) : null) |
||||||
|
|
||||||
|
const [versionWarning] = useState<JSX.Element>((profile.version && profile.version.match(/\b(\w*alpha\w*)\b/g)) ? ( |
||||||
|
<small title="Version Alpha" className="remixui_versionWarning plugin-version">alpha</small> |
||||||
|
) : (profile.version && profile.version.match(/\b(\w*beta\w*)\b/g)) ? ( |
||||||
|
<small title="Version Beta" className="remixui_versionWarning plugin-version">beta</small> |
||||||
|
) : null) |
||||||
|
// const [stateManager] = useState<PluginManagerStateMachine>(new PluginManagerStateMachine(pluginComponent))
|
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="list-group list-group-flush plugins-list-group" data-id="pluginManagerComponentActiveTile"> |
||||||
|
<article className="list-group-item py-1 mb-1 plugins-list-group-item" title={displayName}> |
||||||
|
<div className="remixui_row justify-content-between align-items-center mb-2"> |
||||||
|
<h6 className="remixui_displayName plugin-name"> |
||||||
|
<div> |
||||||
|
{displayName} |
||||||
|
{docLink} |
||||||
|
{versionWarning} |
||||||
|
</div> |
||||||
|
{ |
||||||
|
<button |
||||||
|
onClick={() => { |
||||||
|
pluginComponent.activateP(profile.name) |
||||||
|
PersistActivatedPlugin(pluginComponent, profile) |
||||||
|
}} |
||||||
|
className="btn btn-success btn-sm" |
||||||
|
data-id={`pluginManagerComponentActivateButton${profile.name}`} |
||||||
|
> |
||||||
|
{buttonText} |
||||||
|
</button> |
||||||
|
} |
||||||
|
</h6> |
||||||
|
</div> |
||||||
|
<div className="remixui_description d-flex text-body plugin-text mb-2"> |
||||||
|
{ profile.icon ? <img src={profile.icon} className="mr-1 mt-1 remixui_pluginIcon" alt="profile icon"/> : null } |
||||||
|
<span className="remixui_descriptiontext">{profile.description}</span> |
||||||
|
</div> |
||||||
|
</article> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
export default InactivePluginCard |
@ -1,28 +0,0 @@ |
|||||||
import React, { useState } from 'react' |
|
||||||
import { PluginManagerComponent } from '../../types' |
|
||||||
|
|
||||||
interface DeactivateButtonProps { |
|
||||||
buttonText: string |
|
||||||
pluginName: string |
|
||||||
pluginComponent: PluginManagerComponent |
|
||||||
} |
|
||||||
|
|
||||||
function DeactivateButton ({ |
|
||||||
buttonText, |
|
||||||
pluginName, |
|
||||||
pluginComponent |
|
||||||
}: DeactivateButtonProps) { |
|
||||||
const [dataId] = useState(`pluginManagerComponent${buttonText}Button${pluginName}`) |
|
||||||
return ( |
|
||||||
<button |
|
||||||
onClick={() => { |
|
||||||
pluginComponent.deactivateP(pluginName) |
|
||||||
}} |
|
||||||
className={buttonText === 'Deactivate' ? 'btn btn-secondary btn-sm' : ''} |
|
||||||
data-id={dataId} |
|
||||||
> |
|
||||||
{buttonText} |
|
||||||
</button> |
|
||||||
) |
|
||||||
} |
|
||||||
export default DeactivateButton |
|
@ -1,14 +0,0 @@ |
|||||||
import React, { createContext } from 'react' |
|
||||||
import { PluginManagerContextProviderProps } from '../../types' |
|
||||||
|
|
||||||
export const PluginManagerContext = createContext<Partial<PluginManagerContextProviderProps>>({}) |
|
||||||
|
|
||||||
function PluginManagerContextProvider ({ children, props }) { |
|
||||||
return ( |
|
||||||
<PluginManagerContext.Provider value={props}> |
|
||||||
{children} |
|
||||||
</PluginManagerContext.Provider> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
export default PluginManagerContextProvider |
|
@ -0,0 +1,61 @@ |
|||||||
|
import { useRef, useEffect, useState } from 'react' |
||||||
|
|
||||||
|
interface EventHandler<T extends Event = Event> { |
||||||
|
(e: T): void; |
||||||
|
} |
||||||
|
|
||||||
|
interface WindowEventHook { |
||||||
|
<K extends keyof WindowEventMap>( |
||||||
|
eventName: K, |
||||||
|
handler: EventHandler<WindowEventMap[K]> |
||||||
|
): 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<typeof handler>() |
||||||
|
|
||||||
|
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<string | null>(() => |
||||||
|
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 |
||||||
|
} |
@ -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<Profile> = [] |
||||||
|
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<any>, newlyActivatedPlugins: Profile<any>[]) { |
||||||
|
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)) |
||||||
|
} |
Loading…
Reference in new issue