From 6310d9230b572e649ecc7328b84963c3185898b8 Mon Sep 17 00:00:00 2001
From: Jed Fox <git@jedfox.com>
Date: Fri, 30 Dec 2022 13:05:58 -0500
Subject: [PATCH] Simplify the settings layout and merge everything onto one
 page (#267)

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
---
 .../desktop-client/src/components/Settings.js | 563 ++++++------------
 packages/loot-core/src/mocks/setup.js         |   4 -
 packages/loot-core/src/server/main.js         |  38 --
 .../loot-core/src/server/tracking/events.js   |  61 --
 .../mobile/src/components/manager/Login.js    |   6 +-
 5 files changed, 183 insertions(+), 489 deletions(-)
 delete mode 100644 packages/loot-core/src/server/tracking/events.js

diff --git a/packages/desktop-client/src/components/Settings.js b/packages/desktop-client/src/components/Settings.js
index 616daca20..e93656401 100644
--- a/packages/desktop-client/src/components/Settings.js
+++ b/packages/desktop-client/src/components/Settings.js
@@ -13,6 +13,7 @@ import {
   View,
   Text,
   Button,
+  Link,
   ButtonWithLoading,
   AnchorLink
 } from 'loot-design/src/components/common';
@@ -20,6 +21,7 @@ import { styles, colors } from 'loot-design/src/style';
 import ExpandArrow from 'loot-design/src/svg/ExpandArrow';
 
 import useServerVersion from '../hooks/useServerVersion';
+import { Page } from './Page';
 
 let dateFormats = [
   { value: 'MM/dd/yyyy', label: 'MM/DD/YYYY' },
@@ -29,23 +31,49 @@ let dateFormats = [
   { value: 'dd.MM.yyyy', label: 'DD.MM.YYYY' }
 ];
 
-function Title({ name, style }) {
+function Section({ title, children, style, titleProps, ...props }) {
+  return (
+    <View style={[{ gap: 20, alignItems: 'flex-start' }, style]} {...props}>
+      <View
+        style={[
+          { fontSize: 20, fontWeight: 500, flexShrink: 0 },
+          titleProps && titleProps.style
+        ]}
+        {...titleProps}
+      >
+        {title}
+      </View>
+      {children}
+    </View>
+  );
+}
+
+function ButtonSetting({ button, children, onClick }) {
   return (
     <View
-      style={[
-        { fontSize: 20, fontWeight: 500, marginBottom: 20, flexShrink: 0 },
-        style
-      ]}
+      style={{
+        backgroundColor: colors.n9,
+        alignSelf: 'flex-start',
+        alignItems: 'flex-start',
+        padding: 15,
+        borderRadius: 4,
+        border: '1px solid ' + colors.n8
+      }}
     >
-      {name}
+      <View
+        style={{ marginBottom: 10, maxWidth: 500, lineHeight: 1.5, gap: 10 }}
+      >
+        {children}
+      </View>
+      {button}
     </View>
   );
 }
 
-function Advanced({ prefs, userData, pushModal, resetSync }) {
-  let [expanded, setExpanded] = useState(true);
+function Advanced({ prefs, resetSync }) {
   let [resetting, setResetting] = useState(false);
   let [resettingCache, setResettingCache] = useState(false);
+  let [expanded, setExpanded] = useState(false);
 
   async function onResetSync() {
     setResetting(true);
@@ -59,98 +87,56 @@ function Advanced({ prefs, userData, pushModal, resetSync }) {
     setResettingCache(false);
   }
 
-  return (
-    <View style={{ alignItems: 'flex-start', marginTop: 55 }}>
-      <View
-        style={[
-          {
-            fontSize: 15,
-            marginBottom: 20,
-            flexDirection: 'row',
-            alignItems: 'center'
-          },
-          styles.staticText
-        ]}
-        onClick={() => setExpanded(!expanded)}
+  return expanded ? (
+    <Section title="Advanced Settings" style={{ marginBottom: 25 }}>
+      <Text>Budget ID: {prefs.id}</Text>
+      <Text style={{ color: colors.n6 }}>
+        Sync ID: {prefs.groupId || '(none)'}
+      </Text>
+
+      <ButtonSetting
+        button={
+          <ButtonWithLoading loading={resettingCache} onClick={onResetCache}>
+            Reset budget cache
+          </ButtonWithLoading>
+        }
       >
-        <ExpandArrow
-          width={8}
-          height={8}
-          style={{
-            marginRight: 5,
-            transition: 'transform .2s',
-            transform: !expanded && 'rotateZ(-90deg)'
-          }}
-        />
-        Advanced
-      </View>
-
-      {expanded && (
-        <View style={{ marginBottom: 20, alignItems: 'flex-start' }}>
-          <Text>
-            <strong>Budget ID</strong>: {prefs.id}
-          </Text>
-
-          <View
-            style={{
-              backgroundColor: colors.n9,
-              alignItems: 'flex-start',
-              padding: 15,
-              borderRadius: 4,
-              marginTop: 20,
-              border: '1px solid ' + colors.n8
-            }}
-          >
-            <Text style={{ marginBottom: 10, width: 500, lineHeight: 1.5 }}>
-              <strong>Reset budget cache</strong> will clear all cached values
-              for the budget and recalculate the entire budget. All values in
-              the budget are cached for performance reasons, and if there is a
-              bug in the cache you won't see correct values. There is no danger
-              in resetting the cache. Hopefully you never have to do this.
-            </Text>
-            <ButtonWithLoading loading={resettingCache} onClick={onResetCache}>
-              Reset budget cache
-            </ButtonWithLoading>
-          </View>
-
-          <View
-            style={{
-              backgroundColor: colors.n9,
-              alignItems: 'flex-start',
-              padding: 15,
-              borderRadius: 4,
-              marginTop: 20,
-              border: '1px solid ' + colors.n8
-            }}
-          >
-            <Text style={{ marginBottom: 10, width: 500, lineHeight: 1.5 }}>
-              <strong>Reset sync</strong> will remove all local data used to
-              track changes for syncing, and create a fresh sync id on our
-              server. This file on other devices will have to be re-downloaded
-              to use the new sync id. Use this if there is a problem with
-              syncing and you want to start fresh.
-            </Text>
-
-            <ButtonWithLoading loading={resetting} onClick={onResetSync}>
-              Reset sync
-            </ButtonWithLoading>
-            <Text style={{ marginTop: 15, color: colors.n4, fontSize: 12 }}>
-              Sync ID: {prefs.groupId || '(none)'}
-            </Text>
-          </View>
-        </View>
-      )}
-    </View>
+        <Text>
+          <strong>Reset budget cache</strong> will clear all cached values for
+          the budget and recalculate the entire budget. All values in the budget
+          are cached for performance reasons, and if there is a bug in the cache
+          you won't see correct values. There is no danger in resetting the
+          cache. Hopefully you never have to do this.
+        </Text>
+      </ButtonSetting>
+
+      <ButtonSetting
+        button={
+          <ButtonWithLoading loading={resetting} onClick={onResetSync}>
+            Reset sync
+          </ButtonWithLoading>
+        }
+      >
+        <Text>
+          <strong>Reset sync</strong> will remove all local data used to track
+          changes for syncing, and create a fresh sync ID on our server. This
+          file on other devices will have to be re-downloaded to use the new
+          sync ID. Use this if there is a problem with syncing and you want to
+          start fresh.
+        </Text>
+      </ButtonSetting>
+    </Section>
+  ) : (
+    <Link
+      onClick={() => setExpanded(true)}
+      style={{ flexShrink: 0, alignSelf: 'flex-start', color: colors.p4 }}
+    >
+      Show advanced settings
+    </Link>
   );
 }
 
-function GlobalSettings({
-  globalPrefs,
-  userData,
-  saveGlobalPrefs,
-  pushModal,
-  closeBudget
-}) {
+function GlobalSettings({ globalPrefs, saveGlobalPrefs }) {
   let [documentDirChanged, setDirChanged] = useState(false);
   let dirScrolled = useRef(null);
 
@@ -170,24 +156,13 @@ function GlobalSettings({
     }
   }
 
-  function onAutoUpdate(e) {
-    saveGlobalPrefs({ autoUpdate: e.target.checked });
-  }
-
-  function onTrackUsage(e) {
-    saveGlobalPrefs({ trackUsage: e.target.checked });
-  }
-
   return (
-    <View>
-      <View>
-        <Title name="General" />
-
-        {!Platform.isBrowser && (
+    <>
+      {!Platform.isBrowser && (
+        <Section title="General">
           <View
             style={{
               flexDirection: 'row',
-              maxWidth: 550,
               alignItems: 'center',
               overflow: 'hidden'
             }}
@@ -223,98 +198,19 @@ function GlobalSettings({
               Change location
             </Button>
           </View>
-        )}
-
-        {documentDirChanged && (
-          <Information style={{ marginTop: 10 }}>
-            A restart is required for this change to take effect
-          </Information>
-        )}
-
-        <View
-          style={{
-            flexDirection: 'row',
-            marginTop: 30,
-            alignItems: 'flex-start'
-          }}
-        >
-          <input
-            type="checkbox"
-            checked={globalPrefs.autoUpdate}
-            style={{ marginRight: 5 }}
-            onChange={onAutoUpdate}
-          />
-
-          <View>
-            <Text style={{ fontSize: 15 }}>
-              Automatically check for updates
-            </Text>
-            <View
-              style={{
-                color: colors.n2,
-                marginTop: 10,
-                maxWidth: 600,
-                lineHeight: '1.4em'
-              }}
-            >
-              By default, Actual will automatically apply new updates as they
-              are available. Disabling this will avoid updating Actual. You will
-              need to go to the About menu to manually check for updates.
-            </View>
-          </View>
-        </View>
-      </View>
-
-      <View style={{ marginTop: 30 }}>
-        <Title name="Privacy" />
-
-        <View
-          style={{
-            flexDirection: 'row',
-            marginTop: 30,
-            alignItems: 'flex-start'
-          }}
-        >
-          <input
-            type="checkbox"
-            checked={globalPrefs.trackUsage}
-            style={{ marginRight: 5 }}
-            onChange={onTrackUsage}
-          />
-
-          <View>
-            <Text style={{ fontSize: 15 }}>
-              Send basic usage statistics back to Actual{"'"}s servers
-            </Text>
-            <View
-              style={{
-                color: colors.n2,
-                marginTop: 10,
-                maxWidth: 600,
-                lineHeight: '1.4em'
-              }}
-            >
-              We don{"'"}t track anything specific &mdash; only the fact that
-              you{"'"}ve opened Actual. This helps by giving us important
-              feedback about how popular new features are.
-            </View>
-          </View>
-        </View>
-      </View>
-    </View>
+          )}
+          {documentDirChanged && (
+            <Information style={{ marginTop: 10 }}>
+              A restart is required for this change to take effect
+            </Information>
+          )}
+        </Section>
+      )}
+    </>
   );
 }
 
-function FileSettings({
-  savePrefs,
-  prefs,
-  userData,
-  localServerURL,
-  pushModal,
-  resetSync,
-  setAppState,
-  signOut
-}) {
+function FileSettings({ savePrefs, prefs, pushModal, resetSync }) {
   function onDateFormat(e) {
     let format = e.target.value;
     savePrefs({ dateFormat: format });
@@ -336,158 +232,108 @@ function FileSettings({
 
   let dateFormat = prefs.dateFormat || 'MM/dd/yyyy';
   let numberFormat = prefs.numberFormat || 'comma-dot';
-
   return (
-    <View>
-      <View style={{ marginTop: 30 }}>
-        <Title name="Formatting" />
-
+    <>
+      <Section title="Formatting">
         <Text>
-          Date format:{' '}
+          <label for="settings-numberFormat">Number format: </label>
           <select
+            id="settings-numberFormat"
             {...css({ marginLeft: 5, fontSize: 14 })}
-            onChange={onDateFormat}
+            onChange={onNumberFormat}
           >
-            {dateFormats.map(f => (
-              <option value={f.value} selected={f.value === dateFormat}>
+            {numberFormats.map(f => (
+              <option value={f.value} selected={f.value === numberFormat}>
                 {f.label}
               </option>
             ))}
           </select>
         </Text>
 
-        <Text style={{ marginTop: 20 }}>
-          Number format:{' '}
+        <Text>
+          <label for="settings-dateFormat">Date format: </label>
           <select
+            id="settings-dateFormat"
             {...css({ marginLeft: 5, fontSize: 14 })}
-            onChange={onNumberFormat}
+            onChange={onDateFormat}
           >
-            {numberFormats.map(f => (
-              <option value={f.value} selected={f.value === numberFormat}>
+            {dateFormats.map(f => (
+              <option value={f.value} selected={f.value === dateFormat}>
                 {f.label}
               </option>
             ))}
           </select>
         </Text>
-      </View>
+      </Section>
 
-      <View style={{ marginTop: 30 }}>
-        <Title name="Encryption" />
-        <View style={{ flexDirection: 'row' }}>
-          <View>
-            <Text style={{ fontWeight: 700, fontSize: 15 }}>
-              End-to-end encryption
-            </Text>
-            <View
-              style={{
-                color: colors.n2,
-                marginTop: 10,
-                maxWidth: 600,
-                lineHeight: '1.4em'
+      {prefs.encryptKeyId ? (
+        <ButtonSetting
+          button={
+            <Button onClick={() => onChangeKey()}>Generate new key</Button>
+          }
+        >
+          <Text>
+            <Text style={{ color: colors.g4, fontWeight: 600 }}>
+              End-to-end Encryption is turned on.
+            </Text>{' '}
+            Your data is encrypted with a key that only you have before sending
+            it out to the cloud . Local data remains unencrypted so if you
+            forget your password you can re-encrypt it.
+          </Text>
+        </ButtonSetting>
+      ) : (
+        <ButtonSetting
+          button={
+            <Button
+              onClick={() => {
+                alert(
+                  'End-to-end encryption is not supported on the self-hosted service yet'
+                );
+                // pushModal('create-encryption-key');
               }}
             >
-              {prefs.encryptKeyId ? (
-                <Text>
-                  <Text style={{ color: colors.g4, fontWeight: 600 }}>
-                    Encryption is turned on.
-                  </Text>{' '}
-                  Your data is encrypted with a key that only you have before
-                  sending it out to the cloud . Local data remains unencrypted
-                  so if you forget your password you can re-encrypt it.
-                  <Button
-                    style={{ marginTop: 10 }}
-                    onClick={() => onChangeKey()}
-                  >
-                    Generate new key
-                  </Button>
-                </Text>
-              ) : (
-                <View style={{ alignItems: 'flex-start' }}>
-                  <Text style={{ lineHeight: '1.4em' }}>
-                    Encryption is not enabled. Any data on our servers is still
-                    stored safely and securely, but it's not end-to-end
-                    encrypted which means we have the ability to read it (but we
-                    won't). If you want, you can use a password to encrypt your
-                    data on our servers.
-                  </Text>
-                  <Button
-                    style={{ marginTop: 10 }}
-                    onClick={() => {
-                      alert(
-                        'End-to-end encryption is not supported on the self-hosted service yet'
-                      );
-                      // pushModal('create-encryption-key');
-                    }}
-                  >
-                    Enable encryption
-                  </Button>
-                </View>
-              )}
-            </View>
-          </View>
-        </View>
-      </View>
-
-      <View style={{ marginTop: 30, alignItems: 'flex-start' }}>
-        <Title name="Export" />
-        <Button onClick={onExport}>Export data</Button>
-      </View>
+              Enable encryption…
+            </Button>
+          }
+        >
+          <Text>
+            <strong>End-to-end encryption</strong> is not enabled. Any data on
+            our servers is still stored safely and securely, but it's not
+            end-to-end encrypted which means we have the ability to read it (but
+            we won't). If you want, you can use a password to encrypt your data
+            on our servers.
+          </Text>
+        </ButtonSetting>
+      )}
 
-      <Advanced
-        prefs={prefs}
-        userData={userData}
-        pushModal={pushModal}
-        resetSync={resetSync}
-      />
-    </View>
-  );
-}
+      <ButtonSetting button={<Button onClick={onExport}>Export data</Button>}>
+        <Text>
+          <strong>Export</strong> your data as a zip file containing{' '}
+          <code>db.sqlite</code> and <code>metadata.json</code> files. It can be
+          imported into another Actual instance by clicking the “Import file”
+          button and then choosing “Actual” on the Files page.
+        </Text>
+        {prefs.encryptKeyId ? (
+          <Text>
+            Even though encryption is enabled, the exported zip file will not
+            have any encryption.
+          </Text>
+        ) : null}
+      </ButtonSetting>
 
-function SettingsLink({ to, name, style, first, last }) {
-  return (
-    <AnchorLink
-      to={to}
-      style={[
-        {
-          fontSize: 14,
-          padding: '6px 10px',
-          borderBottom: '2px solid transparent',
-          textDecoration: 'none',
-          borderRadius: first ? '4px 0 0 4px' : last ? '0 4px 4px 0' : 4,
-          border: '1px solid ' + colors.n4,
-          color: colors.n3
-        },
-        style
-      ]}
-      activeStyle={{
-        backgroundColor: colors.p6,
-        borderColor: colors.p6,
-        color: 'white'
-      }}
-    >
-      {name}
-    </AnchorLink>
+      <Advanced prefs={prefs} resetSync={resetSync} />
+    </>
   );
 }
 
-function Version() {
+function About() {
   const version = useServerVersion();
 
   return (
-    <Text
-      style={[
-        {
-          alignSelf: 'center',
-          color: colors.n7,
-          ':hover': { color: colors.n2 },
-          padding: '6px 10px'
-        },
-        styles.staticText,
-        styles.smallText
-      ]}
-    >
-      {`App: v${window.Actual.ACTUAL_VERSION} | Server: ${version}`}
-    </Text>
+    <Section title="About">
+      <Text>Client version: v{window.Actual.ACTUAL_VERSION}</Text>
+      <Text>Server version: {version}</Text>
+    </Section>
   );
 }
 
@@ -509,68 +355,23 @@ class Settings extends React.Component {
     let { prefs, globalPrefs, localServerURL, userData, match } = this.props;
 
     return (
-      <View style={[styles.page, { overflow: 'hidden', fontSize: 14 }]}>
-        <View
-          style={{
-            flexDirection: 'row',
-            alignSelf: 'center',
-            margin: '15px 0 5px 0'
-          }}
-        >
-          <SettingsLink to={`${match.path}/file`} name="File" first={true} />
-          <SettingsLink to={`${match.path}/global`} name="Global" last={true} />
-        </View>
-        <View
-          style={{
-            flexDirection: 'row',
-            alignSelf: 'center',
-            margin: '0 0 10px 0'
-          }}
-        >
-          <Version />
-        </View>
+      <Page title="Settings">
+        <View style={{ flexShrink: 0, gap: 30, maxWidth: 600 }}>
+          <About />
 
-        <View
-          style={[
-            styles.pageContent,
-            {
-              alignItems: 'flex-start',
-              flex: 1,
-              overflow: 'auto',
-              paddingBottom: 20
-            }
-          ]}
-        >
-          <View style={{ flexShrink: 0 }}>
-            <Switch>
-              <Route path={`${match.path}/`} exact>
-                <Redirect to={`${match.path}/file`} />
-              </Route>
-              <Route path={`${match.path}/global`}>
-                <GlobalSettings
-                  globalPrefs={globalPrefs}
-                  userData={userData}
-                  saveGlobalPrefs={this.props.saveGlobalPrefs}
-                  pushModal={this.props.pushModal}
-                  closeBudget={this.props.closeBudget}
-                />
-              </Route>
-              <Route path={`${match.path}/file`}>
-                <FileSettings
-                  prefs={prefs}
-                  localServerURL={localServerURL}
-                  userData={userData}
-                  pushModal={this.props.pushModal}
-                  savePrefs={this.props.savePrefs}
-                  setAppState={this.props.setAppState}
-                  signOut={this.props.signOut}
-                  resetSync={this.props.resetSync}
-                />
-              </Route>
-            </Switch>
-          </View>
+          <GlobalSettings
+            globalPrefs={globalPrefs}
+            saveGlobalPrefs={this.props.saveGlobalPrefs}
+          />
+
+          <FileSettings
+            prefs={prefs}
+            userData={userData}
+            pushModal={this.props.pushModal}
+            resetSync={this.props.resetSync}
+          />
         </View>
-      </View>
+      </Page>
     );
   }
 }
diff --git a/packages/loot-core/src/mocks/setup.js b/packages/loot-core/src/mocks/setup.js
index 172aea37f..728293138 100644
--- a/packages/loot-core/src/mocks/setup.js
+++ b/packages/loot-core/src/mocks/setup.js
@@ -9,15 +9,11 @@ import {
 import { setServer } from '../server/server-config';
 import * as sheet from '../server/sheet';
 import { setSyncingMode } from '../server/sync';
-import * as tracking from '../server/tracking/events';
 import { updateVersion } from '../server/update';
 import { resetTracer, tracer } from '../shared/test-helpers';
 
 jest.mock('../server/post');
 
-// No need to run any of the tracking code in tests
-tracking.toggle(false);
-
 const nativeFs = require('fs');
 
 // By default, syncing is disabled
diff --git a/packages/loot-core/src/server/main.js b/packages/loot-core/src/server/main.js
index f7f4106af..2ff712dcd 100644
--- a/packages/loot-core/src/server/main.js
+++ b/packages/loot-core/src/server/main.js
@@ -68,7 +68,6 @@ import {
 } from './sync';
 import * as syncMigrations from './sync/migrate';
 import toolsApp from './tools/app';
-import * as tracking from './tracking/events';
 import { withUndo, clearUndo, undo, redo } from './undo';
 import { updateVersion } from './update';
 import { uniqueFileName, idFromFileName } from './util/budget-name';
@@ -1212,10 +1211,6 @@ handlers['save-global-prefs'] = async function(prefs) {
   if ('maxMonths' in prefs) {
     await asyncStorage.setItem('max-months', '' + prefs.maxMonths);
   }
-  if ('trackUsage' in prefs) {
-    tracking.toggle(prefs.trackUsage);
-    await asyncStorage.setItem('track-usage', '' + prefs.trackUsage);
-  }
   if ('autoUpdate' in prefs) {
     await asyncStorage.setItem('auto-update', '' + prefs.autoUpdate);
     process.send({ type: 'shouldAutoUpdate', flag: prefs.autoUpdate });
@@ -1236,7 +1231,6 @@ handlers['load-global-prefs'] = async function() {
     [, floatingSidebar],
     [, seenTutorial],
     [, maxMonths],
-    [, trackUsage],
     [, autoUpdate],
     [, documentDir],
     [, encryptKey]
@@ -1244,7 +1238,6 @@ handlers['load-global-prefs'] = async function() {
     'floating-sidebar',
     'seen-tutorial',
     'max-months',
-    'track-usage',
     'auto-update',
     'document-dir',
     'encrypt-key'
@@ -1253,8 +1246,6 @@ handlers['load-global-prefs'] = async function() {
     floatingSidebar: floatingSidebar === 'true' ? true : false,
     seenTutorial: seenTutorial === 'true' ? true : false,
     maxMonths: stringToInteger(maxMonths || ''),
-    // Default to true
-    trackUsage: trackUsage == null || trackUsage === 'true' ? true : false,
     autoUpdate: autoUpdate == null || autoUpdate === 'true' ? true : false,
     documentDir: documentDir || getDefaultDocumentDir(),
     keyId: encryptKey && JSON.parse(encryptKey).id
@@ -1687,26 +1678,6 @@ handlers['load-budget'] = async function({ id }) {
 
   let res = await loadBudget(id, VERSION, { showUpdate: true });
 
-  async function trackSizes() {
-    let getFileSize = async name => {
-      let dbFile = fs.join(fs.getBudgetDir(id), name);
-      try {
-        return await fs.size(dbFile);
-      } catch (err) {
-        return null;
-      }
-    };
-
-    try {
-      let dbSize = await getFileSize('db.sqlite');
-      let cacheSize = await getFileSize('cache.sqlite');
-      tracking.track('app:load-budget', { size: dbSize, cacheSize });
-    } catch (err) {
-      console.warn(err);
-    }
-  }
-  trackSizes();
-
   return res;
 };
 
@@ -2104,10 +2075,6 @@ handlers['app-focused'] = async function() {
   }
 };
 
-handlers['track'] = async function({ name, props }) {
-  tracking.track(name, props);
-};
-
 handlers = installAPI(handlers);
 
 injectAPI.send = (name, args) => runHandler(app.handlers[name], args);
@@ -2160,7 +2127,6 @@ export async function initApp(version, isDev, socketName) {
 
   await sqlite.init();
   await Promise.all([asyncStorage.init(), fs.init()]);
-  await tracking.init();
   await setupDocumentsDir();
 
   let keysStr = await asyncStorage.getItem('encrypt-keys');
@@ -2194,10 +2160,6 @@ export async function initApp(version, isDev, socketName) {
 
   connection.init(socketName, app.handlers);
 
-  tracking.track('app:init', {
-    platform: Platform.isMobile ? 'mobile' : Platform.isWeb ? 'web' : 'desktop'
-  });
-
   if (!isDev && !Platform.isMobile && !Platform.isWeb) {
     let autoUpdate = await asyncStorage.getItem('auto-update');
     process.send({
diff --git a/packages/loot-core/src/server/tracking/events.js b/packages/loot-core/src/server/tracking/events.js
deleted file mode 100644
index fea5dd6e2..000000000
--- a/packages/loot-core/src/server/tracking/events.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { sha256String } from '../encryption-internals';
-
-let currentUniqueId;
-let mixpanel;
-let isEnabled = true;
-
-export function toggle(trackUsage) {
-  isEnabled = trackUsage == null || trackUsage === 'true' ? true : false;
-}
-
-// TODO: Figure out location, send to EU data centers if in EU
-// {
-//     host: "api-eu.mixpanel.com",
-// },
-
-// This must stay up-to-date with all apps that hit mixpanel! That includes the
-// website and server. If changing this, make sure to change it everywhere
-async function hash(userId) {
-  let hashed = await sha256String(userId);
-  return `user-${hashed.replace(/[=/]/g, '')}`;
-}
-
-function isAnonymous(id) {
-  return !id.startsWith('user-');
-}
-
-export async function init() {}
-
-export async function login(userId) {}
-
-let BUFFERING = false;
-let BUFFER = [];
-
-function startBuffering() {
-  BUFFERING = true;
-  BUFFER = [];
-}
-
-function stopBuffering() {
-  for (let call of BUFFER) {
-    call[0](...call[1]);
-  }
-  BUFFERING = false;
-  BUFFER = [];
-}
-
-function buffered(func) {
-  return (...args) => {
-    if (process.env.NODE_ENV !== 'development') {
-      if (BUFFERING) {
-        BUFFER.push([func, [currentUniqueId, ...args]]);
-      } else {
-        func(currentUniqueId, ...args);
-      }
-    }
-  };
-}
-
-export const track = buffered((distinct_id, name, props) => {});
-
-export const setProfile = buffered((distinct_id, props) => {});
diff --git a/packages/mobile/src/components/manager/Login.js b/packages/mobile/src/components/manager/Login.js
index 2c114ca51..3f835702b 100644
--- a/packages/mobile/src/components/manager/Login.js
+++ b/packages/mobile/src/components/manager/Login.js
@@ -55,7 +55,6 @@ function Login({ navigation, createBudget }) {
             navigation={navigation}
             buttons={['back', 'demo']}
             loadDemoBudget={() => {
-              send('track', { name: 'app:create-demo' });
               createBudget({ demoMode: true });
             }}
           />
@@ -90,7 +89,4 @@ function Login({ navigation, createBudget }) {
   );
 }
 
-export default connect(
-  null,
-  actions
-)(Login);
+export default connect(null, actions)(Login);
-- 
GitLab