diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 0698c90ee5..d537d0812e 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -12,6 +12,7 @@ import { VerticalIcons } from './app/components/vertical-icons' import { LandingPage } from './app/ui/landing-page/landing-page' import { MainPanel } from './app/components/main-panel' import { FramingService } from './framingService' +import { ModalPluginTester } from './app/plugins/test' import { WalkthroughService } from './walkthroughService' @@ -20,6 +21,7 @@ import { OffsetToLineColumnConverter, CompilerMetadata, CompilerArtefacts, Fetch import migrateFileSystem from './migrateFileSystem' import Registry from './app/state/registry' import { ConfigPlugin } from './app/plugins/config' +import { ModalPlugin } from './app/plugins/modal' const isElectron = require('is-electron') @@ -158,9 +160,12 @@ class AppComponent { ) const contextualListener = new EditorContextListener() + self.modal = new ModalPlugin() + const configPlugin = new ConfigPlugin() self.engine.register([ + self.modal, configPlugin, blockchain, contentImport, @@ -238,7 +243,9 @@ class AppComponent { contentImport ) + const testplugin = new ModalPluginTester() self.engine.register([ + testplugin, compileTab, run, debug, @@ -266,6 +273,7 @@ class AppComponent { console.log('couldn\'t register iframe plugins', e.message) } + await self.appManager.activatePlugin(['modal']) await self.appManager.activatePlugin(['editor']) await self.appManager.activatePlugin(['theme', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) await self.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) @@ -275,6 +283,7 @@ class AppComponent { await self.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'contextualListener', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport']) await self.appManager.activatePlugin(['settings']) await self.appManager.activatePlugin(['walkthrough']) + await self.appManager.activatePlugin(['testerplugin']) self.appManager.on('filePanel', 'workspaceInitializationCompleted', async () => { await self.appManager.registerContextMenuItems() diff --git a/apps/remix-ide/src/app/plugins/modal.tsx b/apps/remix-ide/src/app/plugins/modal.tsx new file mode 100644 index 0000000000..f69cfda0d4 --- /dev/null +++ b/apps/remix-ide/src/app/plugins/modal.tsx @@ -0,0 +1,44 @@ +import { Plugin } from '@remixproject/engine' +import { LibraryProfile, MethodApi, StatusEvents } from '@remixproject/plugin-utils' +import { AppModal } from '@remix-ui/app' +import { AlertModal } from 'libs/remix-ui/app/src/lib/remix-app/interface' +import { dispatchModalInterface } from 'libs/remix-ui/app/src/lib/remix-app/context/context' + +interface IModalApi { + events: StatusEvents, + methods: { + modal: (args: AppModal) => void + alert: (args: AlertModal) => void + toast: (message: string) => void + } +} + +const profile:LibraryProfile = { + name: 'modal', + displayName: 'Modal', + description: 'Modal', + methods: ['modal', 'alert', 'toast'] +} + +export class ModalPlugin extends Plugin implements MethodApi { + dispatcher: dispatchModalInterface + constructor () { + super(profile) + } + + setDispatcher (dispatcher: dispatchModalInterface) { + this.dispatcher = dispatcher + } + + async modal (args: AppModal) { + this.dispatcher.modal(args) + } + + async alert (args: AlertModal) { + this.dispatcher.alert(args) + } + + async toast (message: string) { + this.dispatcher.toast(message) + } +} diff --git a/apps/remix-ide/src/app/plugins/test.ts b/apps/remix-ide/src/app/plugins/test.ts new file mode 100644 index 0000000000..da32f24039 --- /dev/null +++ b/apps/remix-ide/src/app/plugins/test.ts @@ -0,0 +1,53 @@ +import { Plugin } from '@remixproject/engine' +import { Profile } from '@remixproject/plugin-utils' +import { AlertModal } from 'libs/remix-ui/app/src/lib/remix-app/interface' +import { ModalTypes } from 'libs/remix-ui/app/src/lib/remix-app/types' +import { AppModal } from '../../../../../libs/remix-ui/app/src' + +const profile:Profile = { + name: 'testerplugin', + displayName: 'testerplugin', + description: 'testerplugin', + methods: [] +} + +export class ModalPluginTester extends Plugin { + constructor () { + super(profile) + } + + handleMessage (message: any): void { + console.log(message) + } + + onActivation (): void { + // just a modal + let mod:AppModal = { + id: 'modal1', + title: 'test', + message: 'test', + okFn: this.handleMessage, + okLabel: 'yes', + cancelFn: null, + cancelLabel: 'no' + } + // this.call('modal', 'modal', mod) + + // modal with callback + mod = { ...mod, message: 'gist url', modalType: ModalTypes.prompt, defaultValue: 'prompting' } + // this.call('modal', 'modal', mod) + + // modal with password + mod = { ...mod, message: 'enter password to give me eth', modalType: ModalTypes.password, defaultValue: 'pass' } + // this.call('modal', 'modal', mod) + + const al:AlertModal = { + id: 'myalert', + message: 'alert message' + } + // this.call('modal', 'alert', al) + + // set toaster + // this.call('modal', 'toast', 'toast message') + } +} diff --git a/libs/remix-solidity/src/lib/eventManager.ts b/libs/remix-solidity/src/lib/eventManager.ts index 8282e09b6c..c8c1002430 100644 --- a/libs/remix-solidity/src/lib/eventManager.ts +++ b/libs/remix-solidity/src/lib/eventManager.ts @@ -21,7 +21,7 @@ export default class EventManager { obj = this.anonymous } for (const reg in this.registered[eventName]) { - if (this.registered[eventName][reg].obj === obj && this.registered[eventName][reg].func === func) { + if (this.registered[eventName][reg].obj === obj && this.registered[eventName][reg].func.toString() === func.toString()) { this.registered[eventName].splice(reg, 1) } } diff --git a/libs/remix-tests/package.json b/libs/remix-tests/package.json index e193dfa42f..2055922334 100644 --- a/libs/remix-tests/package.json +++ b/libs/remix-tests/package.json @@ -49,6 +49,7 @@ "color-support": "^1.1.3", "colors": "^1.1.2", "commander": "^2.13.0", + "deep-equal": "^1.0.1", "ethereumjs-util": "^7.0.10", "ethers": "^5.4.2", "ethjs-util": "^0.1.6", diff --git a/libs/remix-tests/src/compiler.ts b/libs/remix-tests/src/compiler.ts index f4b9250ecf..c287ca3677 100644 --- a/libs/remix-tests/src/compiler.ts +++ b/libs/remix-tests/src/compiler.ts @@ -170,8 +170,7 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts * @param opts Options * @param cb Callback */ -export function compileContractSources (sources: SrcIfc, compilerConfig: CompilerConfiguration, importFileCb: any, opts: any, cb): void { - let compiler +export function compileContractSources (sources: SrcIfc, compiler: any, opts: any, cb): void { const filepath = opts.testFilePath || '' const testFileImportRegEx = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm @@ -184,24 +183,14 @@ export function compileContractSources (sources: SrcIfc, compilerConfig: Compile } async.waterfall([ - function loadCompiler (next) { - const { currentCompilerUrl, evmVersion, optimize, runs, usingWorker } = compilerConfig - compiler = new RemixCompiler(importFileCb) - compiler.set('evmVersion', evmVersion) - compiler.set('optimize', optimize) - compiler.set('runs', runs) - compiler.loadVersion(usingWorker, currentCompilerUrl) - // @ts-ignore - compiler.event.register('compilerLoaded', this, (version) => { - next() - }) - }, function doCompilation (next) { - // @ts-ignore - compiler.event.register('compilationFinished', this, (success, data, source) => { + const compilationFinishedCb = (success, data, source) => { if (opts && opts.event) opts.event.emit('compilationFinished', success, data, source) next(null, data) - }) + } + compiler.event.unregister('compilationFinished', compilationFinishedCb) + // @ts-ignore + compiler.event.register('compilationFinished', compilationFinishedCb) compiler.compile(sources, filepath) } ], function (err: Error | null | undefined, result: any) { diff --git a/libs/remix-tests/src/runTestSources.ts b/libs/remix-tests/src/runTestSources.ts index 9d8b8ba400..0f2a72cea5 100644 --- a/libs/remix-tests/src/runTestSources.ts +++ b/libs/remix-tests/src/runTestSources.ts @@ -1,4 +1,6 @@ import async, { ErrorCallback } from 'async' +import deepequal from 'deep-equal' +import { Compiler as RemixCompiler } from '@remix-project/remix-solidity' import { compileContractSources, writeTestAccountsContract } from './compiler' import { deployAll } from './deployer' import { runTest } from './testRunner' @@ -16,6 +18,8 @@ export class UnitTestRunner { accountsLibCode testsAccounts: string[] | null web3 + compiler + compilerConfig constructor () { this.event = new EventEmitter() @@ -53,7 +57,22 @@ export class UnitTestRunner { async.waterfall([ (next) => { - compileContractSources(contractSources, compilerConfig, importFileCb, { accounts: this.testsAccounts, testFilePath: opts.testFilePath, event: this.event }, next) + if (!deepequal(this.compilerConfig, compilerConfig)) { + this.compilerConfig = compilerConfig + const { currentCompilerUrl, evmVersion, optimize, runs, usingWorker } = compilerConfig + this.compiler = new RemixCompiler(importFileCb) + this.compiler.set('evmVersion', evmVersion) + this.compiler.set('optimize', optimize) + this.compiler.set('runs', runs) + this.compiler.loadVersion(usingWorker, currentCompilerUrl) + // @ts-ignore + this.compiler.event.register('compilerLoaded', this, (version) => { + next() + }) + } else next() + }, + (next) => { + compileContractSources(contractSources, this.compiler, { accounts: this.testsAccounts, testFilePath: opts.testFilePath, event: this.event }, next) }, (compilationResult: compilationInterface, asts: ASTInterface, next) => { for (const filename in asts) { diff --git a/libs/remix-ui/app/src/index.ts b/libs/remix-ui/app/src/index.ts index e47f8690e7..00999a2115 100644 --- a/libs/remix-ui/app/src/index.ts +++ b/libs/remix-ui/app/src/index.ts @@ -1 +1,4 @@ export { default as RemixApp } from './lib/remix-app/remix-app' +export { dispatchModalContext } from './lib/remix-app/context/context' +export { ModalProvider } from './lib/remix-app/context/provider' +export { AppModal } from './lib/remix-app/interface/index' diff --git a/libs/remix-ui/app/src/lib/remix-app/actions/modals.ts b/libs/remix-ui/app/src/lib/remix-app/actions/modals.ts new file mode 100644 index 0000000000..95400ca77b --- /dev/null +++ b/libs/remix-ui/app/src/lib/remix-app/actions/modals.ts @@ -0,0 +1,30 @@ +import { AppModal } from '../interface' + +type ActionMap = { + [Key in keyof M]: M[Key] extends undefined + ? { + type: Key; + } + : { + type: Key; + payload: M[Key]; + } +} + +export const enum modalActionTypes { + setModal = 'SET_MODAL', + setToast = 'SET_TOAST', + handleHideModal = 'HANDLE_HIDE_MODAL', + handleToaster = 'HANDLE_HIDE_TOAST' +} + +type ModalPayload = { + [modalActionTypes.setModal]: AppModal + [modalActionTypes.handleHideModal]: any + [modalActionTypes.setToast]: string + [modalActionTypes.handleToaster]: any +} + +export type ModalAction = ActionMap[keyof ActionMap< + ModalPayload +>] diff --git a/libs/remix-ui/app/src/lib/remix-app/dragbar/dragbar.css b/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.css similarity index 100% rename from libs/remix-ui/app/src/lib/remix-app/dragbar/dragbar.css rename to libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.css diff --git a/libs/remix-ui/app/src/lib/remix-app/dragbar/dragbar.tsx b/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx similarity index 100% rename from libs/remix-ui/app/src/lib/remix-app/dragbar/dragbar.tsx rename to libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogViewPlugin.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogViewPlugin.tsx new file mode 100644 index 0000000000..248527075e --- /dev/null +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogViewPlugin.tsx @@ -0,0 +1,16 @@ +import React, { useContext, useEffect } from 'react' +import { AppContext } from '../../context/context' +import { useDialogDispatchers } from '../../context/provider' + +const DialogViewPlugin = () => { + const { modal, alert, toast } = useDialogDispatchers() + const app = useContext(AppContext) + + useEffect(() => { + console.log(modal, app) + app.modal.setDispatcher({ modal, alert, toast }) + }, []) + return <> +} + +export default DialogViewPlugin diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx new file mode 100644 index 0000000000..7aba74f73b --- /dev/null +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx @@ -0,0 +1,16 @@ +import React from 'react' +import { useDialogDispatchers, useDialogs } from '../../context/provider' +import { Toaster } from '@remix-ui/toaster' +import ModalWrapper from './modal-wrapper' + +const AppDialogs = () => { + const { handleHideModal, handleToaster } = useDialogDispatchers() + const { focusModal, focusToaster } = useDialogs() + + return ( + <> + + + ) +} +export default AppDialogs diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx new file mode 100644 index 0000000000..4d4daaed3e --- /dev/null +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx @@ -0,0 +1,45 @@ +import React, { useContext, useEffect, useState } from 'react' +import { AppContext } from '../../context/context' +import { useDialogDispatchers } from '../../context/provider' +const _paq = window._paq = window._paq || [] + +const MatomoDialog = (props) => { + const { settings, showMatamo, appManager } = useContext(AppContext) + const { modal } = useDialogDispatchers() + const [visible, setVisible] = useState(props.hide) + + const message = () => { + return (<>

An Opt-in version of Matomo, an open source data analytics platform is being used to improve Remix IDE.

+

We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.

+

All data collected through Matomo is stored on our own server - no data is ever given to third parties. Our analytics reports are public: take a look.

+

We do not collect nor store any personally identifiable information (PII).

+

For more info, see: Matomo Analyitcs on Remix iDE.

+

You can change your choice in the Settings panel anytime.

) + } + + useEffect(() => { + if (visible && showMatamo) { + modal({ id: 'matomoModal', title: 'Help us to improve Remix IDE', message: message(), okLabel: 'Accept', okFn: handleModalOkClick, cancelLabel: 'Decline', cancelFn: declineModal }) + } + }, [visible]) + + const declineModal = async () => { + settings.updateMatomoAnalyticsChoice(false) + _paq.push(['optUserOut']) + appManager.call('walkthrough', 'start') + setVisible(false) + } + + const handleModalOkClick = async () => { + _paq.push(['forgetUserOptOut']) + // @TODO remove next line when https://github.com/matomo-org/matomo/commit/9e10a150585522ca30ecdd275007a882a70c6df5 is used + document.cookie = 'mtm_consent_removed=; expires=Thu, 01 Jan 1970 00:00:01 GMT;' + settings.updateMatomoAnalyticsChoice(true) + appManager.call('walkthrough', 'start') + setVisible(false) + } + + return (<>) +} + +export default MatomoDialog diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx new file mode 100644 index 0000000000..cff3215e1f --- /dev/null +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx @@ -0,0 +1,56 @@ +import React, { useEffect, useRef, useState } from 'react' +import { ModalDialog } from '@remix-ui/modal-dialog' +import { ModalDialogProps } from 'libs/remix-ui/modal-dialog/src/lib/types' +import { ModalTypes } from '../../types' + +interface ModalWrapperProps extends ModalDialogProps { + modalType?: ModalTypes + defaultValue?: string +} + +const ModalWrapper = (props: ModalWrapperProps) => { + const [state, setState] = useState() + const ref = useRef() + + const onFinishPrompt = async () => { + if (ref.current === undefined) { + props.okFn() + } else { + // @ts-ignore: Object is possibly 'null'. + props.okFn(ref.current.value) + } + } + + const createModalMessage = (defaultValue: string) => { + return ( + <> + {props.message} + + ) + } + + useEffect(() => { + if (props.modalType) { + switch (props.modalType) { + case ModalTypes.prompt: + case ModalTypes.password: + setState({ + ...props, + okFn: onFinishPrompt, + message: createModalMessage(props.defaultValue) + }) + break + default: + setState({ ...props }) + break + } + } else { + setState({ ...props }) + } + }, [props]) + + return ( + + ) +} +export default ModalWrapper diff --git a/libs/remix-ui/app/src/lib/remix-app/modals/alert.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/origin-warning.tsx similarity index 68% rename from libs/remix-ui/app/src/lib/remix-app/modals/alert.tsx rename to libs/remix-ui/app/src/lib/remix-app/components/modals/origin-warning.tsx index b61abbb1d1..61d25db2f6 100644 --- a/libs/remix-ui/app/src/lib/remix-app/modals/alert.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/origin-warning.tsx @@ -1,9 +1,10 @@ import React, { useEffect, useState } from 'react' import { ModalDialog } from '@remix-ui/modal-dialog' +import { useDialogDispatchers } from '../../context/provider' -const AlertModal = () => { - const [visible, setVisible] = useState(true) - const [content, setContent] = useState('') +const OriginWarning = () => { + const { alert } = useDialogDispatchers() + const [content, setContent] = useState(null) useEffect(() => { // check the origin and warn message @@ -20,24 +21,15 @@ const AlertModal = () => { This instance of Remix you are visiting WILL NOT BE UPDATED.\n Please make a backup of your contracts and start using http://remix.ethereum.org`) } - setVisible(content !== '') }, []) - const closeModal = async () => { - setVisible(false) - } - const handleModalOkClick = async () => { - setVisible(false) - } - return ({content}) + useEffect(() => { + if (content) { + alert({ id: 'warningOriging', title: null, message: content }) + } + }, [content]) + + return (<>) } -export default AlertModal +export default OriginWarning diff --git a/libs/remix-ui/app/src/lib/remix-app/modals/splashscreen.tsx b/libs/remix-ui/app/src/lib/remix-app/components/splashscreen.tsx similarity index 100% rename from libs/remix-ui/app/src/lib/remix-app/modals/splashscreen.tsx rename to libs/remix-ui/app/src/lib/remix-app/components/splashscreen.tsx diff --git a/libs/remix-ui/app/src/lib/remix-app/context/context.tsx b/libs/remix-ui/app/src/lib/remix-app/context/context.tsx index f9f97a4555..3745fcddf0 100644 --- a/libs/remix-ui/app/src/lib/remix-app/context/context.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/context/context.tsx @@ -1,5 +1,24 @@ import React from 'react' +import { AlertModal, AppModal } from '../interface' +import { ModalInitialState } from '../state/modals' +import { ModalTypes } from '../types' -const AppContext = React.createContext(null) +export const AppContext = React.createContext(null) -export default AppContext +export interface dispatchModalInterface { + modal: (data: AppModal) => void + toast: (message: string) => void + alert: (data: AlertModal) => void + handleHideModal: () => void, + handleToaster: () => void +} + +export const dispatchModalContext = React.createContext({ + modal: (data: AppModal) => { }, + toast: (message: string) => {}, + alert: (data: AlertModal) => {}, + handleHideModal: () => {}, + handleToaster: () => {} +}) + +export const modalContext = React.createContext(ModalInitialState) diff --git a/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx b/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx new file mode 100644 index 0000000000..cc25c381fd --- /dev/null +++ b/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx @@ -0,0 +1,64 @@ +import React, { useReducer } from 'react' +import { modalActionTypes } from '../actions/modals' +import { AlertModal, AppModal } from '../interface' +import { modalReducer } from '../reducer/modals' +import { ModalInitialState } from '../state/modals' +import { ModalTypes } from '../types' +import { AppContext, dispatchModalContext, modalContext } from './context' + +export const ModalProvider = ({ children = [], reducer = modalReducer, initialState = ModalInitialState } = {}) => { + const [{ modals, toasters, focusModal, focusToaster }, dispatch] = useReducer(reducer, initialState) + + const modal = (data: AppModal) => { + const { id, title, message, okLabel, okFn, cancelLabel, cancelFn, modalType, defaultValue } = data + dispatch({ + type: modalActionTypes.setModal, + payload: { id, title, message, okLabel, okFn, cancelLabel, cancelFn, modalType: modalType || ModalTypes.default, defaultValue: defaultValue } + }) + } + + const alert = (data: AlertModal) => { + modal({ id: data.id, title: data.title || 'Alert', message: data.message || data.title, okLabel: 'OK', okFn: (value?:any) => {}, cancelLabel: '', cancelFn: () => {} }) + } + + const handleHideModal = () => { + dispatch({ + type: modalActionTypes.handleHideModal, + payload: null + }) + } + + const toast = (message: string) => { + dispatch({ + type: modalActionTypes.setToast, + payload: message + }) + } + + const handleToaster = () => { + dispatch({ + type: modalActionTypes.handleToaster, + payload: null + }) + } + + return ( + + {children} + + ) +} + +export const AppProvider = ({ children = [], value = {} } = {}) => { + return + {children} + +} + +export const useDialogs = () => { + return React.useContext(modalContext) +} + +export const useDialogDispatchers = () => { + return React.useContext(dispatchModalContext) +} diff --git a/libs/remix-ui/app/src/lib/remix-app/interface/index.ts b/libs/remix-ui/app/src/lib/remix-app/interface/index.ts new file mode 100644 index 0000000000..feb7c5761f --- /dev/null +++ b/libs/remix-ui/app/src/lib/remix-app/interface/index.ts @@ -0,0 +1,28 @@ +import { ModalTypes } from '../types' + +export interface AppModal { + id: string + hide?: boolean + title: string + // eslint-disable-next-line no-undef + message: string | JSX.Element + okLabel: string + okFn: (value?:any) => void + cancelLabel: string + cancelFn: () => void, + modalType?: ModalTypes, + defaultValue?: string +} + +export interface AlertModal { + id: string + title?: string, + message: string | JSX.Element, +} + +export interface ModalState { + modals: AppModal[], + toasters: string[], + focusModal: AppModal, + focusToaster: string +} diff --git a/libs/remix-ui/app/src/lib/remix-app/modals/matomo.tsx b/libs/remix-ui/app/src/lib/remix-app/modals/matomo.tsx deleted file mode 100644 index 2ab56fa02d..0000000000 --- a/libs/remix-ui/app/src/lib/remix-app/modals/matomo.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react' -import { ModalDialog } from '@remix-ui/modal-dialog' -import AppContext from '../context/context' -const _paq = window._paq = window._paq || [] - -const MatomoDialog = (props) => { - const { settings, showMatamo, appManager } = useContext(AppContext) - const [visible, setVisible] = useState(props.hide) - useEffect(() => { - if (showMatamo) { - setVisible(true) - } else { - setVisible(false) - } - }, []) - const declineModal = async () => { - settings.updateMatomoAnalyticsChoice(false) - _paq.push(['optUserOut']) - appManager.call('walkthrough', 'start') - setVisible(false) - } - const hideModal = async () => { - setVisible(false) - } - const handleModalOkClick = async () => { - _paq.push(['forgetUserOptOut']) - // @TODO remove next line when https://github.com/matomo-org/matomo/commit/9e10a150585522ca30ecdd275007a882a70c6df5 is used - document.cookie = 'mtm_consent_removed=; expires=Thu, 01 Jan 1970 00:00:01 GMT;' - settings.updateMatomoAnalyticsChoice(true) - appManager.call('walkthrough', 'start') - setVisible(false) - } - return ( -

An Opt-in version of Matomo, an open source data analytics platform is being used to improve Remix IDE.

-

We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.

-

All data collected through Matomo is stored on our own server - no data is ever given to third parties. Our analytics reports are public: take a look.

-

We do not collect nor store any personally identifiable information (PII).

-

For more info, see: Matomo Analyitcs on Remix iDE.

-

You can change your choice in the Settings panel anytime.

-
) -} - -export default MatomoDialog diff --git a/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts b/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts new file mode 100644 index 0000000000..951b547f16 --- /dev/null +++ b/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts @@ -0,0 +1,46 @@ +import { modalActionTypes, ModalAction } from '../actions/modals' +import { ModalInitialState } from '../state/modals' +import { AppModal, ModalState } from '../interface' + +export const modalReducer = (state: ModalState = ModalInitialState, action: ModalAction) => { + switch (action.type) { + case modalActionTypes.setModal: { + let modalList:AppModal[] = state.modals + modalList.push(action.payload) + if (state.modals.length === 1 && state.focusModal.hide === true) { // if it's the first one show it + const focusModal: AppModal = { + id: modalList[0].id, + hide: false, + title: modalList[0].title, + message: modalList[0].message, + okLabel: modalList[0].okLabel, + okFn: modalList[0].okFn, + cancelLabel: modalList[0].cancelLabel, + cancelFn: modalList[0].cancelFn, + modalType: modalList[0].modalType, + defaultValue: modalList[0].defaultValue + } + + modalList = modalList.slice() + modalList.shift() + return { ...state, modals: modalList, focusModal: focusModal } + } + return { ...state, modals: modalList } + } + case modalActionTypes.handleHideModal: + state.focusModal = { ...state.focusModal, hide: true, message: null } + return { ...state } + + case modalActionTypes.setToast: + state.toasters.push(action.payload) + if (state.toasters.length > 0) { + const focus = state.toasters[0] + state.toasters.shift() + return { ...state, focusToaster: focus } + } + return { ...state } + + case modalActionTypes.handleToaster: + return { ...state, focusToaster: '' } + } +} diff --git a/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx index d8430eac52..e86e4ed63d 100644 --- a/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx @@ -1,10 +1,13 @@ -import React, { useContext, useEffect, useRef, useState } from 'react' +import React, { useEffect, useRef, useState } from 'react' import './style/remix-app.css' -import RemixSplashScreen from './modals/splashscreen' -import MatomoDialog from './modals/matomo' -import AlertModal from './modals/alert' -import AppContext from './context/context' -import DragBar from './dragbar/dragbar' +import RemixSplashScreen from './components/splashscreen' +import MatomoDialog from './components/modals/matomo' +import OriginWarning from './components/modals/origin-warning' +import DragBar from './components/dragbar/dragbar' +import { AppProvider } from './context/provider' +import AppDialogs from './components/modals/dialogs' +import DialogViewPlugin from './components/modals/dialogViewPlugin' + interface IRemixAppUi { app: any } @@ -68,10 +71,17 @@ const RemixApp = (props: IRemixAppUi) => { hiddenPanel:
} + const value = { + settings: props.app.settings, + showMatamo: props.app.showMatamo, + appManager: props.app.appManager, + modal: props.app.modal + } + return ( - + - +
@@ -82,8 +92,9 @@ const RemixApp = (props: IRemixAppUi) => {
{components.hiddenPanel} -
- + + + ) } diff --git a/libs/remix-ui/app/src/lib/remix-app/state/modals.ts b/libs/remix-ui/app/src/lib/remix-app/state/modals.ts new file mode 100644 index 0000000000..8332d60120 --- /dev/null +++ b/libs/remix-ui/app/src/lib/remix-app/state/modals.ts @@ -0,0 +1,17 @@ +import { ModalState } from '../interface' + +export const ModalInitialState: ModalState = { + modals: [], + toasters: [], + focusModal: { + id: '', + hide: true, + title: '', + message: '', + okLabel: '', + okFn: () => { }, + cancelLabel: '', + cancelFn: () => { } + }, + focusToaster: '' +} diff --git a/libs/remix-ui/app/src/lib/remix-app/types/index.ts b/libs/remix-ui/app/src/lib/remix-app/types/index.ts new file mode 100644 index 0000000000..edca9e147c --- /dev/null +++ b/libs/remix-ui/app/src/lib/remix-app/types/index.ts @@ -0,0 +1,7 @@ +export const enum ModalTypes { + alert = 'alert', + confirm = 'confirm', + prompt = 'prompt', + password = 'password', + default = 'default', +} diff --git a/libs/remix-ui/modal-dialog/src/lib/types/index.ts b/libs/remix-ui/modal-dialog/src/lib/types/index.ts index b03cb16c94..acb171fc0f 100644 --- a/libs/remix-ui/modal-dialog/src/lib/types/index.ts +++ b/libs/remix-ui/modal-dialog/src/lib/types/index.ts @@ -4,7 +4,7 @@ export interface ModalDialogProps { title?: string, message?: string | JSX.Element, okLabel?: string, - okFn?: () => void, + okFn?: (value?:any) => void, cancelLabel?: string, cancelFn?: () => void, modalClass?: string,