Skip to content

Commit 321fcaa

Browse files
authored
Merge branch 'master' into newaichat
2 parents d19828a + bced4b5 commit 321fcaa

File tree

21 files changed

+333
-239
lines changed

21 files changed

+333
-239
lines changed

apps/remix-ide-e2e/src/tests/matomo.test.ts

Lines changed: 38 additions & 151 deletions
Large diffs are not rendered by default.

apps/remix-ide/src/app.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,12 @@ class AppComponent {
222222
}
223223

224224
_paq.push(['trackEvent', 'App', 'load']);
225-
this.matomoConfAlreadySet = Registry.getInstance().get('config').api.exists('settings/matomo-analytics')
226-
this.matomoCurrentSetting = Registry.getInstance().get('config').api.get('settings/matomo-analytics')
225+
this.matomoConfAlreadySet = Registry.getInstance().get('config').api.exists('settings/matomo-perf-analytics')
226+
this.matomoCurrentSetting = Registry.getInstance().get('config').api.get('settings/matomo-perf-analytics')
227227

228228
const electronTracking = (window as any).electronAPI ? await (window as any).electronAPI.canTrackMatomo() : false
229229

230-
const lastMatomoCheck = window.localStorage.getItem('matomo-analytics-consent')
230+
const lastMatomoCheck = window.localStorage.getItem('matomo-perf-analytics-consent')
231231
const sixMonthsAgo = new Date();
232232
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
233233

apps/remix-ide/src/app/tabs/locales/en/remixApp.json

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,24 @@
88
"remixApp.enterText4": "Prototyping - trying out concepts and techniques",
99
"remixApp.enterText5": "Developing projects - Remix as your main dev tool",
1010
"remixApp.enterText6": "Production - only deployments",
11-
"remixApp.matomoText1": "An Opt-in version of <a>Matomo</a>, an open source data analytics platform is being used to improve Remix IDE.",
12-
"remixApp.matomoText2": "We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.",
13-
"remixApp.matomoText3": "All data collected through Matomo is stored on our own server - no data is ever given to third parties.",
14-
"remixApp.matomoText4": "We do not collect nor store any personally identifiable information (PII).",
15-
"remixApp.matomoText5": "For more info, see: <a>Matomo Analytics on Remix iDE</a>.",
16-
"remixApp.matomoText6": "You can change your choice in the Settings panel anytime.",
17-
"remixApp.matomoTitle": "Help us to improve Remix IDE",
11+
"remixApp.matomoText1": "Remix features an AI coding assistant called <a>RemixAI</a>, that needs your approval to use.",
12+
"remixApp.matomoText2": "We also use <a>Matomo</a>, an open-source analytics platform, that helps us improve your experience.",
13+
"remixApp.matomoTitle": "AI and Analytics Preferences",
1814
"remixApp.accept": "Accept",
19-
"remixApp.decline": "Decline"
15+
"remixApp.managePreferences": "Manage Preferences",
16+
"remixApp.savePreferences": "Save Preferences",
17+
"remixApp.declineCookies": "Decline Cookies",
18+
"remixApp.decline": "Decline",
19+
20+
"remixApp.mpOp1Title": "Matomo Anonymous Analytics (Necessary)",
21+
"remixApp.mpOp1Details": "Our use of anonymous analytics data helps us improve your experience by identifying bug and important UX improvements. This data can also be used in overall statistics on Remix use in the wider Web3 ecosystem",
22+
"remixApp.mpOp1Tooltip": "This is a necessary selection for a better experience of Remix IDE",
23+
24+
"remixApp.mpOp2Title": "Matomo Performance Analytics",
25+
"remixApp.mpOp2Details": "Remix believes your privacy is of primary importance. Your help in making Remix the best possible open source public good toolset is also very important. Approval of performance analytics tracking greatly helps us refine and improve the toolset. Rest assured, we will never share this data with any third parties.",
26+
"remixApp.mpOp2Link": "<a>Learn more about analytics</a>",
27+
28+
"remixApp.mpOp3Title": "RemixAI",
29+
"remixApp.mpOp3Details": "RemixAI helps by offering code suggestions and improvements",
30+
"remixApp.mpOp3Link": "<a>Learn more about RemixAI Copilot</a>"
2031
}

apps/remix-ide/src/app/tabs/locales/en/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"settings.useShowGasInEditorText": "Display gas estimates in editor",
1111
"settings.displayErrorsText": "Display errors in editor while typing",
1212
"settings.matomoAnalytics": "Enable Matomo Analytics. See",
13+
"settings.matomoPerfAnalytics": "Enable Matomo Performance Analytics",
1314
"settings.matomoAnalyticsTooltip": "We do not collect personally identifiable information (PII). The info is used to improve the site’s UX & UI.",
1415
"settings.enablePersonalModeText": " Enable Personal Mode for web3 provider",
1516
"settings.enablePersonalModeTooltip": "Transaction sent over Web3 will use the web3.personal API. Be sure the endpoint is opened before enabling it. This mode allows a user to provide a passphrase in the Remix interface without having to unlock the account. Although this is very convenient, you should completely trust the backend you are connected to (Geth, Parity, ...). Remix never persists any passphrase",

