Skip to content

Commit

Permalink
initial commit: monorepo + base app
Browse files Browse the repository at this point in the history
  • Loading branch information
nezz0746 committed Nov 12, 2023
1 parent 6abb22d commit 3c22904
Show file tree
Hide file tree
Showing 17 changed files with 2,198 additions and 159 deletions.
62 changes: 1 addition & 61 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,61 +1 @@
# 🔌 ETH Basic Typescript Starter

### ✨ New version (V3!) ✨

This monorepo should allow you to get started with a simple Counter smart contract on your local anvil instance, and a dapp

- React / Typescript (NextJS)
- 🏎️ [Turborepo](https://turborepo.org/)
- ⚒️ [Foundry](https://github.com/foundry-rs/foundry), with tests & local anvil instance:
- 🚀 [wagmi](https://wagmi.sh/) & 🌈 [RainbowKit](https://www.rainbowkit.com/) !
- Generated custom hooks with the wagmi-cli !
- [Tailwind CSS](https://tailwindcss.com/) with [DaisyUI](https://daisyui.com/) 🌼 !

Recommended:
- [Rivet](https://github.com/paradigmxyz/rivet): Developper wallet & devtool for you local developpement

## Get Started

### 0. Set environment variables (Optional)



### 1. Install dependencies

```
yarn
```

### 2. Start developement process
Will concurrently:

- launch your anvil instance
- start your nextjs app dev server

```
yarn run dev
```

### 3. Deploy

```
yarn run deploy:local
```

Will:
- Run your deploy script on your local chain
- Regenerate your custom hooks stright into your wagmi-config


```bash
/apps
# You foundry project
/contracts
# Your dapp
/web
/packages
# Contains wagmi & rainbowkit config and generated code with the wagmi-cli
/wagmi-config
# Hosting app constants
/shared-config
```
# Ensemble
25 changes: 19 additions & 6 deletions apps/web/components/ConnectButton.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { emojiAvatarForAddress } from "@/services/rainbow";
import { ConnectButton as RainbowKitConnectButton } from "@rainbow-me/rainbowkit";

const ConnectButton = () => {
Expand Down Expand Up @@ -30,27 +31,36 @@ const ConnectButton = () => {
userSelect: "none",
},
})}
className="font-display flex flex-row gap-2"
>
{(() => {
if (!connected) {
return (
<button onClick={openConnectModal} type="button">
<button
className="btn btn-outline rounded-none flex-grow"
onClick={openConnectModal}
type="button"
>
Connect Wallet
</button>
);
}
if (chain.unsupported) {
return (
<button onClick={openChainModal} type="button">
<button
className="btn btn-outline rounded-none flex-grow"
onClick={openChainModal}
type="button"
>
Wrong network
</button>
);
}
return (
<div style={{ display: "flex", gap: 12 }}>
<>
<button
onClick={openChainModal}
className="btn btn-outline"
className="btn btn-outline rounded-none flex-grow"
type="button"
>
{chain.hasIcon && (
Expand Down Expand Up @@ -78,14 +88,17 @@ const ConnectButton = () => {
<button
onClick={openAccountModal}
type="button"
className="btn btn-outline"
className="btn btn-outline rounded-none flex-grow flex flex-row items-center gap-2"
>
<p className="text-xl">
{(emojiAvatarForAddress(account.address) ?? {}).emoji}
</p>
{account.displayName}
{account.displayBalance
? ` (${account.displayBalance})`
: ""}
</button>
</div>
</>
);
})()}
</div>
Expand Down
4 changes: 2 additions & 2 deletions apps/web/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Link from "next/link";
import { ReactElement } from "react";
import useAnvil from "../hooks/useAnvil";
import useAnvil from "@/hooks/useAnvil";
import ConnectButton from "./ConnectButton";
import useChain from "../hooks/useChain";
import useChain from "@/hooks/useChain";

const Layout = ({ children }: { children: ReactElement }) => {
const { selfFund } = useAnvil();
Expand Down
49 changes: 49 additions & 0 deletions apps/web/components/Map/AccountMarker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Marker } from "react-map-gl";
import { usePosition } from "@/hooks/usePosition";
import { useEffect } from "react";
import { emojiAvatarForAddress } from "@/services/rainbow";
import { MapPinIcon } from "@heroicons/react/24/outline";
import { useAccount } from "wagmi";
import classNames from "classnames";

const AccountMarker = () => {
const { address } = useAccount();
const {
updatePosition,
position: { latitude, longitude },
} = usePosition();

useEffect(() => {
updatePosition(latitude, longitude);
}, []);

return (
<Marker
longitude={longitude}
latitude={latitude}
onDragEnd={({ lngLat: { lat, lng } }) => {
updatePosition(lat, lng);
}}
anchor="center"
draggable
>
<div
className={classNames(
"w-11 h-11 flex flex-row items-center justify-center rounded-md"
)}
style={{
background: address ? emojiAvatarForAddress(address).color : "white",
boxShadow: "0 0 0 2px #fff",
}}
>
{address ? (
<p className="text-xl">{emojiAvatarForAddress(address).emoji}</p>
) : (
<MapPinIcon className="w-6 h-6 text-gray-800" />
)}
</div>
</Marker>
);
};

export default AccountMarker;
26 changes: 26 additions & 0 deletions apps/web/components/Map/GeohashLayer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Source, FillLayer, Layer } from "react-map-gl";
import { usePosition } from "@/hooks/usePosition";

const GeohashLayer = () => {
const {
position: { feature },
} = usePosition();

const layerStyle: FillLayer = {
id: "data",
type: "fill",
paint: {
"fill-color": "black",
"fill-opacity": 0.2,
"fill-outline-color": "black",
},
};

return (
<Source id="data" type="geojson" data={feature}>
<Layer {...layerStyle} />
</Source>
);
};

export default GeohashLayer;
32 changes: 32 additions & 0 deletions apps/web/components/Map/Map.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Map from "react-map-gl";
import { commonLocations } from "@/services/constants";
import GeohashLayer from "./GeohashLayer";
import AccountMarker from "./AccountMarker";

const token = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN;

const AppMap = () => {
return (
<Map
mapboxAccessToken={token}
projection={{ name: "globe" }}
initialViewState={commonLocations.paris}
style={{
width: "100%",
height: "100%",
}}
interactiveLayerIds={["data"]}
mapStyle="mapbox://styles/nezz0746/closnc6ke00qa01nz5uvf7yad"
>
<div className="bg-white flex flex-row gap-2 items-center p-2 absolute top-[6px] left-[6px] border">
<p className="font-display font-bold tracking-tight text-xl">
Ensemble
</p>
</div>
<AccountMarker />
<GeohashLayer />
</Map>
);
};

export default AppMap;
50 changes: 50 additions & 0 deletions apps/web/components/SidePannel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { usePosition } from "@/hooks/usePosition";
import ConnectButton from "./ConnectButton";

const SidePannel = () => {
const { position, setPrecision } = usePosition();
return (
<>
<div>
<p className="text-lg font-bold">
<ConnectButton />
</p>
</div>
<div className="flex flex-col gap-4">
<div className="flex flex-row items-center justify-between font-display">
<p>Geohash</p>
<p>{position.geohash}</p>
</div>
<div className="font-display flex flex-row justify-between gap-4">
<p>Precision</p>
<div className="flex-grow ml-10">
<input
type="range"
min={2}
max={6}
value={position.precision}
onChange={(e) => {
setPrecision(parseInt(e.target.value));
}}
className="range range-xs"
step={1}
/>
<div className="w-full flex justify-between text-xs px-2">
<span>2</span>
<span>3</span>
<span>4</span>
<span>5</span>
<span>6</span>
</div>
</div>
</div>

<button className="btn btn-outline cursor-not-allowed w-full rounded-none">
<p className="font-display">Move</p>
</button>
</div>
</>
);
};

export default SidePannel;
69 changes: 69 additions & 0 deletions apps/web/hooks/usePosition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { create } from "zustand";
import { produce } from "immer";
import { commonLocations } from "@/services/constants";
import { Feature, Polygon, bboxPolygon } from "@turf/turf";
import ngeohash from "ngeohash";

export type Positon = {
latitude: number;
longitude: number;
precision: number;
geohash: string;
feature: Feature<Polygon>;
};

type PositionStore = {
position: Positon;
updatePosition: (
latitude: number,
longitude: number,
precision?: number
) => void;
setPrecision: (precision: number) => void;
};

export const usePosition = create<PositionStore>((set) => ({
position: {
latitude: commonLocations.paris.latitude,
longitude: commonLocations.paris.longitude,
precision: 6,
geohash: "",
feature: {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [],
},
properties: {},
},
},
setPrecision: (precision) => {
set(
produce((state) => {
const new_hash = ngeohash.encode(
state.position.longitude,
state.position.latitude,
precision
);
state.position.geohash = new_hash;
state.position.precision = precision;
state.position.feature = bboxPolygon(ngeohash.decode_bbox(new_hash));
})
);
},
updatePosition: (latitude, longitude) => {
set(
produce((state) => {
const new_hash = ngeohash.encode(
longitude,
latitude,
state.position.precision
);
state.position.latitude = latitude;
state.position.longitude = longitude;
state.position.geohash = new_hash;
state.position.feature = bboxPolygon(ngeohash.decode_bbox(new_hash));
})
);
},
}));
Loading

0 comments on commit 3c22904

Please sign in to comment.