diff --git a/apps/web/next.config.js b/apps/web/next.config.js
index aeb5a253..c58f5075 100644
--- a/apps/web/next.config.js
+++ b/apps/web/next.config.js
@@ -10,11 +10,16 @@ const nextConfig = {
]
},
async redirects() {
+ if (process.env.NODE_ENV === "development") {
+ return [];
+ }
+
return [
{
source: "/",
destination: "/previewnet",
- permanent: true
+ permanent: true,
+
},
// Redirect to the main release that doesn't support Cadence v1.0
{
diff --git a/apps/web/src/common/NetworkDropdown.tsx b/apps/web/src/common/NetworkDropdown.tsx
index 1bca0b63..b5749b4f 100644
--- a/apps/web/src/common/NetworkDropdown.tsx
+++ b/apps/web/src/common/NetworkDropdown.tsx
@@ -36,12 +36,6 @@ export function NetworkDropdown(props: NetworkDropdownProps) {
{network}
))}
-
)
}
diff --git a/apps/web/src/common/interaction-page-params.ts b/apps/web/src/common/interaction-page-params.ts
index a1f66608..2dbc4087 100644
--- a/apps/web/src/common/interaction-page-params.ts
+++ b/apps/web/src/common/interaction-page-params.ts
@@ -1,8 +1,12 @@
import { FlowNetworkId } from "@onflowser/core/src/flow-utils";
+import {
+ InteractionTemplateFilters
+} from "@onflowser/ui/src/interactions/components/InteractionTemplates/interaction-templates-controller.provider";
// Must be placed in separate (type-only) file,
// so that opengraph-image doesn't reach code size limit.
export type InteractionsPageParams = {
networkId: FlowNetworkId;
interaction?: string;
+ templateFilters: InteractionTemplateFilters;
}
diff --git a/apps/web/src/common/root.tsx b/apps/web/src/common/root.tsx
index 4c0809d5..cb4a22de 100644
--- a/apps/web/src/common/root.tsx
+++ b/apps/web/src/common/root.tsx
@@ -50,6 +50,9 @@ import { BaseDialog } from "@onflowser/ui/src/common/overlays/dialogs/base/BaseD
import { NetworkDropdown } from "./NetworkDropdown";
import { ProfileDropdown } from "@/common/ProfileDropdown";
import { ConfirmDialogProvider } from "@onflowser/ui/src/contexts/confirm-dialog.context";
+import {
+ InteractionTemplateFiltersProvider
+} from "@onflowser/ui/src/interactions/components/InteractionTemplates/interaction-templates-controller.provider";
const indexSyncIntervalInMs = 500;
@@ -448,7 +451,7 @@ function ApiSetupPrompt(props: {
}
function Content() {
- const { networkId, interaction, setNetworkId } = useInteractionsPageParams();
+ const { networkId, interaction, setNetworkId, templateFilters } = useInteractionsPageParams();
const interactionRegistry = useInteractionRegistry();
const templatesRegistry = useTemplatesRegistry();
@@ -488,19 +491,21 @@ function Content() {
return (
-
-
-
-
- }
- tabOrder={["templates", "history"]}
- enabledInteractionSourceTypes={[
- 'session',
- 'flix',
- ]}
- />
+
+
+
+
+
+ }
+ tabOrder={["templates", "history"]}
+ enabledInteractionSourceTypes={[
+ 'session',
+ 'flix',
+ ]}
+ />
+
);
}
diff --git a/apps/web/src/common/use-interaction-page-params.ts b/apps/web/src/common/use-interaction-page-params.ts
index 40eb76b6..50de156a 100644
--- a/apps/web/src/common/use-interaction-page-params.ts
+++ b/apps/web/src/common/use-interaction-page-params.ts
@@ -1,6 +1,10 @@
-import { useParams, useRouter } from "next/navigation";
+import { useParams, useRouter, useSearchParams } from "next/navigation";
import { FlowNetworkId, FlowUtils } from "@onflowser/core/src/flow-utils";
import { InteractionsPageParams } from "@/common/interaction-page-params";
+import {
+ InteractionTemplateFilters
+} from "@onflowser/ui/src/interactions/components/InteractionTemplates/interaction-templates-controller.provider";
+import { ensurePrefixedAddress } from "@onflowser/core";
type UseInteractionPageParams = InteractionsPageParams & {
setNetworkId: (network: FlowNetworkId) => void;
@@ -8,24 +12,44 @@ type UseInteractionPageParams = InteractionsPageParams & {
export function useInteractionsPageParams(): UseInteractionPageParams {
const params = useParams();
+ const search = useSearchParams();
const router = useRouter();
const interaction = params.interaction as string | undefined;
const networkId = params.networkId;
+ const templateFilters: InteractionTemplateFilters = {};
+
+ const rawFlixDependency = search.get("flix-dependency");
+ if (rawFlixDependency) {
+ // Accepts format A.address.ContractName
+ const [prefix, address, name] = rawFlixDependency.split(".");
+
+ templateFilters.dependencies = {
+ contractAddress: ensurePrefixedAddress(address),
+ contractName: name
+ }
+ }
if (!FlowUtils.isValidFlowNetwork(networkId)) {
throw new Error(`Unknown Flow network: ${networkId}`)
}
function setNetworkId(network: FlowNetworkId) {
+ let prefix = "";
+
+ if (network === "previewnet") {
+ prefix = "https://previewnet.flowser.dev"
+ }
+
if (interaction) {
- router.replace(`/${network}/${interaction}`)
+ router.replace(`${prefix}/${network}/${interaction}`)
} else {
- router.replace(`/${network}`)
+ router.replace(`${prefix}/${network}`)
}
}
return {
+ templateFilters,
networkId,
interaction,
setNetworkId
diff --git a/packages/ui/src/interactions/components/InteractionTemplates/InteractionTemplates.tsx b/packages/ui/src/interactions/components/InteractionTemplates/InteractionTemplates.tsx
index 16a2f970..17ba54c6 100644
--- a/packages/ui/src/interactions/components/InteractionTemplates/InteractionTemplates.tsx
+++ b/packages/ui/src/interactions/components/InteractionTemplates/InteractionTemplates.tsx
@@ -19,6 +19,9 @@ import { Shimmer } from "../../../common/loaders/Shimmer/Shimmer";
import { MenuItem } from "@szhsin/react-menu";
import { EditTemplateNameDialog } from "../EditTemplateNameDialog/EditTemplateNameDialog";
import { Menu } from "../../../common/overlays/Menu/Menu";
+import { useOptionalInteractionTemplateFilters } from "./interaction-templates-controller.provider";
+import { FlixUtils } from "@onflowser/core";
+import { useFlowNetworkId } from "../../../contexts/flow-network.context";
type InteractionTemplatesProps = {
enabledSourceTypes: InteractionSourceType[];
@@ -35,14 +38,38 @@ export function InteractionTemplates(props: InteractionTemplatesProps): ReactEle
function StoredTemplates(props: InteractionTemplatesProps) {
const [searchTerm, setSearchTerm] = useState("");
+ const networkId = useFlowNetworkId();
const templatesRegistry = useTemplatesRegistry();
+ const filters = useOptionalInteractionTemplateFilters();
const [filterToSources, setFilterToSources] = useLocalStorage("interaction-filters", []);
const filteredTemplates = useMemo(() => {
- const searchQueryResults = searchTerm === "" ? templatesRegistry.templates : templatesRegistry.templates.filter((template) => template.name.toLowerCase().includes(searchTerm.toLowerCase()));
+ let results = Array.from(templatesRegistry.templates);
- const sourceFilterResults = filterToSources.length === 0 ? searchQueryResults : searchQueryResults.filter(e => filterToSources.includes(e.source));
+ if (filters?.dependencies) {
+ results = results.filter(template => template.flix && FlixUtils.getDependencies(template.flix, networkId).some(dependency => {
+ let shouldInclude = true;
- return sourceFilterResults;
+ if (filters.dependencies?.contractAddress) {
+ shouldInclude = shouldInclude && dependency.address == filters.dependencies.contractAddress;
+ }
+
+ if (filters.dependencies?.contractName) {
+ shouldInclude = shouldInclude && dependency.name === filters.dependencies.contractName;
+ }
+
+ return shouldInclude;
+ }));
+ }
+
+ if (searchTerm) {
+ results = results.filter((template) => template.name.toLowerCase().includes(searchTerm.toLowerCase()))
+ }
+
+ if (filterToSources.length > 0) {
+ results = results.filter(template => filterToSources.includes(template.source))
+ }
+
+ return results;
}, [searchTerm, filterToSources, templatesRegistry.templates]);
const filteredAndSortedTemplates = useMemo(
() =>
diff --git a/packages/ui/src/interactions/components/InteractionTemplates/interaction-templates-controller.provider.tsx b/packages/ui/src/interactions/components/InteractionTemplates/interaction-templates-controller.provider.tsx
new file mode 100644
index 00000000..dda22fa8
--- /dev/null
+++ b/packages/ui/src/interactions/components/InteractionTemplates/interaction-templates-controller.provider.tsx
@@ -0,0 +1,27 @@
+import { createContext, ReactNode, useContext } from "react";
+
+export type InteractionTemplateFilters = {
+ dependencies?: {
+ contractAddress?: string;
+ contractName?: string;
+ }
+}
+
+const Context = createContext(undefined as never);
+
+type Props = {
+ filters: InteractionTemplateFilters;
+ children: ReactNode;
+}
+
+export function InteractionTemplateFiltersProvider(props: Props) {
+ return (
+
+ {props.children}
+
+ )
+}
+
+export function useOptionalInteractionTemplateFilters(): InteractionTemplateFilters | undefined {
+ return useContext(Context);
+}