diff --git a/src/app.jsx b/src/app.jsx
index 5bf428de..44ef3d82 100644
--- a/src/app.jsx
+++ b/src/app.jsx
@@ -28,178 +28,7 @@ const { VITE_CLIENT_NAME: CLIENT_NAME } = import.meta.env;
 
 window.__STATES__ = states;
 
-async function startStream() {
-  const stream = await masto.v1.stream.streamUser();
-  console.log('STREAM START', { stream });
-  stream.on('update', (status) => {
-    console.log('UPDATE', status);
-
-    const inHomeNew = states.homeNew.find((s) => s.id === status.id);
-    const inHome = states.home.find((s) => s.id === status.id);
-    if (!inHomeNew && !inHome) {
-      states.homeNew.unshift({
-        id: status.id,
-        reblog: status.reblog?.id,
-        reply: !!status.inReplyToAccountId,
-      });
-    }
-
-    states.statuses.set(status.id, status);
-    if (status.reblog) {
-      states.statuses.set(status.reblog.id, status.reblog);
-    }
-  });
-  stream.on('status.update', (status) => {
-    console.log('STATUS.UPDATE', status);
-    states.statuses.set(status.id, status);
-    if (status.reblog) {
-      states.statuses.set(status.reblog.id, status.reblog);
-    }
-  });
-  stream.on('delete', (statusID) => {
-    console.log('DELETE', statusID);
-    // states.statuses.delete(statusID);
-    const s = states.statuses.get(statusID);
-    if (s) s._deleted = true;
-  });
-  stream.on('notification', (notification) => {
-    console.log('NOTIFICATION', notification);
-
-    const inNotificationsNew = states.notificationsNew.find(
-      (n) => n.id === notification.id,
-    );
-    const inNotifications = states.notifications.find(
-      (n) => n.id === notification.id,
-    );
-    if (!inNotificationsNew && !inNotifications) {
-      states.notificationsNew.unshift(notification);
-    }
-
-    if (notification.status && !states.statuses.has(notification.status.id)) {
-      states.statuses.set(notification.status.id, notification.status);
-      if (
-        notification.status.reblog &&
-        !states.statuses.has(notification.status.reblog.id)
-      ) {
-        states.statuses.set(
-          notification.status.reblog.id,
-          notification.status.reblog,
-        );
-      }
-    }
-  });
-
-  stream.ws.onclose = () => {
-    console.log('STREAM CLOSED!');
-
-    requestAnimationFrame(() => {
-      startStream();
-    });
-  };
-
-  return {
-    stream,
-    stopStream: () => {
-      stream.ws.close();
-    },
-  };
-}
-
-function startVisibility() {
-  const handleVisibilityChange = () => {
-    if (document.visibilityState === 'hidden') {
-      const timestamp = Date.now();
-      store.session.set('lastHidden', timestamp);
-    } else {
-      const timestamp = Date.now();
-      const lastHidden = store.session.get('lastHidden');
-      const diff = timestamp - lastHidden;
-      const diffMins = Math.round(diff / 1000 / 60);
-      if (diffMins > 1) {
-        console.log('visible', { lastHidden, diffMins });
-        setTimeout(() => {
-          // Buffer for WS reconnect
-          (async () => {
-            try {
-              const firstStatusID = states.home[0]?.id;
-              const firstNotificationID = states.notifications[0]?.id;
-              const fetchHome = masto.v1.timelines.listHome({
-                limit: 1,
-                ...(firstStatusID && { sinceId: firstStatusID }),
-              });
-              const fetchNotifications = masto.v1.notifications.list({
-                limit: 1,
-                ...(firstNotificationID && { sinceId: firstNotificationID }),
-              });
-
-              const newStatuses = await fetchHome;
-              if (
-                newStatuses.length &&
-                newStatuses[0].id !== states.home[0].id
-              ) {
-                states.homeNew = newStatuses.map((status) => {
-                  states.statuses.set(status.id, status);
-                  if (status.reblog) {
-                    states.statuses.set(status.reblog.id, status.reblog);
-                  }
-                  return {
-                    id: status.id,
-                    reblog: status.reblog?.id,
-                    reply: !!status.inReplyToAccountId,
-                  };
-                });
-              }
-
-              const newNotifications = await fetchNotifications;
-              if (newNotifications.length) {
-                const notification = newNotifications[0];
-                const inNotificationsNew = states.notificationsNew.find(
-                  (n) => n.id === notification.id,
-                );
-                const inNotifications = states.notifications.find(
-                  (n) => n.id === notification.id,
-                );
-                if (!inNotificationsNew && !inNotifications) {
-                  states.notificationsNew.unshift(notification);
-                }
-
-                if (
-                  notification.status &&
-                  !states.statuses.has(notification.status.id)
-                ) {
-                  states.statuses.set(
-                    notification.status.id,
-                    notification.status,
-                  );
-                  if (
-                    notification.status.reblog &&
-                    !states.statuses.has(notification.status.reblog.id)
-                  ) {
-                    states.statuses.set(
-                      notification.status.reblog.id,
-                      notification.status.reblog,
-                    );
-                  }
-                }
-              }
-            } catch (e) {
-              // Silently fail
-              console.error(e);
-            }
-          })();
-        }, 100);
-      }
-    }
-  };
-  document.addEventListener('visibilitychange', handleVisibilityChange);
-  return {
-    stop: () => {
-      document.removeEventListener('visibilitychange', handleVisibilityChange);
-    },
-  };
-}
-
-export function App() {
+function App() {
   const snapStates = useSnapshot(states);
   const [isLoggedIn, setIsLoggedIn] = useState(false);
   const [uiState, setUIState] = useState('loading');
@@ -458,3 +287,176 @@ export function App() {
     </>
   );
 }
+
+async function startStream() {
+  const stream = await masto.v1.stream.streamUser();
+  console.log('STREAM START', { stream });
+  stream.on('update', (status) => {
+    console.log('UPDATE', status);
+
+    const inHomeNew = states.homeNew.find((s) => s.id === status.id);
+    const inHome = states.home.find((s) => s.id === status.id);
+    if (!inHomeNew && !inHome) {
+      states.homeNew.unshift({
+        id: status.id,
+        reblog: status.reblog?.id,
+        reply: !!status.inReplyToAccountId,
+      });
+    }
+
+    states.statuses.set(status.id, status);
+    if (status.reblog) {
+      states.statuses.set(status.reblog.id, status.reblog);
+    }
+  });
+  stream.on('status.update', (status) => {
+    console.log('STATUS.UPDATE', status);
+    states.statuses.set(status.id, status);
+    if (status.reblog) {
+      states.statuses.set(status.reblog.id, status.reblog);
+    }
+  });
+  stream.on('delete', (statusID) => {
+    console.log('DELETE', statusID);
+    // states.statuses.delete(statusID);
+    const s = states.statuses.get(statusID);
+    if (s) s._deleted = true;
+  });
+  stream.on('notification', (notification) => {
+    console.log('NOTIFICATION', notification);
+
+    const inNotificationsNew = states.notificationsNew.find(
+      (n) => n.id === notification.id,
+    );
+    const inNotifications = states.notifications.find(
+      (n) => n.id === notification.id,
+    );
+    if (!inNotificationsNew && !inNotifications) {
+      states.notificationsNew.unshift(notification);
+    }
+
+    if (notification.status && !states.statuses.has(notification.status.id)) {
+      states.statuses.set(notification.status.id, notification.status);
+      if (
+        notification.status.reblog &&
+        !states.statuses.has(notification.status.reblog.id)
+      ) {
+        states.statuses.set(
+          notification.status.reblog.id,
+          notification.status.reblog,
+        );
+      }
+    }
+  });
+
+  stream.ws.onclose = () => {
+    console.log('STREAM CLOSED!');
+
+    requestAnimationFrame(() => {
+      startStream();
+    });
+  };
+
+  return {
+    stream,
+    stopStream: () => {
+      stream.ws.close();
+    },
+  };
+}
+
+function startVisibility() {
+  const handleVisibilityChange = () => {
+    if (document.visibilityState === 'hidden') {
+      const timestamp = Date.now();
+      store.session.set('lastHidden', timestamp);
+    } else {
+      const timestamp = Date.now();
+      const lastHidden = store.session.get('lastHidden');
+      const diff = timestamp - lastHidden;
+      const diffMins = Math.round(diff / 1000 / 60);
+      if (diffMins > 1) {
+        console.log('visible', { lastHidden, diffMins });
+        setTimeout(() => {
+          // Buffer for WS reconnect
+          (async () => {
+            try {
+              const firstStatusID = states.home[0]?.id;
+              const firstNotificationID = states.notifications[0]?.id;
+              const fetchHome = masto.v1.timelines.listHome({
+                limit: 1,
+                ...(firstStatusID && { sinceId: firstStatusID }),
+              });
+              const fetchNotifications = masto.v1.notifications.list({
+                limit: 1,
+                ...(firstNotificationID && { sinceId: firstNotificationID }),
+              });
+
+              const newStatuses = await fetchHome;
+              if (
+                newStatuses.length &&
+                newStatuses[0].id !== states.home[0].id
+              ) {
+                states.homeNew = newStatuses.map((status) => {
+                  states.statuses.set(status.id, status);
+                  if (status.reblog) {
+                    states.statuses.set(status.reblog.id, status.reblog);
+                  }
+                  return {
+                    id: status.id,
+                    reblog: status.reblog?.id,
+                    reply: !!status.inReplyToAccountId,
+                  };
+                });
+              }
+
+              const newNotifications = await fetchNotifications;
+              if (newNotifications.length) {
+                const notification = newNotifications[0];
+                const inNotificationsNew = states.notificationsNew.find(
+                  (n) => n.id === notification.id,
+                );
+                const inNotifications = states.notifications.find(
+                  (n) => n.id === notification.id,
+                );
+                if (!inNotificationsNew && !inNotifications) {
+                  states.notificationsNew.unshift(notification);
+                }
+
+                if (
+                  notification.status &&
+                  !states.statuses.has(notification.status.id)
+                ) {
+                  states.statuses.set(
+                    notification.status.id,
+                    notification.status,
+                  );
+                  if (
+                    notification.status.reblog &&
+                    !states.statuses.has(notification.status.reblog.id)
+                  ) {
+                    states.statuses.set(
+                      notification.status.reblog.id,
+                      notification.status.reblog,
+                    );
+                  }
+                }
+              }
+            } catch (e) {
+              // Silently fail
+              console.error(e);
+            }
+          })();
+        }, 100);
+      }
+    }
+  };
+  document.addEventListener('visibilitychange', handleVisibilityChange);
+  return {
+    stop: () => {
+      document.removeEventListener('visibilitychange', handleVisibilityChange);
+    },
+  };
+}
+
+export { App };