diff --git a/frontend/src/metabase/dashboard/containers/DashboardApp.jsx b/frontend/src/metabase/dashboard/containers/DashboardApp.jsx
index 7a5b93546b93f12160735a00016af891aa2d3c64..188e6cd26572674b8ed0e5f8117b495fb1471b48 100644
--- a/frontend/src/metabase/dashboard/containers/DashboardApp.jsx
+++ b/frontend/src/metabase/dashboard/containers/DashboardApp.jsx
@@ -40,6 +40,7 @@ import {
   getFavicon,
   getDocumentTitle,
   getIsRunning,
+  getIsLoadingComplete,
 } from "../selectors";
 import { getDatabases, getMetadata } from "metabase/selectors/metadata";
 import {
@@ -81,6 +82,7 @@ const mapStateToProps = (state, props) => {
     pageFavicon: getFavicon(state),
     documentTitle: getDocumentTitle(state),
     isRunning: getIsRunning(state),
+    isLoadingComplete: getIsLoadingComplete(state),
   };
 };
 
@@ -96,16 +98,17 @@ const mapDispatchToProps = {
 const DashboardApp = props => {
   const options = parseHashOptions(window.location.hash);
 
-  const { isRunning, dashboard } = props;
+  const { isRunning, isLoadingComplete, dashboard } = props;
 
   const [editingOnLoad] = useState(options.edit);
   const [addCardOnLoad] = useState(options.add && parseInt(options.add));
 
-  const [shouldSendNotification, setShouldSendNotification] = useState(false);
   const [isShowingToaster, setIsShowingToaster] = useState(false);
 
   const onTimeout = useCallback(() => {
-    setIsShowingToaster(true);
+    if (Notification.permission === "default") {
+      setIsShowingToaster(true);
+    }
   }, []);
 
   useLoadingTimer(isRunning, {
@@ -118,26 +121,20 @@ const DashboardApp = props => {
   useOnUnmount(props.reset);
 
   useEffect(() => {
-    if (!isRunning) {
+    if (isLoadingComplete) {
       setIsShowingToaster(false);
-    }
-    if (!isRunning && shouldSendNotification) {
-      if (document.hidden) {
+      if (Notification.permission === "granted" && document.hidden) {
         showNotification(
-          t`All Set! ${dashboard.name} is ready.`,
+          t`All Set! ${dashboard?.name} is ready.`,
           t`All questions loaded`,
         );
       }
-      setShouldSendNotification(false);
     }
-  }, [isRunning, shouldSendNotification, showNotification, dashboard?.name]);
+  }, [isLoadingComplete, showNotification, dashboard?.name]);
 
   const onConfirmToast = useCallback(async () => {
-    const result = await requestPermission();
-    if (result === "granted") {
-      setIsShowingToaster(false);
-      setShouldSendNotification(true);
-    }
+    await requestPermission();
+    setIsShowingToaster(false);
   }, [requestPermission]);
 
   const onDismissToast = useCallback(() => {
diff --git a/frontend/src/metabase/dashboard/reducers.js b/frontend/src/metabase/dashboard/reducers.js
index cfbad20b7719738aed669dee3d5256e1f712a601..9996bc8f8e4569c166993a4ced4ca3f8bfdeeae1 100644
--- a/frontend/src/metabase/dashboard/reducers.js
+++ b/frontend/src/metabase/dashboard/reducers.js
@@ -298,7 +298,7 @@ const loadingDashCards = handleActions(
     [INITIALIZE]: {
       next: state => ({
         ...state,
-        isLoadingComplete: false,
+        loadingStatus: "idle",
       }),
     },
     [FETCH_DASHBOARD]: {
@@ -307,13 +307,14 @@ const loadingDashCards = handleActions(
         dashcardIds: Object.values(payload.entities.dashcard || {})
           .filter(dc => !isVirtualDashCard(dc))
           .map(dc => dc.id),
-        isLoadingComplete: false,
+        loadingStatus: "idle",
       }),
     },
     [FETCH_DASHBOARD_CARD_DATA]: {
       next: state => ({
         ...state,
         loadingIds: state.dashcardIds,
+        loadingStatus: "running",
         startTime:
           state.dashcardIds.length > 0 &&
           // check that performance is defined just in case
@@ -329,7 +330,7 @@ const loadingDashCards = handleActions(
           ...state,
           loadingIds,
           ...(loadingIds.length === 0
-            ? { startTime: null, isLoadingComplete: true }
+            ? { startTime: null, loadingStatus: "complete" }
             : {}),
         };
       },
@@ -340,16 +341,14 @@ const loadingDashCards = handleActions(
         return {
           ...state,
           loadingIds,
-          ...(loadingIds.length === 0
-            ? { startTime: null, isLoadingComplete: true }
-            : {}),
+          ...(loadingIds.length === 0 ? { startTime: null } : {}),
         };
       },
     },
     [RESET]: {
       next: state => ({
         ...state,
-        isLoadingComplete: false,
+        loadingStatus: "idle",
       }),
     },
   },
@@ -357,7 +356,7 @@ const loadingDashCards = handleActions(
     dashcardIds: [],
     loadingIds: [],
     startTime: null,
-    isLoadingComplete: false,
+    loadingStatus: "idle",
   },
 );
 
diff --git a/frontend/src/metabase/dashboard/reducers.unit.spec.js b/frontend/src/metabase/dashboard/reducers.unit.spec.js
index 110b630a60b074df4f40d9e3840acce4b12ff116..d749012b82e671bdc4a77656e9b4c0ece32d0543 100644
--- a/frontend/src/metabase/dashboard/reducers.unit.spec.js
+++ b/frontend/src/metabase/dashboard/reducers.unit.spec.js
@@ -26,7 +26,7 @@ describe("dashboard reducers", () => {
         dashcardIds: [],
         loadingIds: [],
         startTime: null,
-        isLoadingComplete: false,
+        loadingStatus: "idle",
       },
       parameterValues: {},
       parameterValuesSearchCache: {},
diff --git a/frontend/src/metabase/dashboard/selectors.js b/frontend/src/metabase/dashboard/selectors.js
index 158ea6a0af58aff3473bfc349334ba905f6c3ac5..dd9c3cbe4ed6880a5edf4ca3e3c28415ce2c32ad 100644
--- a/frontend/src/metabase/dashboard/selectors.js
+++ b/frontend/src/metabase/dashboard/selectors.js
@@ -34,7 +34,9 @@ export const getFavicon = state =>
     : null;
 
 export const getIsRunning = state =>
-  state.dashboard.loadingDashCards.loadingIds > 0;
+  state.dashboard.loadingDashCards.loadingStatus === "running";
+export const getIsLoadingComplete = state =>
+  state.dashboard.loadingDashCards.loadingStatus === "complete";
 
 export const getLoadingStartTime = state =>
   state.dashboard.loadingDashCards.startTime;
diff --git a/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx b/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx
index 3f82b6f2bba3fc63e21b3b3dcc0ec6d3d57c3164..2488c3eeff6d677d27b870e7f39393096e02d6fa 100644
--- a/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx
+++ b/frontend/src/metabase/query_builder/containers/QueryBuilder.jsx
@@ -86,6 +86,7 @@ import {
   getDocumentTitle,
   getPageFavicon,
   getIsTimeseries,
+  getIsLoadingComplete,
 } from "../selectors";
 import * as actions from "../actions";
 
@@ -186,6 +187,7 @@ const mapStateToProps = (state, props) => {
     snippetCollectionId: getSnippetCollectionId(state),
     documentTitle: getDocumentTitle(state),
     pageFavicon: getPageFavicon(state),
+    isLoadingComplete: getIsLoadingComplete(state),
   };
 };
 
@@ -222,6 +224,7 @@ function QueryBuilder(props) {
     allLoaded,
     showTimelinesForCollection,
     card,
+    isLoadingComplete,
   } = props;
 
   const forceUpdate = useForceUpdate();
@@ -365,13 +368,14 @@ function QueryBuilder(props) {
     }
   });
 
-  const { isRunning } = uiControls;
-
-  const [shouldSendNotification, setShouldSendNotification] = useState(false);
   const [isShowingToaster, setIsShowingToaster] = useState(false);
 
+  const { isRunning } = uiControls;
+
   const onTimeout = useCallback(() => {
-    setIsShowingToaster(true);
+    if (Notification.permission === "default") {
+      setIsShowingToaster(true);
+    }
   }, []);
 
   useLoadingTimer(isRunning, {
@@ -382,26 +386,21 @@ function QueryBuilder(props) {
   const [requestPermission, showNotification] = useWebNotification();
 
   useEffect(() => {
-    if (!isRunning) {
+    if (isLoadingComplete) {
       setIsShowingToaster(false);
-    }
-    if (!isRunning && shouldSendNotification) {
-      if (document.hidden) {
+
+      if (Notification.permission === "granted" && document.hidden) {
         showNotification(
           t`All Set! Your question is ready.`,
           t`${card.name} is loaded.`,
         );
       }
-      setShouldSendNotification(false);
     }
-  }, [isRunning, shouldSendNotification, showNotification, card?.name]);
+  }, [isLoadingComplete, showNotification, card?.name]);
 
   const onConfirmToast = useCallback(async () => {
-    const result = await requestPermission();
-    if (result === "granted") {
-      setIsShowingToaster(false);
-      setShouldSendNotification(true);
-    }
+    await requestPermission();
+    setIsShowingToaster(false);
   }, [requestPermission]);
 
   const onDismissToast = useCallback(() => {
diff --git a/frontend/src/metabase/query_builder/reducers.js b/frontend/src/metabase/query_builder/reducers.js
index 77f9deef0dc128c4e8725d657f10ad7b745d7fab..c11e3b8a878f1347e4fb695e5af6425745fb549c 100644
--- a/frontend/src/metabase/query_builder/reducers.js
+++ b/frontend/src/metabase/query_builder/reducers.js
@@ -72,6 +72,7 @@ const DEFAULT_UI_CONTROLS = {
   isShowingNewbModal: false,
   isEditing: false,
   isRunning: false,
+  isQueryComplete: false,
   isShowingSummarySidebar: false,
   isShowingFilterSidebar: false,
   isShowingChartTypeSidebar: false,
@@ -93,6 +94,8 @@ const DEFAULT_LOADING_CONTROLS = {
   timeoutId: "",
 };
 
+const DEFAULT_QUERY_STATUS = "idle";
+
 const UI_CONTROLS_SIDEBAR_DEFAULTS = {
   isShowingSummarySidebar: false,
   isShowingFilterSidebar: false,
@@ -196,12 +199,18 @@ export const uiControls = handleActions(
       next: (state, { payload }) => ({ ...state, isEditing: false }),
     },
 
-    [RUN_QUERY]: state => ({ ...state, isRunning: true }),
+    [RUN_QUERY]: state => ({
+      ...state,
+      isRunning: true,
+    }),
     [CANCEL_QUERY]: {
       next: (state, { payload }) => ({ ...state, isRunning: false }),
     },
     [QUERY_COMPLETED]: {
-      next: (state, { payload }) => ({ ...state, isRunning: false }),
+      next: (state, { payload }) => ({
+        ...state,
+        isRunning: false,
+      }),
     },
     [QUERY_ERRORED]: {
       next: (state, { payload }) => ({ ...state, isRunning: false }),
@@ -324,6 +333,15 @@ export const loadingControls = handleActions(
   DEFAULT_LOADING_CONTROLS,
 );
 
+export const queryStatus = handleActions(
+  {
+    [RUN_QUERY]: state => "running",
+    [QUERY_COMPLETED]: state => "complete",
+    [CANCEL_QUERY]: state => "idle",
+  },
+  DEFAULT_QUERY_STATUS,
+);
+
 export const zoomedRowObjectId = handleActions(
   {
     [INITIALIZE_QB]: {
diff --git a/frontend/src/metabase/query_builder/selectors.js b/frontend/src/metabase/query_builder/selectors.js
index 56ff851d5db176e39a977279f5332583a4fed73b..6c79ae5dc92873b7a1b71263037dc1d53575b61d 100644
--- a/frontend/src/metabase/query_builder/selectors.js
+++ b/frontend/src/metabase/query_builder/selectors.js
@@ -38,6 +38,7 @@ import ObjectMode from "metabase/modes/components/modes/ObjectMode";
 import { LOAD_COMPLETE_FAVICON } from "metabase/hoc/Favicon";
 
 export const getUiControls = state => state.qb.uiControls;
+const getQueryStatus = state => state.qb.queryStatus;
 const getLoadingControls = state => state.qb.loadingControls;
 
 export const getIsShowingTemplateTagsEditor = state =>
@@ -69,6 +70,8 @@ export const getIsAnySidebarOpen = createSelector([getUiControls], uiControls =>
 
 export const getIsEditing = state => getUiControls(state).isEditing;
 export const getIsRunning = state => getUiControls(state).isRunning;
+export const getIsLoadingComplete = state =>
+  getQueryStatus(state) === "complete";
 
 export const getCard = state => state.qb.card;
 export const getOriginalCard = state => state.qb.originalCard;
diff --git a/resources/frontend_client/app/assets/img/blue_check.png b/resources/frontend_client/app/assets/img/blue_check.png
index 8939ee67880fd4bb569a89cabd013e585d89ea79..f690a5d1716490a31abbde02f23c1ec1863d3dd0 100644
Binary files a/resources/frontend_client/app/assets/img/blue_check.png and b/resources/frontend_client/app/assets/img/blue_check.png differ