apps/remix-ide/src/app/tabs/settings-tab.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export default class SettingsTab extends ViewPlugin {
3636
}
3737
element: HTMLDivElement
3838
public useMatomoAnalytics: any
39+
public useMatomoPerfAnalytics: boolean
3940
public useCopilot: any
4041
dispatch: React.Dispatch<any> = () => {}
4142
constructor(config, editor) {
@@ -52,6 +53,7 @@ export default class SettingsTab extends ViewPlugin {
5253
this.element = document.createElement('div')
5354
this.element.setAttribute('id', 'settingsTab')
5455
this.useMatomoAnalytics = null
56+
this.useMatomoPerfAnalytics = null
5557
this.useCopilot = this.get('settings/copilot/suggest/activate')
5658
}
5759

@@ -78,7 +80,7 @@ export default class SettingsTab extends ViewPlugin {
7880
config={state.config}
7981
editor={state.editor}
8082
_deps={state._deps}
81-
useMatomoAnalytics={state.useMatomoAnalytics}
83+
useMatomoPerfAnalytics={state.useMatomoPerfAnalytics}
8284
useCopilot={state.useCopilot}
8385
themeModule={state._deps.themeModule}
8486
localeModule={state._deps.localeModule}
@@ -123,4 +125,21 @@ export default class SettingsTab extends ViewPlugin {
123125
...this
124126
})
125127
}
128+
129+
updateMatomoPerfAnalyticsChoice(isChecked) {
130+
this.config.set('settings/matomo-perf-analytics', isChecked)
131+
// set timestamp to local storage to track when the user has given consent
132+
localStorage.setItem('matomo-perf-analytics-consent', Date.now().toString())
133+
this.useMatomoPerfAnalytics = isChecked
134+
if (!isChecked) {
135+
// revoke tracking consent for performance data
136+
_paq.push(['forgetMatomoPerfConsentGiven'])
137+
} else {
138+
// user has given consent to process their performance data
139+
_paq.push(['setMatomoPerfConsentGiven'])
140+
}
141+
this.dispatch({
142+
...this
143+
})
144+
}
126145
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import React, { useContext, useEffect, useRef, useState } from 'react'
2+
import { FormattedMessage } from 'react-intl'
3+
import { useDialogDispatchers } from '../../context/provider'
4+
import { ToggleSwitch } from '@remix-ui/toggle'
5+
import { AppContext } from '../../context/context'
6+
declare global {
7+
interface Window {
8+
_paq: any
9+
}
10+
}
11+
const _paq = (window._paq = window._paq || [])
12+
13+
const ManagePreferencesSwitcher = (prop: {
14+
setParentState: (state: any) => void
15+
}) => {
16+
const [remixAISwitch, setRemixAISwitch] = useState(true)
17+
const [matPerfSwitch, setMatPerfSwitch] = useState(true)
18+
19+
useEffect(() => {
20+
prop.setParentState({
21+
remixAISwitch,
22+
matPerfSwitch
23+
})
24+
}, [remixAISwitch, matPerfSwitch])
25+
26+
return (
27+
<>
28+
<div data-id="matomoAnonAnalytics" className='justify-content-between d-flex'>
29+
<div className='mt-2'>
30+
<h6 className='text-secondary'><FormattedMessage id="remixApp.mpOp1Title" /></h6>
31+
<p className='form-check-label text-secondary'><FormattedMessage id="remixApp.mpOp1Details" /></p>
32+
</div>
33+
<div>
34+
<ToggleSwitch
35+
id = "matomoAnonAnalyticsToggle"
36+
size = "2xl"
37+
tooltipTextId = "remixApp.mpOp1Tooltip"
38+
disabled = {true}
39+
></ToggleSwitch>
40+
</div>
41+
</div>
42+
<div data-id="matomoPerfAnalytics" className='justify-content-between d-flex'>
43+
<div className='mt-3'>
44+
<h6><FormattedMessage id="remixApp.mpOp2Title" /></h6>
45+
<p className='form-check-label'><FormattedMessage id="remixApp.mpOp2Details" /></p>
46+
<p className='mt-1'><FormattedMessage
47+
id="remixApp.mpOp2Link"
48+
values={{
49+
a: (chunks) => (
50+
<a className="text-primary" href="https://matomo.org" target="_blank" rel="noreferrer">
51+
{chunks}
52+
</a>
53+
),
54+
}}
55+
/></p>
56+
</div>
57+
<div>
58+
<ToggleSwitch
59+
id = "matomoPerfAnalyticsToggle"
60+
size = "2xl"
61+
isOn = {matPerfSwitch}
62+
onClick = {() => setMatPerfSwitch(!matPerfSwitch)}
63+
></ToggleSwitch>
64+
</div>
65+
</div>
66+
<div data-id="remixAI" className='justify-content-between d-flex'>
67+
<div className='mt-2'>
68+
<h6><FormattedMessage id="remixApp.mpOp3Title" /></h6>
69+
<p className='form-check-label'><FormattedMessage id="remixApp.mpOp3Details" /></p>
70+
<p className='mt-1'><FormattedMessage
71+
id="remixApp.mpOp3Link"
72+
values={{
73+
a: (chunks) => (
74+
<a className="text-primary" href="https://remix-ide.readthedocs.io/en/latest/ai.html" target="_blank" rel="noreferrer">
75+
{chunks}
76+
</a>
77+
),
78+
}}
79+
/></p>
80+
</div>
81+
<div>
82+
<ToggleSwitch
83+
id = "remixAIToggle"
84+
size = "2xl"
85+
isOn = {remixAISwitch}
86+
onClick = {() => setRemixAISwitch(!remixAISwitch)}
87+
></ToggleSwitch>
88+
</div>
89+
</div>
90+
</>
91+
)
92+
}
93+
94+
const ManagePreferencesDialog = (props) => {
95+
const { modal } = useDialogDispatchers()
96+
const { settings } = useContext(AppContext)
97+
const [visible, setVisible] = useState<boolean>(true)
98+
const switcherState = useRef<Record<string, any>>(null)
99+
100+
useEffect(() => {
101+
if (visible) {
102+
modal({
103+
id: 'managePreferencesModal',
104+
title: <FormattedMessage id="remixApp.managePreferences" />,
105+
message: <ManagePreferencesSwitcher setParentState={(state)=>{
106+
switcherState.current = state
107+
}} />,
108+
okLabel: <FormattedMessage id="remixApp.savePreferences" />,
109+
okFn: savePreferences,
110+
showCancelIcon: true,
111+
preventBlur: true
112+
})
113+
}
114+
}, [visible])
115+
116+
const savePreferences = async () => {
117+
_paq.push(['setConsentGiven']) // default consent to process their anonymous data
118+
settings.updateMatomoAnalyticsChoice(true) // Always true for matomo Anonymous analytics
119+
settings.updateMatomoPerfAnalyticsChoice(switcherState.current.matPerfSwitch) // Enable/Disable Matomo Performance analytics
120+
if (switcherState.current.matPerfSwitch) {
121+
// user has given consent to process their performance data
122+
_paq.push(['setMatomoPerfConsentGiven'])
123+
} else {
124+
// revoke tracking consent for performance data
125+
_paq.push(['forgetMatomoPerfConsentGiven'])
126+
}
127+
settings.updateCopilotChoice(switcherState.current.remixAISwitch) // Enable/Disable RemixAI copilot
128+
setVisible(false)
129+
props.savePreferencesFn()
130+
}
131+
132+
return <></>
133+
}
134+
135+
export default ManagePreferencesDialog

libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React, { useContext, useEffect, useState } from 'react'
22
import { FormattedMessage } from 'react-intl'
33
import { AppContext } from '../../context/context'
44
import { useDialogDispatchers } from '../../context/provider'
5-
import { AppModalCancelTypes } from '../../types'
65
declare global {
76
interface Window {
87
_paq: any
@@ -11,12 +10,13 @@ declare global {
1110
const _paq = (window._paq = window._paq || [])
1211

1312
interface MatomoDialogProps {
14-
okFn: () => void
13+
acceptAllFn: () => void
14+
managePreferencesFn: () => void
1515
hide: boolean
1616
}
1717

1818
const MatomoDialog = (props: MatomoDialogProps) => {
19-
const { settings, showMatomo, appManager } = useContext(AppContext)
19+
const { settings, showMatomo } = useContext(AppContext)
2020
const { modal } = useDialogDispatchers()
2121
const [visible, setVisible] = useState<boolean>(props.hide)
2222

@@ -28,37 +28,23 @@ const MatomoDialog = (props: MatomoDialogProps) => {
2828
id="remixApp.matomoText1"
2929
values={{
3030
a: (chunks) => (
31-
<a href="https://matomo.org" target="_blank" rel="noreferrer">
31+
<a href="https://remix-ide.readthedocs.io/en/latest/ai.html" target="_blank" rel="noreferrer">
3232
{chunks}
3333
</a>
3434
),
3535
}}
36-
/>
37-
</p>
38-
<p>
39-
<FormattedMessage id="remixApp.matomoText2" />
40-
</p>
41-
<p>
42-
<FormattedMessage id="remixApp.matomoText3" />
43-
</p>
44-
<p>
45-
<FormattedMessage id="remixApp.matomoText4" />
46-
</p>
47-
<p>
36+
/><br/>
4837
<FormattedMessage
49-
id="remixApp.matomoText5"
38+
id="remixApp.matomoText2"
5039
values={{
5140
a: (chunks) => (
52-
<a href="https://medium.com/p/66ef69e14931/" target="_blank" rel="noreferrer">
41+
<a href="https://matomo.org" target="_blank" rel="noreferrer">
5342
{chunks}
5443
</a>
5544
),
5645
}}
5746
/>
5847
</p>
59-
<p>
60-
<FormattedMessage id="remixApp.matomoText6" />
61-
</p>
6248
</>
6349
)
6450
}
@@ -70,30 +56,28 @@ const MatomoDialog = (props: MatomoDialogProps) => {
7056
title: <FormattedMessage id="remixApp.matomoTitle" />,
7157
message: message(),
7258
okLabel: <FormattedMessage id="remixApp.accept" />,
73-
okFn: handleModalOkClick,
74-
cancelLabel: <FormattedMessage id="remixApp.decline" />,
75-
cancelFn: declineModal,
59+
okFn: handleAcceptAllClick,
60+
cancelLabel: <FormattedMessage id="remixApp.managePreferences" />,
61+
cancelFn: handleManagePreferencesClick,
62+
showCancelIcon: true,
7663
preventBlur: true
7764
})
7865
}
7966
}, [visible])
8067

