From 059b2ce10ec7b615744929201e4f173fcdb20178 Mon Sep 17 00:00:00 2001
From: Ryan Laurie <30528226+iethree@users.noreply.github.com>
Date: Thu, 12 Sep 2024 12:38:58 -0600
Subject: [PATCH] Remove more metabse settings (#47510)

* remove more metabse settings

* fix missing update for latest version check

* test updates

* fix setting selectors
---
 .../SettingsSAMLForm/SettingsSAMLForm.jsx     | 11 ++---
 .../src/metabase-enterprise/plugins.js        |  4 --
 .../no-unconditional-metabase-links-render.js | 37 ++------------
 ...itional-metabase-links-render.unit.spec.js | 41 ----------------
 .../src/metabase-types/api/mocks/settings.ts  |  2 +-
 frontend/src/metabase-types/api/settings.ts   |  1 +
 frontend/src/metabase/admin/app/actions.ts    |  7 +--
 .../ModelCachingControl.tsx                   |  7 ++-
 .../databases/containers/DatabaseListApp.jsx  |  6 +--
 .../DataModelApp/ModelEducationalModal.jsx    |  4 +-
 .../people/containers/UserSuccessModal.jsx    | 19 ++++---
 .../SettingsUpdatesForm.jsx                   |  7 +--
 .../SettingsUpdatesForm.unit.spec.js          |  8 +--
 .../VersionUpdateNotice.jsx                   | 23 ++++++---
 .../src/metabase/admin/settings/selectors.js  | 28 +++++++++--
 .../components/HomeHelpCard/HomeHelpCard.tsx  |  4 +-
 frontend/src/metabase/lib/settings.ts         | 49 +------------------
 frontend/src/metabase/lib/utils.ts            | 22 +++++++++
 .../NewModelOptions/NewModelOptions.tsx       |  5 +-
 .../PaymentBanner/PaymentBanner.tsx           |  6 +--
 .../pulse/components/RecipientPicker.tsx      |  8 +--
 .../components/RecipientPicker.unit.spec.tsx  | 12 ++---
 .../VisualizationError/VisualizationError.tsx |  6 +--
 frontend/src/metabase/routes.jsx              |  6 ++-
 frontend/src/metabase/selectors/settings.ts   |  9 ++++
 .../CloudMigrationHelp/CloudMigrationHelp.tsx |  4 +-
 26 files changed, 143 insertions(+), 193 deletions(-)

diff --git a/enterprise/frontend/src/metabase-enterprise/auth/components/SettingsSAMLForm/SettingsSAMLForm.jsx b/enterprise/frontend/src/metabase-enterprise/auth/components/SettingsSAMLForm/SettingsSAMLForm.jsx
index 7805839f16e..e897df4e1cb 100644
--- a/enterprise/frontend/src/metabase-enterprise/auth/components/SettingsSAMLForm/SettingsSAMLForm.jsx
+++ b/enterprise/frontend/src/metabase-enterprise/auth/components/SettingsSAMLForm/SettingsSAMLForm.jsx
@@ -9,7 +9,7 @@ import SettingHeader from "metabase/admin/settings/components/SettingHeader";
 import GroupMappingsWidget from "metabase/admin/settings/containers/GroupMappingsWidget";
 import { updateSamlSettings } from "metabase/admin/settings/settings";
 import { settingToFormField } from "metabase/admin/settings/utils";
-import { useDocsUrl } from "metabase/common/hooks";
+import { useDocsUrl, useSetting } from "metabase/common/hooks";
 import Breadcrumbs from "metabase/components/Breadcrumbs";
 import ExternalLink from "metabase/core/components/ExternalLink";
 import CS from "metabase/css/core/index.css";
@@ -23,7 +23,6 @@ import {
   FormTextInput,
   FormTextarea,
 } from "metabase/forms";
-import MetabaseSettings from "metabase/lib/settings";
 import { Stack } from "metabase/ui";
 
 import {
@@ -72,11 +71,13 @@ const SettingsSAMLForm = ({ elements = [], settingValues = {}, onSubmit }) => {
     "people-and-groups/authenticating-with-saml",
   );
 
+  const siteUrl = useSetting("site-url");
+
   return (
     <FormProvider
       initialValues={{
         ...attributeValues,
-        [FAKE_ACS_URL_KEY]: getAcsCustomerUrl(),
+        [FAKE_ACS_URL_KEY]: `${siteUrl}/auth/sso`,
       }}
       onSubmit={handleSubmit}
       enableReinitialize
@@ -271,10 +272,6 @@ const getAttributeValues = (values, defaults) => {
   );
 };
 
-const getAcsCustomerUrl = () => {
-  return `${MetabaseSettings.get("site-url")}/auth/sso`;
-};
-
 SettingsSAMLForm.propTypes = propTypes;
 
 const mapDispatchToProps = {
diff --git a/enterprise/frontend/src/metabase-enterprise/plugins.js b/enterprise/frontend/src/metabase-enterprise/plugins.js
index 36857097559..cc278b0831a 100644
--- a/enterprise/frontend/src/metabase-enterprise/plugins.js
+++ b/enterprise/frontend/src/metabase-enterprise/plugins.js
@@ -1,10 +1,6 @@
-import MetabaseSettings from "metabase/lib/settings";
 import { PLUGIN_IS_EE_BUILD } from "metabase/plugins";
 
 // SETTINGS OVERRIDES:
-
-// NOTE: temporarily use "latest" for Enterprise Edition docs
-MetabaseSettings.docsTag = () => "latest";
 PLUGIN_IS_EE_BUILD.isEEBuild = () => true;
 
 import "./shared";
diff --git a/frontend/lint/eslint-rules/no-unconditional-metabase-links-render.js b/frontend/lint/eslint-rules/no-unconditional-metabase-links-render.js
index d7746c3bfd4..3f44b835d85 100644
--- a/frontend/lint/eslint-rules/no-unconditional-metabase-links-render.js
+++ b/frontend/lint/eslint-rules/no-unconditional-metabase-links-render.js
@@ -4,12 +4,11 @@
 //
 // The following cases are considered errors:
 //
-// 1. MetabaseSettings.learnUrl(string)
-// 2. useDocsUrl hook
-// 3. getDocsUrl selector from "metabase/selectors/settings"
-// 4. getLearnUrl selector from "metabase/selectors/settings"
-// 5. inline string "metabase.com/docs/"
-// 6. inline string "metabase.com/learn/"
+// 1. useDocsUrl hook
+// 2. getDocsUrl selector from "metabase/selectors/settings"
+// 3. getLearnUrl selector from "metabase/selectors/settings"
+// 4. inline string "metabase.com/docs/"
+// 5. inline string "metabase.com/learn/"
 //
 // If a link shouldn't be rendered conditionally e.g. it's only show for admins, or is rendered inside admin settings, you need to disable the rule with a reason.
 // e.g. "// eslint-disable-next-line no-unconditional-metabase-links-render -- This link only shows for admins."
@@ -50,7 +49,6 @@ module.exports = {
   },
 
   create(context) {
-    let metabaseSettings;
     let isGetDocsUrlSelectorImported = false;
     let isGetLearnUrlSelectorImported = false;
     let isGetShowMetabaseLinksSelectorImported = false;
@@ -89,17 +87,6 @@ module.exports = {
 
     return {
       ImportDeclaration(node) {
-        if (
-          getImportedModuleNode(node, {
-            isDefault: true,
-            source: "metabase/lib/settings",
-          })
-        ) {
-          metabaseSettings = getImportedModuleNode(node, {
-            isDefault: true,
-            source: "metabase/lib/settings",
-          });
-        }
         if (
           getImportedModuleNode(node, {
             named: "getDocsUrl",
@@ -171,20 +158,6 @@ module.exports = {
             message: ERROR_MESSAGE,
           });
         }
-
-        // call `MetabaseSettings.learnUrl`
-        if (
-          metabaseSettings?.references.some(
-            reference => reference.identifier === node?.callee?.object,
-          ) &&
-          !isGetShowMetabaseLinksSelectorImported &&
-          ["learnUrl", "docsUrl"].includes(node?.callee?.property?.name)
-        ) {
-          context.report({
-            node,
-            message: ERROR_MESSAGE,
-          });
-        }
       },
       Literal(node) {
         if (typeof node.value !== "string") {
diff --git a/frontend/lint/tests/no-unconditional-metabase-links-render.unit.spec.js b/frontend/lint/tests/no-unconditional-metabase-links-render.unit.spec.js
index 839116ba6bf..fedce3ded6d 100644
--- a/frontend/lint/tests/no-unconditional-metabase-links-render.unit.spec.js
+++ b/frontend/lint/tests/no-unconditional-metabase-links-render.unit.spec.js
@@ -19,20 +19,6 @@ const ruleTester = new RuleTester({
 const VALID_CASES = [
   {
     code: `
-import MetabaseSettings from "metabase/lib/settings";
-import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
-
-const docsUrl = MetabaseSettings.docsUrl("permissions/data")`,
-  },
-  {
-    code: `
-import MetabaseSettings from "metabase/lib/settings";
-import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
-
-const docsUrl = MetabaseSettings.learnUrl("permissions/data")`,
-  },
-  {
-    code: `
 import { useSelector } from "metabase/lib/redux";
 import { getDocsUrl } from "metabase/selectors/settings";
 import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
@@ -97,33 +83,6 @@ const { url } = useDocsUrl("permissions/data");`,
   },
 ];
 const INVALID_CASES = [
-  {
-    name: 'Detect any name of the default import of "metabase/lib/settings"',
-    code: `
-import Settings from "metabase/lib/settings";
-
-const docsUrl = Settings.docsUrl("permissions/data")`,
-    error:
-      /Metabase links must be rendered conditionally\.(.|\n)*Please import `getShowMetabaseLinks`(.|\n)*Or add comment to indicate the reason why this rule needs to be disabled/,
-  },
-  {
-    name: "Detect MetabaseSettings.docsUrl()",
-    code: `
-import MetabaseSettings from "metabase/lib/settings";
-
-const docsUrl = MetabaseSettings.docsUrl("permissions/data")`,
-    error:
-      /Metabase links must be rendered conditionally\.(.|\n)*Please import `getShowMetabaseLinks`(.|\n)*Or add comment to indicate the reason why this rule needs to be disabled/,
-  },
-  {
-    name: "Detect MetabaseSettings.learn()",
-    code: `
-import MetabaseSettings from "metabase/lib/settings";
-
-const docsUrl = MetabaseSettings.learnUrl("permissions/data")`,
-    error:
-      /Metabase links must be rendered conditionally\.(.|\n)*Please import `getShowMetabaseLinks`(.|\n)*Or add comment to indicate the reason why this rule needs to be disabled/,
-  },
   {
     name: "Detect getDocsUrl()",
     code: `
diff --git a/frontend/src/metabase-types/api/mocks/settings.ts b/frontend/src/metabase-types/api/mocks/settings.ts
index 152211f1dd6..fcc99403ab7 100644
--- a/frontend/src/metabase-types/api/mocks/settings.ts
+++ b/frontend/src/metabase-types/api/mocks/settings.ts
@@ -200,7 +200,7 @@ export const createMockSettings = (
   "openai-organization": null,
   "openai-model": null,
   "openai-available-models": [],
-  "other-sso-enabled?": null,
+  "other-sso-enabled?": false,
   "password-complexity": { total: 6, digit: 1 },
   "persisted-models-enabled": false,
   "persisted-model-refresh-cron-schedule": "0 0 0/6 * * ? *",
diff --git a/frontend/src/metabase-types/api/settings.ts b/frontend/src/metabase-types/api/settings.ts
index a7bc61f7ada..9b3a589dfc0 100644
--- a/frontend/src/metabase-types/api/settings.ts
+++ b/frontend/src/metabase-types/api/settings.ts
@@ -246,6 +246,7 @@ interface AdminSettings {
   "premium-embedding-token": string | null;
   "saml-configured"?: boolean;
   "saml-enabled"?: boolean;
+  "other-sso-enabled?"?: boolean; // yes the question mark is in the variable name
   "show-database-syncing-modal": boolean;
   "token-status": TokenStatus | null;
   "version-info": VersionInfo | null;
diff --git a/frontend/src/metabase/admin/app/actions.ts b/frontend/src/metabase/admin/app/actions.ts
index c680a5d62f0..cc6e25a40c2 100644
--- a/frontend/src/metabase/admin/app/actions.ts
+++ b/frontend/src/metabase/admin/app/actions.ts
@@ -1,13 +1,14 @@
 import { updateSetting } from "metabase/admin/settings/settings";
 import { createAsyncThunk } from "metabase/lib/redux";
-import Settings from "metabase/lib/settings";
+
+import { getCurrentVersion } from "../settings/selectors";
 
 export const disableNotice = createAsyncThunk(
   "metabase/admin/app/DISABLE_NOTICE",
-  async (_, { dispatch }) => {
+  async (_, { getState, dispatch }) => {
     const setting = {
       key: "deprecation-notice-version",
-      value: Settings.currentVersion(),
+      value: getCurrentVersion(getState()),
     };
     await dispatch(updateSetting(setting));
   },
diff --git a/frontend/src/metabase/admin/databases/components/DatabaseEditApp/Sidebar/ModelCachingControl/ModelCachingControl.tsx b/frontend/src/metabase/admin/databases/components/DatabaseEditApp/Sidebar/ModelCachingControl/ModelCachingControl.tsx
index 4fd689abcb2..30601632dcf 100644
--- a/frontend/src/metabase/admin/databases/components/DatabaseEditApp/Sidebar/ModelCachingControl/ModelCachingControl.tsx
+++ b/frontend/src/metabase/admin/databases/components/DatabaseEditApp/Sidebar/ModelCachingControl/ModelCachingControl.tsx
@@ -5,13 +5,12 @@ import {
   PERSIST_DATABASE,
   UNPERSIST_DATABASE,
 } from "metabase/admin/databases/database";
-import { useDocsUrl } from "metabase/common/hooks";
+import { useDocsUrl, useSetting } from "metabase/common/hooks";
 import ActionButton from "metabase/components/ActionButton";
 import TippyPopover from "metabase/components/Popover/TippyPopover";
 import ExternalLink from "metabase/core/components/ExternalLink";
 import ButtonsS from "metabase/css/components/buttons.module.css";
 import { useDispatch } from "metabase/lib/redux";
-import MetabaseSettings from "metabase/lib/settings";
 import { MetabaseApi } from "metabase/services";
 import type Database from "metabase-lib/v1/metadata/Database";
 import { getModelCacheSchemaName } from "metabase-lib/v1/metadata/utils/models";
@@ -66,8 +65,8 @@ function ModelCachingControl({ database }: Props) {
     ? t`Turn model persistence off`
     : t`Turn model persistence on`;
 
-  const siteUUID = MetabaseSettings.get("site-uuid") || "";
-  const cacheSchemaName = getModelCacheSchemaName(databaseId, siteUUID);
+  const siteUUID = useSetting("site-uuid");
+  const cacheSchemaName = getModelCacheSchemaName(databaseId, siteUUID || "");
 
   const handleCachingChange = async () => {
     setError(null);
diff --git a/frontend/src/metabase/admin/databases/containers/DatabaseListApp.jsx b/frontend/src/metabase/admin/databases/containers/DatabaseListApp.jsx
index 205e491596d..f7ec9761f6e 100644
--- a/frontend/src/metabase/admin/databases/containers/DatabaseListApp.jsx
+++ b/frontend/src/metabase/admin/databases/containers/DatabaseListApp.jsx
@@ -3,9 +3,9 @@ import _ from "underscore";
 
 import LoadingAndGenericErrorWrapper from "metabase/components/LoadingAndGenericErrorWrapper";
 import Database from "metabase/entities/databases";
-import MetabaseSettings from "metabase/lib/settings";
 import { isSyncInProgress } from "metabase/lib/syncing";
 import { PLUGIN_FEATURE_LEVEL_PERMISSIONS } from "metabase/plugins";
+import { getSetting } from "metabase/selectors/settings";
 import { getUserIsAdmin } from "metabase/selectors/user";
 
 import DatabaseList from "../components/DatabaseList";
@@ -41,8 +41,8 @@ const mapStateToProps = (state, props) => ({
 
   created: props.location.query.created,
   createdDbId: props.location.query.createdDbId,
-  engines: MetabaseSettings.get("engines"),
-  showSyncingModal: MetabaseSettings.get("show-database-syncing-modal"),
+  engines: getSetting(state, "engines"),
+  showSyncingModal: getSetting(state, "show-database-syncing-modal"),
 
   deletes: getDeletes(state),
   deletionError: getDeletionError(state),
diff --git a/frontend/src/metabase/admin/datamodel/containers/DataModelApp/ModelEducationalModal.jsx b/frontend/src/metabase/admin/datamodel/containers/DataModelApp/ModelEducationalModal.jsx
index 23ae4436b61..5c71fdc7996 100644
--- a/frontend/src/metabase/admin/datamodel/containers/DataModelApp/ModelEducationalModal.jsx
+++ b/frontend/src/metabase/admin/datamodel/containers/DataModelApp/ModelEducationalModal.jsx
@@ -6,7 +6,7 @@ import Modal from "metabase/components/Modal";
 import ModalContent from "metabase/components/ModalContent";
 import ButtonsS from "metabase/css/components/buttons.module.css";
 import CS from "metabase/css/core/index.css";
-import MetabaseSettings from "metabase/lib/settings";
+import { getLearnUrl } from "metabase/selectors/settings";
 
 import {
   ButtonLink,
@@ -20,7 +20,7 @@ ModelEducationalModal.propTypes = {
   onClose: PropTypes.func.isRequired,
 };
 
-const EDUCATION_URL = MetabaseSettings.learnUrl("getting-started/models");
+const EDUCATION_URL = getLearnUrl("getting-started/models");
 
 export function ModelEducationalModal({ isOpen, onClose }) {
   return (
diff --git a/frontend/src/metabase/admin/people/containers/UserSuccessModal.jsx b/frontend/src/metabase/admin/people/containers/UserSuccessModal.jsx
index 60d2ccf901c..250a3d0b1c5 100644
--- a/frontend/src/metabase/admin/people/containers/UserSuccessModal.jsx
+++ b/frontend/src/metabase/admin/people/containers/UserSuccessModal.jsx
@@ -12,7 +12,7 @@ import Button from "metabase/core/components/Button";
 import Link from "metabase/core/components/Link";
 import CS from "metabase/css/core/index.css";
 import Users from "metabase/entities/users";
-import MetabaseSettings from "metabase/lib/settings";
+import { getSetting, isSsoEnabled } from "metabase/selectors/settings";
 
 import { clearTemporaryPassword } from "../people";
 import { getUserTemporaryPassword } from "../selectors";
@@ -25,10 +25,14 @@ class UserSuccessModal extends Component {
   }
 
   render() {
-    const { onClose, user, temporaryPassword } = this.props;
-    const isSsoEnabled =
-      MetabaseSettings.isSsoEnabled() &&
-      !MetabaseSettings.isPasswordLoginEnabled();
+    const {
+      onClose,
+      user,
+      temporaryPassword,
+      isSsoEnabled,
+      isPasswordLoginEnabled,
+    } = this.props;
+    const ssoEnabled = isSsoEnabled && !isPasswordLoginEnabled;
     return (
       <ModalContent
         title={t`${user.common_name} has been added`}
@@ -38,7 +42,7 @@ class UserSuccessModal extends Component {
         {temporaryPassword ? (
           <PasswordSuccess user={user} temporaryPassword={temporaryPassword} />
         ) : (
-          <EmailSuccess isSsoEnabled={isSsoEnabled} user={user} />
+          <EmailSuccess isSsoEnabled={ssoEnabled} user={user} />
         )}
       </ModalContent>
     );
@@ -86,7 +90,6 @@ const PasswordSuccess = ({ user, temporaryPassword }) => (
     </div>
   </div>
 );
-
 export default _.compose(
   Users.load({
     id: (state, props) => props.params.userId,
@@ -96,6 +99,8 @@ export default _.compose(
       temporaryPassword: getUserTemporaryPassword(state, {
         userId: props.params.userId,
       }),
+      isSsoEnabled: isSsoEnabled(state),
+      isPasswordLoginEnabled: getSetting(state, "enable-password-login"),
     }),
     {
       onClose: () => push("/admin/people"),
diff --git a/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.jsx b/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.jsx
index 0efc2f43f7a..e7bc0ab453e 100644
--- a/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.jsx
+++ b/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.jsx
@@ -2,8 +2,8 @@ import cx from "classnames";
 import PropTypes from "prop-types";
 
 import { UpsellHostingUpdates } from "metabase/admin/upsells";
+import { useSetting } from "metabase/common/hooks";
 import CS from "metabase/css/core/index.css";
-import MetabaseSettings from "metabase/lib/settings";
 import { Flex } from "metabase/ui";
 
 import { SettingsSetting } from "../SettingsSetting";
@@ -18,16 +18,17 @@ export default function SettingsUpdatesForm({ elements, updateSetting }) {
       autoFocus={index === 0}
     />
   ));
+  const isHosted = useSetting("is-hosted");
 
   return (
     <Flex justify="space-between">
       <div style={{ width: "585px" }}>
-        {!MetabaseSettings.isHosted() && <ul>{settings}</ul>}
+        {!isHosted && <ul>{settings}</ul>}
 
         <div className={CS.px2}>
           <div
             className={cx(CS.pt3, {
-              [CS.borderTop]: !MetabaseSettings.isHosted(),
+              [CS.borderTop]: !isHosted,
             })}
           >
             <VersionUpdateNotice />
diff --git a/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.unit.spec.js b/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.unit.spec.js
index 922a53f3742..48234a37ec6 100644
--- a/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.unit.spec.js
+++ b/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.unit.spec.js
@@ -51,17 +51,17 @@ function setup({
 }
 
 describe("SettingsUpdatesForm", () => {
-  it("shows custom message for Cloud installations", () => {
+  it("shows custom message for Cloud installations", async () => {
     setup({ isHosted: true });
     expect(
-      screen.getByText(/Metabase Cloud keeps your instance up-to-date/),
+      await screen.findByText(/Metabase Cloud keeps your instance up-to-date/),
     ).toBeInTheDocument();
   });
 
-  it("shows correct message when latest version is installed", () => {
+  it("shows correct message when latest version is installed", async () => {
     setup({ currentVersion: "v1.0.0", latestVersion: "v1.0.0" });
     expect(
-      screen.getByText(/You're running Metabase 1.0.0/),
+      await screen.findByText(/You're running Metabase 1.0.0/),
     ).toBeInTheDocument();
   });
 
diff --git a/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/VersionUpdateNotice/VersionUpdateNotice.jsx b/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/VersionUpdateNotice/VersionUpdateNotice.jsx
index 843aa6d61e1..4d8b087433d 100644
--- a/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/VersionUpdateNotice/VersionUpdateNotice.jsx
+++ b/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/VersionUpdateNotice/VersionUpdateNotice.jsx
@@ -2,10 +2,17 @@ import cx from "classnames";
 import PropTypes from "prop-types";
 import { t } from "ttag";
 
+import {
+  getCurrentVersion,
+  getLatestVersion,
+} from "metabase/admin/settings/selectors";
 import ExternalLink from "metabase/core/components/ExternalLink";
 import ButtonsS from "metabase/css/components/buttons.module.css";
 import CS from "metabase/css/core/index.css";
+import { useSelector } from "metabase/lib/redux";
 import MetabaseSettings from "metabase/lib/settings";
+import { newVersionAvailable, versionIsLatest } from "metabase/lib/utils";
+import { getIsHosted } from "metabase/setup/selectors";
 
 import {
   NewVersionContainer,
@@ -13,18 +20,22 @@ import {
 } from "./VersionUpdateNotice.styled";
 
 export default function VersionUpdateNotice() {
-  const currentVersion = formatVersion(MetabaseSettings.currentVersion());
+  const currentVersion = useSelector(getCurrentVersion);
+  const latestVersion = useSelector(getLatestVersion);
+  const isHosted = useSelector(getIsHosted);
 
-  if (MetabaseSettings.isHosted()) {
+  if (isHosted) {
     return <CloudCustomers currentVersion={currentVersion} />;
   }
 
-  if (MetabaseSettings.versionIsLatest()) {
-    return <OnLatestVersion currentVersion={currentVersion} />;
+  const displayVersion = formatVersion(currentVersion);
+
+  if (versionIsLatest({ currentVersion, latestVersion })) {
+    return <OnLatestVersion currentVersion={displayVersion} />;
   }
 
-  if (MetabaseSettings.newVersionAvailable()) {
-    return <NewVersionAvailable currentVersion={currentVersion} />;
+  if (newVersionAvailable({ currentVersion, latestVersion })) {
+    return <NewVersionAvailable currentVersion={displayVersion} />;
   }
 
   return <div>{t`No successful checks yet.`}</div>;
diff --git a/frontend/src/metabase/admin/settings/selectors.js b/frontend/src/metabase/admin/settings/selectors.js
index f0ad8ed62e5..ef158dbfd62 100644
--- a/frontend/src/metabase/admin/settings/selectors.js
+++ b/frontend/src/metabase/admin/settings/selectors.js
@@ -6,6 +6,7 @@ import { SMTPConnectionForm } from "metabase/admin/settings/components/Email/SMT
 import Breadcrumbs from "metabase/components/Breadcrumbs";
 import { DashboardSelector } from "metabase/components/DashboardSelector";
 import MetabaseSettings from "metabase/lib/settings";
+import { newVersionAvailable } from "metabase/lib/utils";
 import {
   PLUGIN_ADMIN_SETTINGS_AUTH_TABS,
   PLUGIN_ADMIN_SETTINGS_UPDATES,
@@ -620,9 +621,30 @@ export const getSettingValues = createSelector(getSettings, settings => {
   return settingValues;
 });
 
-export const getNewVersionAvailable = createSelector(getSettings, settings => {
-  return MetabaseSettings.newVersionAvailable(settings);
-});
+export const getCurrentVersion = createSelector(
+  getDerivedSettingValues,
+  settings => {
+    return settings.version?.tag;
+  },
+);
+
+export const getLatestVersion = createSelector(
+  getDerivedSettingValues,
+  settings => {
+    return settings["version-info"]?.latest?.version;
+  },
+);
+
+export const getNewVersionAvailable = createSelector(
+  getCurrentVersion,
+  getLatestVersion,
+  (currentVersion, latestVersion) => {
+    return newVersionAvailable({
+      currentVersion,
+      latestVersion,
+    });
+  },
+);
 
 export const getSections = createSelector(
   getSettings,
diff --git a/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx b/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx
index ddf7fba8e71..0d93824c845 100644
--- a/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx
+++ b/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx
@@ -2,7 +2,7 @@ import { t } from "ttag";
 
 import { useUniqueId } from "metabase/hooks/use-unique-id";
 import { useSelector } from "metabase/lib/redux";
-import MetabaseSettings from "metabase/lib/settings";
+import { getLearnUrl } from "metabase/selectors/settings";
 import {
   getApplicationName,
   getShowMetabaseLinks,
@@ -20,7 +20,7 @@ export const HomeHelpCard = (): JSX.Element | null => {
   }
 
   return (
-    <CardRoot href={MetabaseSettings.learnUrl()} aria-labelledby={cardTitleId}>
+    <CardRoot href={getLearnUrl()} aria-labelledby={cardTitleId}>
       <CardIcon name="reference" />
       <CardTitle id={cardTitleId}>{t`${applicationName} tips`}</CardTitle>
     </CardRoot>
diff --git a/frontend/src/metabase/lib/settings.ts b/frontend/src/metabase/lib/settings.ts
index 29a59e405a8..a32c1628b42 100644
--- a/frontend/src/metabase/lib/settings.ts
+++ b/frontend/src/metabase/lib/settings.ts
@@ -3,7 +3,7 @@ import { msgid, ngettext, t } from "ttag";
 import _ from "underscore";
 
 import { parseTimestamp } from "metabase/lib/time";
-import { compareVersions, numberToWord } from "metabase/lib/utils";
+import { numberToWord } from "metabase/lib/utils";
 import type {
   PasswordComplexity,
   SettingKey,
@@ -145,13 +145,6 @@ class MetabaseSettings {
     return this.get("cloud-gateway-ips") || [];
   }
 
-  /**
-   * @deprecated use getSetting(state, "has-user-setup")
-   */
-  hasUserSetup() {
-    return this.get("has-user-setup");
-  }
-
   /**
    * @deprecated use getSetting(state, "hide-embed-branding?")
    */
@@ -187,17 +180,6 @@ class MetabaseSettings {
     return this.get("other-sso-enabled?");
   }
 
-  /**
-   * @deprecated use getSetting(state, ...)
-   */
-  isSsoEnabled() {
-    return (
-      this.isLdapEnabled() ||
-      this.isGoogleAuthEnabled() ||
-      this.isOtherSsoEnabled()
-    );
-  }
-
   /**
    * @deprecated use getSetting(state, "enable-password-login")
    */
@@ -273,35 +255,6 @@ class MetabaseSettings {
     }
   }
 
-  /**
-   * @deprecated use getLearnUrl
-   */
-  learnUrl(path = "") {
-    // eslint-disable-next-line no-unconditional-metabase-links-render -- This is the implementation of MetabaseSettings.learnUrl()
-    return `https://www.metabase.com/learn/${path}`;
-  }
-
-  /**
-   * @deprecated use getStoreUrl
-   */
-  storeUrl(path = "") {
-    return `https://store.metabase.com/${path}`;
-  }
-
-  migrateToCloudGuideUrl() {
-    return "https://www.metabase.com/cloud/docs/migrate/guide";
-  }
-
-  newVersionAvailable() {
-    const result = compareVersions(this.currentVersion(), this.latestVersion());
-    return result != null && result < 0;
-  }
-
-  versionIsLatest() {
-    const result = compareVersions(this.currentVersion(), this.latestVersion());
-    return result != null && result >= 0;
-  }
-
   /**
    * @deprecated use getSetting(state, "version-info")
    */
diff --git a/frontend/src/metabase/lib/utils.ts b/frontend/src/metabase/lib/utils.ts
index 4da633ea070..e7434b2cc97 100644
--- a/frontend/src/metabase/lib/utils.ts
+++ b/frontend/src/metabase/lib/utils.ts
@@ -138,4 +138,26 @@ export function compareVersions(
   return 0;
 }
 
+export function newVersionAvailable({
+  currentVersion,
+  latestVersion,
+}: {
+  currentVersion: string;
+  latestVersion: string;
+}) {
+  const result = compareVersions(currentVersion, latestVersion);
+  return result != null && result < 0;
+}
+
+export function versionIsLatest({
+  currentVersion,
+  latestVersion,
+}: {
+  currentVersion: string;
+  latestVersion: string;
+}) {
+  const result = compareVersions(currentVersion, latestVersion);
+  return result != null && result >= 0;
+}
+
 export const isEEBuild = () => PLUGIN_IS_EE_BUILD.isEEBuild();
diff --git a/frontend/src/metabase/models/containers/NewModelOptions/NewModelOptions.tsx b/frontend/src/metabase/models/containers/NewModelOptions/NewModelOptions.tsx
index bf9b6cdb5e0..14b1db02c66 100644
--- a/frontend/src/metabase/models/containers/NewModelOptions/NewModelOptions.tsx
+++ b/frontend/src/metabase/models/containers/NewModelOptions/NewModelOptions.tsx
@@ -9,12 +9,11 @@ import { Grid } from "metabase/components/Grid";
 import CS from "metabase/css/core/index.css";
 import Databases from "metabase/entities/databases";
 import { useSelector } from "metabase/lib/redux";
-import MetabaseSettings from "metabase/lib/settings";
 import * as Urls from "metabase/lib/urls";
 import NewModelOption from "metabase/models/components/NewModelOption";
 import { NoDatabasesEmptyState } from "metabase/reference/databases/NoDatabasesEmptyState";
 import { getHasDataAccess, getHasNativeWrite } from "metabase/selectors/data";
-import { getSetting } from "metabase/selectors/settings";
+import { getLearnUrl, getSetting } from "metabase/selectors/settings";
 import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
 
 import {
@@ -23,7 +22,7 @@ import {
   OptionsRoot,
 } from "./NewModelOptions.styled";
 
-const EDUCATIONAL_LINK = MetabaseSettings.learnUrl("data-modeling/models");
+const EDUCATIONAL_LINK = getLearnUrl("data-modeling/models");
 
 interface NewModelOptionsProps {
   location: Location;
diff --git a/frontend/src/metabase/nav/components/PaymentBanner/PaymentBanner.tsx b/frontend/src/metabase/nav/components/PaymentBanner/PaymentBanner.tsx
index 13bf516c5a5..69dd09c2d2f 100644
--- a/frontend/src/metabase/nav/components/PaymentBanner/PaymentBanner.tsx
+++ b/frontend/src/metabase/nav/components/PaymentBanner/PaymentBanner.tsx
@@ -3,7 +3,7 @@ import { jt, t } from "ttag";
 import Banner from "metabase/components/Banner";
 import ExternalLink from "metabase/core/components/ExternalLink";
 import CS from "metabase/css/core/index.css";
-import MetabaseSettings from "metabase/lib/settings";
+import { getStoreUrl } from "metabase/selectors/settings";
 import type { TokenStatus } from "metabase-types/api";
 
 interface PaymentBannerProps {
@@ -19,7 +19,7 @@ export const PaymentBanner = ({ isAdmin, tokenStatus }: PaymentBannerProps) => {
           <ExternalLink
             key="payment-past-due"
             className={CS.link}
-            href={MetabaseSettings.storeUrl()}
+            href={getStoreUrl()}
           >
             {t`review your payment settings`}
           </ExternalLink>
@@ -33,7 +33,7 @@ export const PaymentBanner = ({ isAdmin, tokenStatus }: PaymentBannerProps) => {
           <ExternalLink
             key="payment-unpaid"
             className={CS.link}
-            href={MetabaseSettings.storeUrl()}
+            href={getStoreUrl()}
           >
             {t`Review your payment settings`}
           </ExternalLink>
diff --git a/frontend/src/metabase/pulse/components/RecipientPicker.tsx b/frontend/src/metabase/pulse/components/RecipientPicker.tsx
index d488ad5c8ae..24ff861deee 100644
--- a/frontend/src/metabase/pulse/components/RecipientPicker.tsx
+++ b/frontend/src/metabase/pulse/components/RecipientPicker.tsx
@@ -2,12 +2,12 @@ import cx from "classnames";
 import { t } from "ttag";
 import _ from "underscore";
 
+import { useSetting } from "metabase/common/hooks";
 import TokenField from "metabase/components/TokenField";
 import UserAvatar from "metabase/components/UserAvatar";
 import CS from "metabase/css/core/index.css";
 import { isEmail } from "metabase/lib/email";
 import { recipientIsValid } from "metabase/lib/pulse";
-import MetabaseSettings from "metabase/lib/settings";
 import { Text } from "metabase/ui";
 import type { User } from "metabase-types/api";
 
@@ -33,7 +33,7 @@ export const RecipientPicker = ({
   };
 
   const isValid = recipients.every(r => recipientIsValid(r));
-  const domains = MetabaseSettings.subscriptionAllowedDomains().join(", ");
+  const domains = useSetting("subscription-allowed-domains");
 
   return (
     <div>
@@ -68,7 +68,9 @@ export const RecipientPicker = ({
           updateOnInputBlur
         />
       </div>
-      {!isValid && <ErrorMessage>{invalidRecipientText(domains)}</ErrorMessage>}
+      {domains && !isValid && (
+        <ErrorMessage>{invalidRecipientText(domains)}</ErrorMessage>
+      )}
     </div>
   );
 };
diff --git a/frontend/src/metabase/pulse/components/RecipientPicker.unit.spec.tsx b/frontend/src/metabase/pulse/components/RecipientPicker.unit.spec.tsx
index b7afd84971d..c3673f3798e 100644
--- a/frontend/src/metabase/pulse/components/RecipientPicker.unit.spec.tsx
+++ b/frontend/src/metabase/pulse/components/RecipientPicker.unit.spec.tsx
@@ -1,6 +1,6 @@
-import { render, screen } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
 
+import { renderWithProviders, screen } from "__support__/ui";
 import { RecipientPicker } from "metabase/pulse/components/RecipientPicker";
 import { createMockUser } from "metabase-types/api/mocks";
 
@@ -18,7 +18,7 @@ const TEST_USERS = [
 describe("recipient picker", () => {
   describe("focus", () => {
     it("should be focused if there are no recipients", async () => {
-      render(
+      renderWithProviders(
         <RecipientPicker
           recipients={[]}
           users={TEST_USERS}
@@ -31,7 +31,7 @@ describe("recipient picker", () => {
       expect(await screen.findByText("Dustin")).toBeInTheDocument();
     });
     it("should not be focused if there are existing recipients", () => {
-      render(
+      renderWithProviders(
         <RecipientPicker
           recipients={[TEST_USERS[0]]}
           users={TEST_USERS}
@@ -49,7 +49,7 @@ describe("recipient picker", () => {
     it("should track additions", async () => {
       const onRecipientsChange = jest.fn();
 
-      render(
+      renderWithProviders(
         <RecipientPicker
           recipients={[TEST_USERS[0]]}
           users={TEST_USERS}
@@ -70,7 +70,7 @@ describe("recipient picker", () => {
     it("should track removals", async () => {
       const onRecipientsChange = jest.fn();
 
-      render(
+      renderWithProviders(
         <RecipientPicker
           recipients={[TEST_USERS[0], TEST_USERS[1], TEST_USERS[2]]}
           users={TEST_USERS}
@@ -92,7 +92,7 @@ describe("recipient picker", () => {
     it("should support adding emails", async () => {
       const onRecipientsChange = jest.fn();
 
-      render(
+      renderWithProviders(
         <RecipientPicker
           recipients={[TEST_USERS[0]]}
           users={TEST_USERS}
diff --git a/frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.tsx b/frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.tsx
index 814fa52b36a..a1afdc4e552 100644
--- a/frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.tsx
+++ b/frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.tsx
@@ -8,7 +8,7 @@ import CS from "metabase/css/core/index.css";
 import QueryBuilderS from "metabase/css/query_builder.module.css";
 import { getEngineNativeType } from "metabase/lib/engine";
 import { useSelector } from "metabase/lib/redux";
-import MetabaseSettings from "metabase/lib/settings";
+import { getLearnUrl } from "metabase/selectors/settings";
 import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
 import * as Lib from "metabase-lib";
 import type Question from "metabase-lib/v1/Question";
@@ -115,9 +115,7 @@ export function VisualizationError({
           </QueryErrorHeader>
           <QueryErrorMessage>{processedError}</QueryErrorMessage>
           {isSql && showMetabaseLinks && (
-            <QueryErrorLink
-              href={MetabaseSettings.learnUrl("debugging-sql/sql-syntax")}
-            >
+            <QueryErrorLink href={getLearnUrl("debugging-sql/sql-syntax")}>
               {t`Learn how to debug SQL errors`}
             </QueryErrorLink>
           )}
diff --git a/frontend/src/metabase/routes.jsx b/frontend/src/metabase/routes.jsx
index 8955d21d482..383d6822af8 100644
--- a/frontend/src/metabase/routes.jsx
+++ b/frontend/src/metabase/routes.jsx
@@ -26,7 +26,6 @@ import { ModalRoute } from "metabase/hoc/ModalRoute";
 import { Route } from "metabase/hoc/Title";
 import { HomePage } from "metabase/home/components/HomePage";
 import { trackPageView } from "metabase/lib/analytics";
-import MetabaseSettings from "metabase/lib/settings";
 import DatabaseMetabotApp from "metabase/metabot/containers/DatabaseMetabotApp";
 import ModelMetabotApp from "metabase/metabot/containers/ModelMetabotApp";
 import NewModelOptions from "metabase/models/containers/NewModelOptions";
@@ -63,10 +62,13 @@ import {
   IsAuthenticated,
   IsNotAuthenticated,
 } from "./route-guards";
+import { getSetting } from "./selectors/settings";
 import { getApplicationName } from "./selectors/whitelabel";
 
 export const getRoutes = store => {
   const applicationName = getApplicationName(store.getState());
+  const hasUserSetup = getSetting(store.getState(), "has-user-setup");
+
   return (
     <Route title={applicationName} component={App}>
       {/* SETUP */}
@@ -74,7 +76,7 @@ export const getRoutes = store => {
         path="/setup"
         component={Setup}
         onEnter={(nextState, replace) => {
-          if (MetabaseSettings.hasUserSetup()) {
+          if (hasUserSetup) {
             replace("/");
           }
           trackPageView(location.pathname);
diff --git a/frontend/src/metabase/selectors/settings.ts b/frontend/src/metabase/selectors/settings.ts
index 296f8e6156b..e64454b1f9a 100644
--- a/frontend/src/metabase/selectors/settings.ts
+++ b/frontend/src/metabase/selectors/settings.ts
@@ -27,10 +27,19 @@ export const getSetting = <S extends State, T extends GetSettingKey<S>>(
   return setting;
 };
 
+export const isSsoEnabled = (state: State) =>
+  getSetting(state, "ldap-enabled") ||
+  getSetting(state, "google-auth-enabled") ||
+  getSetting(state, "saml-enabled") ||
+  getSetting(state, "other-sso-enabled?");
+
 export const getStoreUrl = (path = "") => {
   return `https://store.metabase.com/${path}`;
 };
 
+export const migrateToCloudGuideUrl = () =>
+  "https://www.metabase.com/cloud/docs/migrate/guide";
+
 export const getLearnUrl = (path = "") => {
   // eslint-disable-next-line no-unconditional-metabase-links-render -- This is the implementation of getLearnUrl()
   return `https://www.metabase.com/learn/${path}`;
diff --git a/frontend/src/metabase/setup/components/CloudMigrationHelp/CloudMigrationHelp.tsx b/frontend/src/metabase/setup/components/CloudMigrationHelp/CloudMigrationHelp.tsx
index c3059d465c6..d7cc366f6ab 100644
--- a/frontend/src/metabase/setup/components/CloudMigrationHelp/CloudMigrationHelp.tsx
+++ b/frontend/src/metabase/setup/components/CloudMigrationHelp/CloudMigrationHelp.tsx
@@ -2,7 +2,7 @@ import { t } from "ttag";
 
 import HelpCard from "metabase/components/HelpCard";
 import { useSelector } from "metabase/lib/redux";
-import MetabaseSettings from "metabase/lib/settings";
+import { migrateToCloudGuideUrl } from "metabase/selectors/settings";
 
 import { getIsHosted } from "../../selectors";
 import { useStep } from "../../useStep";
@@ -18,7 +18,7 @@ export const CloudMigrationHelp = () => {
     <SetupCardContainer isVisible={isVisible}>
       <HelpCard
         title={t`Migrating from self-hosted?`}
-        helpUrl={MetabaseSettings.migrateToCloudGuideUrl()}
+        helpUrl={migrateToCloudGuideUrl()}
       >{t`Check out our docs for how to migrate your self-hosted instance to Cloud.`}</HelpCard>
     </SetupCardContainer>
   );
-- 
GitLab