diff --git a/packages/app-extension/src/app/Router.tsx b/packages/app-extension/src/app/Router.tsx
index 43c10c40c..00831d581 100644
--- a/packages/app-extension/src/app/Router.tsx
+++ b/packages/app-extension/src/app/Router.tsx
@@ -41,6 +41,7 @@ import {
import { AuthenticatedSync } from "../components/Unlocked/AuthenticatedSync";
import { WithAuth } from "../components/Unlocked/WithAuth";
import { refreshFeatureGates } from "../gates/FEATURES";
+import { Spotlight } from "../spotlight/Spotlight";
import { sanitizeTransactionWithFeeConfig } from "../utils/solana";
import "./App.css";
@@ -58,6 +59,7 @@ export default function Router() {
theme={isDarkMode ? "dark" : "light"}
/>
<_Router />
+
>
);
diff --git a/packages/app-extension/src/spotlight/ActionRow.tsx b/packages/app-extension/src/spotlight/ActionRow.tsx
new file mode 100644
index 000000000..c68b51ace
--- /dev/null
+++ b/packages/app-extension/src/spotlight/ActionRow.tsx
@@ -0,0 +1,38 @@
+import { PushDetail } from "@coral-xyz/react-common";
+import { useCustomTheme } from "@coral-xyz/themes";
+
+import { SELECTED_BLUE } from "./colors";
+import { Line } from "./Line";
+
+export const ActionRow = ({
+ title,
+ onClick,
+ selected = false,
+}: {
+ title: string;
+ onClick: any;
+ selected?: boolean;
+}) => {
+ const theme = useCustomTheme();
+
+ return (
+
(null);
+ const background = useBackgroundClient();
+ const { push, toRoot } = useNavigation();
+
+ useEffect(() => {
+ async function keyDownTextField(e: any) {
+ if (e.which === 38) {
+ setArrowIndex((x) => (x === null ? 0 : x - 1));
+ } else if (e.which === 40) {
+ setArrowIndex((x) => (x === null ? 0 : x + 1));
+ }
+ if ((e.key === "Enter" || e.key === "Return") && arrowIndex !== null) {
+ await toRoot();
+ await background.request({
+ method: UI_RPC_METHOD_NAVIGATION_ACTIVE_TAB_UPDATE,
+ params: [TAB_MESSAGES],
+ });
+ if (arrowIndex !== null && arrowIndex !== -1 && arrowIndex % 2 === 0) {
+ push({
+ title: `@${friend.username}`,
+ componentId: NAV_COMPONENT_MESSAGE_CHAT,
+ componentProps: {
+ userId: friend?.uuid,
+ id: friend?.uuid,
+ username: friend?.username,
+ },
+ });
+ } else {
+ push({
+ title: `@${friend?.username}`,
+ componentId: NAV_COMPONENT_MESSAGE_PROFILE,
+ componentProps: {
+ userId: friend?.uuid,
+ },
+ });
+ }
+ setOpen(false);
+ }
+ }
+ document.addEventListener("keydown", keyDownTextField);
+
+ return () => {
+ document.removeEventListener("keydown", keyDownTextField);
+ };
+ }, [arrowIndex, friend]);
+
+ return (
+
+
+
{}}
+ contact={friend}
+ selected={false}
+ />
+
+ {
+ await toRoot();
+ await background.request({
+ method: UI_RPC_METHOD_NAVIGATION_ACTIVE_TAB_UPDATE,
+ params: [TAB_MESSAGES],
+ });
+
+ push({
+ title: `@${friend.username}`,
+ componentId: NAV_COMPONENT_MESSAGE_CHAT,
+ componentProps: {
+ userId: friend?.uuid,
+ id: friend?.uuid,
+ username: friend?.username,
+ },
+ });
+ setOpen(false);
+ }}
+ />
+ {
+ await toRoot();
+ await background.request({
+ method: UI_RPC_METHOD_NAVIGATION_ACTIVE_TAB_UPDATE,
+ params: [TAB_MESSAGES],
+ });
+
+ push({
+ title: `@${friend?.username}`,
+ componentId: NAV_COMPONENT_MESSAGE_PROFILE,
+ componentProps: {
+ userId: friend?.uuid,
+ },
+ });
+ setOpen(false);
+ }}
+ />
+
+ );
+};
diff --git a/packages/app-extension/src/spotlight/GroupIdentifier.tsx b/packages/app-extension/src/spotlight/GroupIdentifier.tsx
new file mode 100644
index 000000000..ec38d7c18
--- /dev/null
+++ b/packages/app-extension/src/spotlight/GroupIdentifier.tsx
@@ -0,0 +1,23 @@
+import { PushDetail } from "@coral-xyz/react-common";
+import { useCustomTheme } from "@coral-xyz/themes";
+
+import { ActionRow } from "./ActionRow";
+
+export const GroupIdentifier = ({ name }: { name: string }) => {
+ const theme = useCustomTheme();
+
+ return (
+
+ );
+};
diff --git a/packages/app-extension/src/spotlight/Line.tsx b/packages/app-extension/src/spotlight/Line.tsx
new file mode 100644
index 000000000..a1b6d6222
--- /dev/null
+++ b/packages/app-extension/src/spotlight/Line.tsx
@@ -0,0 +1,9 @@
+import { useCustomTheme } from "@coral-xyz/themes";
+
+export const Line = () => {
+ const theme = useCustomTheme();
+
+ return (
+
+ );
+};
diff --git a/packages/app-extension/src/spotlight/SearchBar.tsx b/packages/app-extension/src/spotlight/SearchBar.tsx
new file mode 100644
index 000000000..411f09a3f
--- /dev/null
+++ b/packages/app-extension/src/spotlight/SearchBar.tsx
@@ -0,0 +1,38 @@
+import * as React from "react";
+import { TextInput } from "@coral-xyz/react-common";
+import { useCustomTheme } from "@coral-xyz/themes";
+import SearchIcon from "@mui/icons-material/Search";
+
+import { useStyles } from "./styles";
+
+export const SpotlightSearchBar = ({
+ searchFilter,
+ setSearchFilter,
+}: {
+ searchFilter: string;
+ setSearchFilter: any;
+}) => {
+ const classes = useStyles();
+ const theme = useCustomTheme();
+
+ return (
+
+ }
+ value={searchFilter}
+ setValue={async (e) => {
+ const prefix = e.target.value;
+ setSearchFilter(prefix);
+ }}
+ inputProps={{
+ style: {
+ height: "48px",
+ },
+ }}
+ />
+ );
+};
diff --git a/packages/app-extension/src/spotlight/SearchBody.tsx b/packages/app-extension/src/spotlight/SearchBody.tsx
new file mode 100644
index 000000000..c3f0b8311
--- /dev/null
+++ b/packages/app-extension/src/spotlight/SearchBody.tsx
@@ -0,0 +1,82 @@
+import { SpotlightContacts } from "./SpotlightContacts";
+import { SpotlightGroups } from "./SpotlightGroups";
+import { SpotlightNfts } from "./SpotlightNfts";
+import { SpotlightTokens } from "./SpotlightTokens";
+import { useSearchedContacts } from "./useSearchedContacts";
+import { useSearchedGroupsCollections } from "./useSearchedGroups";
+import { useSearchedNfts } from "./useSearchedNfts";
+import { useSearchedTokens } from "./useSearchedTokens";
+import { getCurrentCounter } from "./utils";
+
+export const SearchBody = ({
+ searchFilter,
+ arrowIndex,
+ setOpen,
+ setSelectedContact,
+}: {
+ searchFilter: string;
+ arrowIndex: number;
+ setOpen: any;
+ setSelectedContact: any;
+}) => {
+ const contacts = useSearchedContacts(searchFilter);
+ const groups = useSearchedGroupsCollections(searchFilter);
+ const nfts = useSearchedNfts(searchFilter);
+ const tokens = useSearchedTokens(searchFilter);
+ const allResultsLength =
+ contacts.length + groups.length + nfts.length + tokens.length;
+ const currentCounter = getCurrentCounter(arrowIndex, allResultsLength);
+
+ if (!searchFilter) return ;
+
+ return (
+
+
+
+
+
+ = contacts.length &&
+ currentCounter < contacts.length + groups.length
+ ? currentCounter - contacts.length
+ : null
+ }
+ groups={groups}
+ setOpen={setOpen}
+ />
+
+
+ = contacts.length + groups.length &&
+ currentCounter < contacts.length + groups.length + nfts.length
+ ? currentCounter - contacts.length - groups.length
+ : null
+ }
+ nfts={nfts}
+ setOpen={setOpen}
+ />
+
+
+ = contacts.length + groups.length + nfts.length &&
+ currentCounter <
+ contacts.length + groups.length + nfts.length + tokens.length
+ ? currentCounter - contacts.length - groups.length - nfts.length
+ : null
+ }
+ tokens={tokens}
+ setOpen={setOpen}
+ />
+
+
+ );
+};
diff --git a/packages/app-extension/src/spotlight/Spotlight.tsx b/packages/app-extension/src/spotlight/Spotlight.tsx
new file mode 100644
index 000000000..f2b87ce81
--- /dev/null
+++ b/packages/app-extension/src/spotlight/Spotlight.tsx
@@ -0,0 +1,218 @@
+import * as React from "react";
+import { useEffect, useRef, useState } from "react";
+import {
+ Blockchain,
+ NAV_COMPONENT_MESSAGE_GROUP_CHAT,
+ NAV_COMPONENT_NFT_DETAIL,
+ NAV_COMPONENT_TOKEN,
+ toTitleCase,
+} from "@coral-xyz/common";
+import {
+ useActiveWallet,
+ useBlockchainConnectionUrl,
+ useBreakpoints,
+ useNavigation,
+} from "@coral-xyz/recoil";
+import { useCustomTheme } from "@coral-xyz/themes";
+import Box from "@mui/material/Box";
+import Modal from "@mui/material/Modal";
+
+import { FriendCard } from "./FriendCard";
+import { SpotlightSearchBar } from "./SearchBar";
+import { SearchBody } from "./SearchBody";
+import { useSearchedContacts } from "./useSearchedContacts";
+import { useSearchedGroupsCollections } from "./useSearchedGroups";
+import { useSearchedNfts } from "./useSearchedNfts";
+import { useSearchedTokens } from "./useSearchedTokens";
+import { getCurrentCounter } from "./utils";
+
+const style = {
+ position: "fixed",
+ top: 50,
+ left: "50%",
+ transform: "translate(-50%, 0)",
+ boxShadow: 24,
+};
+
+export const Spotlight = () => {
+ const [open, setOpen] = useState(false);
+ const theme = useCustomTheme();
+ const { isXs } = useBreakpoints();
+ const [arrowIndex, setArrowIndex] = useState(0);
+ const [selectedContact, setSelectedContact] = useState<{
+ username: string;
+ image: string;
+ uuid: string;
+ } | null>(null);
+
+ useEffect(() => {
+ function keyDownTextField(e: any) {
+ if (e.key === "k" && e.metaKey) {
+ setOpen(true);
+ e.preventDefault();
+ }
+ if (e.which === 27 || e.keyCode === 27) {
+ setOpen(false);
+ }
+ if (e.which === 38) {
+ setArrowIndex((x) => x - 1);
+ } else if (e.which === 40) {
+ setArrowIndex((x) => x + 1);
+ }
+ }
+ document.addEventListener("keydown", keyDownTextField);
+
+ return () => {
+ document.removeEventListener("keydown", keyDownTextField);
+ };
+ }, []);
+
+ return (
+ {
+ if (selectedContact) {
+ setSelectedContact(null);
+ } else {
+ setOpen(false);
+ }
+ }}
+ >
+
+
+
+
+ );
+};
+
+function SpotlightInner({
+ arrowIndex,
+ selectedContact,
+ setSelectedContact,
+ setOpen,
+}: {
+ arrowIndex: number;
+ selectedContact: { username: string; image: string; uuid: string } | null;
+ setSelectedContact: any;
+ setOpen: any;
+}) {
+ const [searchFilter, setSearchFilter] = useState("");
+ const contacts = useSearchedContacts(searchFilter);
+ const groups = useSearchedGroupsCollections(searchFilter);
+ const nfts = useSearchedNfts(searchFilter);
+ const tokens = useSearchedTokens(searchFilter);
+ const allResultsLength =
+ contacts.length + groups.length + nfts.length + tokens.length;
+ const { push, toRoot } = useNavigation();
+ const activeWallet = useActiveWallet();
+ const connectionUrl = useBlockchainConnectionUrl(activeWallet.blockchain);
+
+ if (selectedContact) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+ {
+ if (e.keyCode === 13) {
+ const currentCounter = getCurrentCounter(
+ arrowIndex,
+ allResultsLength
+ );
+ const selectedContactIndex =
+ currentCounter < contacts.length ? currentCounter : null;
+ const selectedGroupChatIndex =
+ currentCounter >= contacts.length &&
+ currentCounter - contacts.length < groups.length
+ ? currentCounter - contacts.length
+ : null;
+ const selectedNFtChatIndex =
+ currentCounter >= contacts.length + groups.length &&
+ currentCounter - contacts.length - groups.length < nfts.length
+ ? currentCounter - contacts.length - groups.length
+ : null;
+ const selectedTokenIndex =
+ currentCounter >= contacts.length + groups.length + nfts.length &&
+ currentCounter - contacts.length - groups.length - nfts.length <
+ tokens.length
+ ? currentCounter - contacts.length - groups.length - nfts.length
+ : null;
+
+ if (selectedContactIndex || selectedContactIndex === 0) {
+ setSelectedContact(contacts[selectedContactIndex]);
+ return;
+ }
+ if (selectedGroupChatIndex || selectedGroupChatIndex === 0) {
+ const group = groups[selectedGroupChatIndex];
+ push({
+ title: group?.name,
+ componentId: NAV_COMPONENT_MESSAGE_GROUP_CHAT,
+ componentProps: {
+ fromInbox: true,
+ id: group.collectionId,
+ title: group?.name,
+ },
+ });
+ setOpen(false);
+ return;
+ }
+ if (selectedNFtChatIndex || selectedNFtChatIndex === 0) {
+ const nft = nfts[selectedNFtChatIndex];
+ push({
+ title: nft?.name,
+ componentId: NAV_COMPONENT_NFT_DETAIL,
+ componentProps: {
+ nftId: nft.id,
+ publicKey: activeWallet.publicKey,
+ connectionUrl,
+ },
+ });
+ setOpen(false);
+ return;
+ }
+ if (selectedTokenIndex || selectedTokenIndex === 0) {
+ const token = tokens[selectedTokenIndex];
+ push({
+ title: `${toTitleCase(Blockchain.SOLANA)} / ${token.name}`,
+ componentId: NAV_COMPONENT_TOKEN,
+ componentProps: {
+ blockchain: "solana",
+ tokenAddress: token.address,
+ publicKey: activeWallet.publicKey,
+ },
+ });
+ setOpen(false);
+ return;
+ }
+ }
+ }}
+ >
+
+
+
+ );
+}
diff --git a/packages/app-extension/src/spotlight/SpotlightContacts.tsx b/packages/app-extension/src/spotlight/SpotlightContacts.tsx
new file mode 100644
index 000000000..15362f235
--- /dev/null
+++ b/packages/app-extension/src/spotlight/SpotlightContacts.tsx
@@ -0,0 +1,75 @@
+import { UserIcon } from "@coral-xyz/react-common";
+import { useCustomTheme } from "@coral-xyz/themes";
+
+import { SELECTED_BLUE } from "./colors";
+import { GroupIdentifier } from "./GroupIdentifier";
+
+export const SpotlightContacts = ({
+ contacts,
+ selectedIndex,
+ setSelectedContact,
+}: {
+ selectedIndex: number | null;
+ contacts: {
+ username: string;
+ image: string;
+ uuid: string;
+ }[];
+ setSelectedContact: any;
+}) => {
+ if (!contacts.length) return null;
+ return (
+
+
+ {contacts.map((contact, index) => (
+
+ ))}
+
+ );
+};
+
+export function SpotlightContact({
+ contact,
+ selected,
+ setSelectedContact,
+}: {
+ contact: {
+ username: string;
+ image: string;
+ uuid: string;
+ };
+ selected: boolean;
+ setSelectedContact: any;
+}) {
+ const theme = useCustomTheme();
+ return (
+ {
+ setSelectedContact(contact);
+ }}
+ >
+
+
+ {contact.username}
+
+
+ );
+}
diff --git a/packages/app-extension/src/spotlight/SpotlightGroups.tsx b/packages/app-extension/src/spotlight/SpotlightGroups.tsx
new file mode 100644
index 000000000..39021128f
--- /dev/null
+++ b/packages/app-extension/src/spotlight/SpotlightGroups.tsx
@@ -0,0 +1,84 @@
+import { NAV_COMPONENT_MESSAGE_GROUP_CHAT } from "@coral-xyz/common";
+import { UserIcon } from "@coral-xyz/react-common";
+import { useNavigation } from "@coral-xyz/recoil";
+import { useCustomTheme } from "@coral-xyz/themes";
+
+import { SELECTED_BLUE } from "./colors";
+import { GroupIdentifier } from "./GroupIdentifier";
+
+export const SpotlightGroups = ({
+ groups,
+ selectedIndex,
+ setOpen,
+}: {
+ groups: {
+ name: string;
+ image: string;
+ collectionId: string;
+ }[];
+ selectedIndex: number | null;
+ setOpen: any;
+}) => {
+ if (!groups.length) return null;
+ return (
+
+
+ {groups.map((group, index) => (
+
+ ))}
+
+ );
+};
+
+function SpotlightGroup({
+ group,
+ selected,
+ setOpen,
+}: {
+ group: { name: string; image: string; collectionId: string };
+ selected: boolean;
+ setOpen: any;
+}) {
+ const theme = useCustomTheme();
+ const { push, toRoot } = useNavigation();
+
+ return (
+ {
+ push({
+ title: group?.name,
+ componentId: NAV_COMPONENT_MESSAGE_GROUP_CHAT,
+ componentProps: {
+ fromInbox: true,
+ id: group.collectionId,
+ title: group?.name,
+ },
+ });
+ setOpen(false);
+ }}
+ >
+
+
+ {group.name}
+
+
+ );
+}
diff --git a/packages/app-extension/src/spotlight/SpotlightNfts.tsx b/packages/app-extension/src/spotlight/SpotlightNfts.tsx
new file mode 100644
index 000000000..6fc9a64f8
--- /dev/null
+++ b/packages/app-extension/src/spotlight/SpotlightNfts.tsx
@@ -0,0 +1,86 @@
+import { NAV_COMPONENT_NFT_DETAIL } from "@coral-xyz/common";
+import { UserIcon } from "@coral-xyz/react-common";
+import {
+ useActiveWallet,
+ useBlockchainConnectionUrl,
+ useNavigation,
+} from "@coral-xyz/recoil";
+import { useCustomTheme } from "@coral-xyz/themes";
+
+import { SELECTED_BLUE } from "./colors";
+import { GroupIdentifier } from "./GroupIdentifier";
+
+export const SpotlightNfts = ({
+ nfts,
+ selectedIndex,
+ setOpen,
+}: {
+ nfts: { name: string; image: string; id: string }[];
+ selectedIndex: number | null;
+ setOpen: any;
+}) => {
+ if (!nfts.length) return null;
+ return (
+
+
+ {nfts.map((nft, index) => (
+
+ ))}
+
+ );
+};
+
+function SpotlightNft({
+ nft,
+ selected,
+ setOpen,
+}: {
+ nft: { name: string; image: string; id: string };
+ selected: boolean;
+ setOpen: any;
+}) {
+ const activeWallet = useActiveWallet();
+ const connectionUrl = useBlockchainConnectionUrl(activeWallet.blockchain);
+ const { push } = useNavigation();
+
+ const theme = useCustomTheme();
+ return (
+ {
+ push({
+ title: nft?.name,
+ componentId: NAV_COMPONENT_NFT_DETAIL,
+ componentProps: {
+ nftId: nft.id,
+ publicKey: activeWallet.publicKey,
+ connectionUrl,
+ },
+ });
+ setOpen(false);
+ }}
+ >
+
+
+ {nft.name}
+
+
+ );
+}
diff --git a/packages/app-extension/src/spotlight/SpotlightTokens.tsx b/packages/app-extension/src/spotlight/SpotlightTokens.tsx
new file mode 100644
index 000000000..be7e84e92
--- /dev/null
+++ b/packages/app-extension/src/spotlight/SpotlightTokens.tsx
@@ -0,0 +1,85 @@
+import {
+ Blockchain,
+ NAV_COMPONENT_TOKEN,
+ toTitleCase,
+} from "@coral-xyz/common";
+import { UserIcon } from "@coral-xyz/react-common";
+import { useActiveWallet, useNavigation } from "@coral-xyz/recoil";
+import { useCustomTheme } from "@coral-xyz/themes";
+
+import { SELECTED_BLUE } from "./colors";
+import { GroupIdentifier } from "./GroupIdentifier";
+
+export const SpotlightTokens = ({
+ selectedIndex,
+ tokens,
+ setOpen,
+}: {
+ selectedIndex: number | null;
+ tokens: { image: string; id: string; name: string; address: string }[];
+ setOpen: any;
+}) => {
+ if (!tokens.length) return null;
+ return (
+
+
+ {tokens.map((token, index) => (
+
+ ))}
+
+ );
+};
+
+function SpotlightToken({
+ selected,
+ token,
+ setOpen,
+}: {
+ selected: boolean;
+ token: { image: string; id: string; name: string; address: string };
+ setOpen: any;
+}) {
+ const theme = useCustomTheme();
+ const { push } = useNavigation();
+ const activeWallet = useActiveWallet();
+
+ return (
+ {
+ push({
+ title: `${toTitleCase(Blockchain.SOLANA)} / ${token.name}`,
+ componentId: NAV_COMPONENT_TOKEN,
+ componentProps: {
+ blockchain: "solana",
+ tokenAddress: token.address,
+ publicKey: activeWallet.publicKey,
+ },
+ });
+ setOpen(false);
+ }}
+ >
+
+
+ {token.name}
+
+
+ );
+}
diff --git a/packages/app-extension/src/spotlight/colors.ts b/packages/app-extension/src/spotlight/colors.ts
new file mode 100644
index 000000000..2cb03239b
--- /dev/null
+++ b/packages/app-extension/src/spotlight/colors.ts
@@ -0,0 +1 @@
+export const SELECTED_BLUE = "rgba(76, 148, 255, 0.65)";
diff --git a/packages/app-extension/src/spotlight/styles.ts b/packages/app-extension/src/spotlight/styles.ts
new file mode 100644
index 000000000..4df59436c
--- /dev/null
+++ b/packages/app-extension/src/spotlight/styles.ts
@@ -0,0 +1,117 @@
+import { styles } from "@coral-xyz/themes";
+
+export const useStyles = styles((theme) => ({
+ searchField: {
+ marginTop: "16px",
+ marginBottom: "16px",
+ width: "inherit",
+ display: "flex",
+ "& .MuiOutlinedInput-root": {
+ "& input": {
+ paddingTop: 0,
+ paddingBottom: 0,
+ },
+ },
+ },
+ icon: {
+ background: theme.custom.colors.textBackground,
+ },
+ iconInner: {
+ background: theme.custom.colors.fontColor,
+ },
+ topImage: {
+ maxWidth: "25vw",
+ },
+ horizontalCenter: {
+ display: "flex",
+ justifyContent: "center",
+ },
+ container: {
+ marginLeft: "16px",
+ marginRight: "16px",
+ },
+ roundBtn: {
+ marginLeft: 8,
+ borderRadius: 20,
+ width: 20,
+ height: 20,
+ cursor: "pointer",
+ background: "#FFFFFF",
+ border: "2px solid #F0F0F2",
+ fontSize: 10,
+ },
+ add: {
+ width: 17,
+ paddingBottom: 6,
+ paddingRight: 1,
+ },
+ iconCircular: {
+ width: "32px",
+ height: "32px",
+ borderRadius: "16px",
+ marginRight: "8px",
+ color: theme.custom.colors.positive,
+ },
+ iconCircularBig: {
+ width: "40px",
+ height: "40px",
+ borderRadius: "16px",
+ marginRight: "8px",
+ color: theme.custom.colors.positive,
+ },
+ hoverParent: {
+ "&:hover $hoverChild, & .Mui-focused $hoverChild": {
+ visibility: "visible",
+ },
+ },
+ hoverChild: {
+ visibility: "hidden",
+ },
+ text: {
+ color: theme.custom.colors.fontColor2,
+ },
+ smallText: {
+ fontSize: 12,
+ color: theme.custom.colors.fontColor2,
+ },
+ userText: {
+ fontSize: 16,
+ marginTop: 4,
+ color: theme.custom.colors.fontColor2,
+ },
+ userTextSmall: {
+ fontSize: 14,
+ color: theme.custom.colors.fontColor2,
+ },
+ timestamp: {
+ fontSize: 14,
+ minWidth: 60,
+ color: theme.custom.colors.fontColor2,
+ },
+ smallTitle: {
+ color: theme.custom.colors.smallTextColor,
+ fontWeight: 600,
+ },
+ smallSubTitle: {
+ color: theme.custom.colors.smallTextColor,
+ fontWeight: 500,
+ },
+ contactIconOuter: {
+ background: theme.custom.colors.textBorder,
+ },
+ menuItem: {
+ fontWeight: 400,
+ fontSize: 14,
+ color: theme.custom.colors.fontColor,
+ padding: "12px 16px",
+ },
+ menu: {
+ "& .MuiList-root": {
+ padding: 0,
+ },
+ paddingTop: 0,
+ paddingBottom: 0,
+ minWidth: 184,
+ color: theme.custom.colors.fontColor,
+ },
+}));
diff --git a/packages/app-extension/src/spotlight/useSearchedContacts.tsx b/packages/app-extension/src/spotlight/useSearchedContacts.tsx
new file mode 100644
index 000000000..d566cef4c
--- /dev/null
+++ b/packages/app-extension/src/spotlight/useSearchedContacts.tsx
@@ -0,0 +1,17 @@
+import { useContacts } from "@coral-xyz/db";
+import { useUser } from "@coral-xyz/recoil";
+
+export const useSearchedContacts = (searchFilter: string) => {
+ const { uuid } = useUser();
+ const contacts = useContacts(uuid);
+
+ return contacts
+ .filter((x) =>
+ x.remoteUsername?.toLowerCase().includes(searchFilter?.toLowerCase())
+ )
+ .map((x) => ({
+ username: x.remoteUsername,
+ image: x.remoteUserImage,
+ uuid: x.remoteUserId,
+ }));
+};
diff --git a/packages/app-extension/src/spotlight/useSearchedGroups.tsx b/packages/app-extension/src/spotlight/useSearchedGroups.tsx
new file mode 100644
index 000000000..3981b7ff5
--- /dev/null
+++ b/packages/app-extension/src/spotlight/useSearchedGroups.tsx
@@ -0,0 +1,14 @@
+import { useGroupCollections, useUser } from "@coral-xyz/recoil";
+
+export const useSearchedGroupsCollections = (searchFilter: string) => {
+ const { uuid } = useUser();
+ const collections = useGroupCollections({ uuid });
+
+ return collections
+ .filter((x) => x.name?.toLowerCase()?.includes(searchFilter.toLowerCase()))
+ .map((x) => ({
+ name: x.name || "",
+ image: x.image || "",
+ collectionId: x.collectionId || "",
+ }));
+};
diff --git a/packages/app-extension/src/spotlight/useSearchedNfts.tsx b/packages/app-extension/src/spotlight/useSearchedNfts.tsx
new file mode 100644
index 000000000..a9ea74654
--- /dev/null
+++ b/packages/app-extension/src/spotlight/useSearchedNfts.tsx
@@ -0,0 +1,29 @@
+import React from "react";
+import type { Nft } from "@coral-xyz/common";
+import { Blockchain } from "@coral-xyz/common";
+import { nftsByOwner, useActiveSolanaWallet } from "@coral-xyz/recoil";
+import { useRecoilValueLoadable } from "recoil";
+
+export const useSearchedNfts = (searchFilter: string) => {
+ const activeSolWallet = useActiveSolanaWallet();
+ const { contents, state }: any = useRecoilValueLoadable(
+ nftsByOwner({
+ publicKey: activeSolWallet.publicKey,
+ blockchain: Blockchain.SOLANA,
+ })
+ );
+
+ if (state === "loading" || state === "hasError") {
+ return [];
+ }
+
+ return contents
+ .filter((x: Nft) =>
+ x.name?.toLowerCase()?.includes(searchFilter.toLowerCase())
+ )
+ .map((x: Nft) => ({
+ name: x.name || "",
+ image: x.imageUrl || "",
+ id: x.id || "",
+ }));
+};
diff --git a/packages/app-extension/src/spotlight/useSearchedTokens.tsx b/packages/app-extension/src/spotlight/useSearchedTokens.tsx
new file mode 100644
index 000000000..cb3178129
--- /dev/null
+++ b/packages/app-extension/src/spotlight/useSearchedTokens.tsx
@@ -0,0 +1,34 @@
+import React from "react";
+import { Blockchain } from "@coral-xyz/common";
+import {
+ blockchainBalancesSorted,
+ useActiveSolanaWallet,
+ useLoader,
+} from "@coral-xyz/recoil";
+
+export const useSearchedTokens = (searchFilter: string) => {
+ const activeSolWallet = useActiveSolanaWallet();
+
+ const [tokenAccounts, , isLoading] = useLoader(
+ blockchainBalancesSorted({
+ publicKey: activeSolWallet.publicKey,
+ blockchain: Blockchain.SOLANA,
+ }),
+ [],
+ [activeSolWallet]
+ );
+
+ if (isLoading) {
+ //TODO: adda skeletons here
+ return [];
+ }
+
+ return tokenAccounts
+ .filter((x) => x.name.toLowerCase().includes(searchFilter.toLowerCase()))
+ .map((x) => ({
+ name: x.name || "",
+ id: x.mint || "",
+ image: x.logo || "",
+ address: x.address || "",
+ }));
+};
diff --git a/packages/app-extension/src/spotlight/utils.ts b/packages/app-extension/src/spotlight/utils.ts
new file mode 100644
index 000000000..999e9afd8
--- /dev/null
+++ b/packages/app-extension/src/spotlight/utils.ts
@@ -0,0 +1,10 @@
+export const getCurrentCounter = (
+ arrowIndex: number,
+ allResultsLength: number
+) => {
+ return arrowIndex >= 0
+ ? arrowIndex % allResultsLength
+ : (arrowIndex +
+ -1 * Math.ceil(arrowIndex / allResultsLength) * allResultsLength) %
+ allResultsLength;
+};