81-
const declineModal = async (reason: AppModalCancelTypes) => {
82-
if (reason === AppModalCancelTypes.click || reason === AppModalCancelTypes.enter) {
83-
settings.updateMatomoAnalyticsChoice(false)
84-
// revoke tracking consent
85-
_paq.push(['forgetConsentGiven'])
86-
setVisible(false)
87-
}
68+
const handleAcceptAllClick = async () => {
69+
_paq.push(['setConsentGiven']) // default consent to process their anonymous data
70+
_paq.push(['setMatomoPerfConsentGiven']) // user has given consent to process their performance data
71+
settings.updateMatomoAnalyticsChoice(true) // Enable Matomo Anonymous analytics
72+
settings.updateMatomoPerfAnalyticsChoice(true) // Enable Matomo Performance analytics
73+
settings.updateCopilotChoice(true) // Enable RemixAI copilot
74+
setVisible(false)
75+
props.acceptAllFn()
8876
}
8977

90-
const handleModalOkClick = async () => {
91-
92-
// user has given consent to process their data
93-
_paq.push(['setConsentGiven'])
94-
settings.updateMatomoAnalyticsChoice(true)
78+
const handleManagePreferencesClick = async () => {
9579
setVisible(false)
96-
props.okFn()
80+
props.managePreferencesFn()
9781
}
9882

9983
return <></>

libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ interface ModalWrapperProps extends ModalDialogProps {
88
}
99

1010
const ModalWrapper = (props: ModalWrapperProps) => {
11+
1112
const [state, setState] = useState<ModalDialogProps>()
1213
const ref = useRef()
1314
const formRef = useRef()
@@ -144,6 +145,6 @@ const ModalWrapper = (props: ModalWrapperProps) => {
144145

145146
if (!props.id || props.id === '') return null
146147

147-
return <ModalDialog id={props.id} {...state} handleHide={handleHide} />
148+
return <ModalDialog id={props.id} {...state} handleHide={handleHide} showCancelIcon={props.showCancelIcon} />
148149
}
149150
export default ModalWrapper

libs/remix-ui/app/src/lib/remix-app/context/provider.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt
2323
}
2424

2525
const modal = (modalData: AppModal) => {
26-
const { id, title, message, validationFn, okLabel, okFn, cancelLabel, cancelFn, modalType, modalParentClass, defaultValue, hideFn, data, preventBlur } = modalData
26+
const { id, title, message, validationFn, okLabel, okFn, cancelLabel, cancelFn, modalType, modalParentClass, defaultValue, hideFn, data, showCancelIcon, preventBlur } = modalData
2727
return new Promise((resolve, reject) => {
2828
dispatch({
2929
type: modalActionTypes.setModal,
@@ -43,6 +43,7 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt
4343
resolve,
4444
next: onNextFn,
4545
data,
46+
showCancelIcon,
4647
preventBlur
4748
}
4849
})

libs/remix-ui/app/src/lib/remix-app/interface/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export interface AppModal {
2525
resolve?: (value?:any) => void,
2626
next?: () => void,
2727
data?: any,
28+
showCancelIcon?: boolean,
2829
preventBlur?: boolean
2930
}
3031

0 commit comments

Comments
 (0)