Skip to content

Commit

Permalink
Address Tags Rework (#61)
Browse files Browse the repository at this point in the history
* Completely rework entire tag input

holy shit

* Remove unused dependencies, add tags to nodes

* remove unused libraries
  • Loading branch information
tubarao312 authored Feb 8, 2024
1 parent 2afddc1 commit b81c311
Show file tree
Hide file tree
Showing 24 changed files with 812 additions and 257 deletions.
3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@
"@heroicons/react": "^2.1.1",
"axios": "^1.6.2",
"clsx": "^2.0.0",
"dotenv": "^16.3.1",
"firebase": "^10.7.1",
"framer-motion": "^10.17.9",
"orval": "^6.23.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand All @@ -36,7 +34,6 @@
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.7",
"@types/dotenv": "^8.2.0",
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.15",
"@types/uuid": "^9.0.7",
Expand Down
5 changes: 2 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { QueryClientProvider } from "react-query";
import useCustomQueryClient from "./hooks/useCustomQueryClient";
import authService from "./services/auth/auth.services";

import "./services/firebase/firebase";
import useAuthState from "./hooks/useAuthState";

import PrivateApp from "./PrivateApp";
import PublicApp from "./PublicApp";
import { MobileWarningTemplate } from "./templates";

function App() {
const queryClient = useCustomQueryClient();
const user = authService.useAuthState().user;
const { user } = useAuthState();

return (
<>
Expand Down
14 changes: 12 additions & 2 deletions src/components/common/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,25 @@ interface BadgeProps {
text: string;
Icon?: any;
className?: string;
onClick?: () => void;
}

/** This component is a badge that displays a text and a color.
*
* @param text: The text of the badge
* @param color: The color of the badge
* @param Icon: The icon of the badge
* @param className: The class name of the
* @param className: The class name of the badge
* @param onClick: The function to call when the badge is clicked
*/

export default function Badge({ color, text, Icon, className }: BadgeProps) {
export default function Badge({
color,
text,
Icon,
className,
onClick,
}: BadgeProps) {
const { text: textColor, background, ring } = ColorMap[color];

return (
Expand All @@ -27,7 +35,9 @@ export default function Badge({ color, text, Icon, className }: BadgeProps) {
textColor,
background,
ring,
onClick && "cursor-pointer",
)}
onClick={onClick}
>
{Icon && <Icon className={`mr-0.5 h-3 w-3 ${textColor}`} />}
{text}
Expand Down
46 changes: 23 additions & 23 deletions src/components/graph/Graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import ReactFlow, {
} from "reactflow";
import "reactflow/dist/style.css";

import authService from "../../services/auth/auth.services";
import useAuthState from "../../hooks/useAuthState";

import { AddressAnalysis } from "../../api/model";

Expand All @@ -47,11 +47,9 @@ import {
convertNodeListToRecord,
} from "./graph_calculations";

import analytics from "../../services/firebase/analytics/analytics";
import { storeAddress } from "../../services/firebase/search-history/search-history";
import firestore, {
StoreUrlObject,
} from "../../services/firebase/short-urls/short-urls";
import analytics from "../../services/firestore/analytics/analytics";
import { storeAddress } from "../../services/firestore/user/search-history";

import generateShortUrl from "../../utils/generateShortUrl";
import TutorialPopup from "./tutorial/TutorialPopup";
import DraggableWindow from "./analysis_window/AnalysisWindow";
Expand Down Expand Up @@ -642,22 +640,24 @@ const GraphProvided: FC<GraphProvidedProps> = ({
}

async function copyLink(shortenedUrl: string): Promise<void> {
const link = getLink();
const key = shortenedUrl.split("/").pop()!;

const storeUrlObj: StoreUrlObject = {
originalUrl: link,
key: key,
};

await firestore.storeUrl(storeUrlObj).then(async (id) => {
if (id) {
await navigator.clipboard.writeText(shortenedUrl);
analytics.logAnalyticsEvent("copy_link", {
link: shortenedUrl,
});
}
});
console.log(getLink());
console.log(shortenedUrl);
// const link = getLink();
// const key = shortenedUrl.split("/").pop()!;

// const storeUrlObj: StoreUrlObject = {
// originalUrl: link,
// key: key,
// };

// await firestore.storeUrl(storeUrlObj).then(async (id) => {
// if (id) {
// await navigator.clipboard.writeText(shortenedUrl);
// analytics.logAnalyticsEvent("copy_link", {
// link: shortenedUrl,
// });
// }
// });
}

// Getting the node count so that we can show the legend dynamically ---------
Expand Down Expand Up @@ -763,7 +763,7 @@ const PublicGraph: FC<GraphProps> = ({
const [searchedAddresses, setSearchedAddresses] =
useState<string[]>(initialAddresses);

const { user } = authService.useAuthState();
const { user } = useAuthState();

const onSetSearchedAddress = (newAddress: string) => {
setSearchedAddresses([newAddress]);
Expand Down
2 changes: 1 addition & 1 deletion src/components/graph/analysis_window/AnalysisWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Transition } from "@headlessui/react";
import Draggable from "react-draggable";

import { Advanced, Transactions, Overview } from "./content";
import Header from "./Header";
import Header from "./header/Header";

import { AddressAnalysis } from "../../../api/model";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,36 @@ import {
ArrowsPointingInIcon,
IdentificationIcon,
} from "@heroicons/react/20/solid";

import { XMarkIcon as XMarkSmallIcon } from "@heroicons/react/16/solid";

import clsx from "clsx";
import { FC, useContext, useCallback } from "react";
import { FC, useContext, useCallback, useState, useEffect } from "react";

import EntityLogo from "../../common/EntityLogo";
import BlockExplorerAddressIcon from "../../common/utility_icons/BlockExplorerAddressIcon";
import CopyToClipboardIcon from "../../common/utility_icons/CopyToClipboardIcon";
import LabelList from "../../common/LabelList";
import RiskIndicator from "../../common/RiskIndicator";
import { AddressAnalysis, Category } from "../../../../api/model";
import { CategoryClasses } from "../../../../utils/categories";

import {
deleteCustomAddressTag,
getCustomAddressesTags,
storeCustomAddressesTags,
} from "../../../../services/firestore/graph/addresses/custom-tags";

import { AnalysisContext } from "./AnalysisWindow";
import { GraphContext } from "../Graph";
import { AnalysisMode, AnalysisModes } from "./AnalysisWindow";
import { AddressAnalysis, Category } from "../../../api/model";
import { Colors } from "../../../../utils/colors";

import { PathExpansionArgs } from "../Graph";
import EntityLogo from "../../../common/EntityLogo";
import BlockExplorerAddressIcon from "../../../common/utility_icons/BlockExplorerAddressIcon";
import CopyToClipboardIcon from "../../../common/utility_icons/CopyToClipboardIcon";
import Badge from "../../../common/Badge";
import RiskIndicator from "../../../common/RiskIndicator";

import { CategoryClasses } from "../../../utils/categories";
import { AnalysisContext } from "../AnalysisWindow";
import { GraphContext } from "../../Graph";
import { AnalysisMode, AnalysisModes } from "../AnalysisWindow";

import { PathExpansionArgs } from "../../Graph";

import TagInput from "./TagInput";

interface ModeButtonProps {
isActive: boolean;
Expand Down Expand Up @@ -53,7 +66,16 @@ const ModeButton: FC<ModeButtonProps> = ({
})}
aria-hidden="true"
/>
{analysisMode.name}
<a
className="w-fit overflow-hidden"
style={{
maxWidth: isActive ? "5rem" : "0px",
opacity: isActive ? 1 : 0,
transition: "all 0.3s ease-in-out",
}}
>
{analysisMode.name}
</a>
</button>
);
};
Expand Down Expand Up @@ -110,6 +132,63 @@ function getCategoryRisk(category: string): number {
return categoryClass.risk;
}

// Label + Tag + Tag Input Wrapped Component

const LabelsAndTags: FC = () => {
const { analysisData, address } = useContext(AnalysisContext);

// Labels get displayed first in the flex-wrap
const labels = analysisData!.labels;

// Tags need to be fetched from firestore async
const [tags, setTags] = useState<string[]>([]);

async function fetchAddressTags(address: string) {
getCustomAddressesTags(address).then((tags) => {
setTags(tags);
});
}

useEffect(() => {
fetchAddressTags(address);
}, [address]);

const onCreateCustomAddressTag = useCallback(
(tag: string) => {
storeCustomAddressesTags(address, [...tags, tag]);
setTags([...tags, tag]);
},
[tags],
);

// Display everything and user input at the end in a flex-wrap
return (
<span className="flex w-96 flex-wrap items-center gap-x-1.5 gap-y-1">
{labels.map((label) => (
<Badge key={label} color={Colors.BLUE} text={label} />
))}
{tags.map((tag) => (
<Badge
key={tag}
color={Colors.PURPLE}
text={tag}
Icon={XMarkSmallIcon}
onClick={() => {
deleteCustomAddressTag(address, tag);
const newTags = tags.filter((t) => t !== tag);
setTags(newTags);
}}
/>
))}
<TagInput
address={address}
initialTags={tags}
onCreateCustomAddressTag={onCreateCustomAddressTag}
/>
</span>
);
};

interface HeaderProps {
onExit: () => void;
setAnalysisMode: (mode: AnalysisMode) => void;
Expand Down Expand Up @@ -227,9 +306,9 @@ const Header: FC<HeaderProps> = ({
/>
)}
</span>
<span className="flex w-60 flex-row items-center justify-start gap-x-1.5 overflow-x-auto">
<span className="flex flex-row items-center justify-start gap-x-1.5">
{/* List of labels using Badges shown underneath the address. */}
<LabelList labels={analysisData!.labels} />
<LabelsAndTags />
</span>
</div>
</span>
Expand Down
Loading

0 comments on commit b81c311

Please sign in to comment.