Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TEMPLATE-REQUEST] Simple Lists #21

Open
codingshot opened this issue Oct 15, 2024 · 0 comments
Open

[TEMPLATE-REQUEST] Simple Lists #21

codingshot opened this issue Oct 15, 2024 · 0 comments
Labels
template-request Request an entire template

Comments

@codingshot
Copy link
Member

https://potlock.notion.site/Simple-Lists-120c1f4ba97e8034a772f63b0b9f2335?pvs=4

// pages/_app.js
import { useEffect, useState } from 'react';
import { WalletSelector } from '@near-wallet-selector/core';
import { setupWalletSelector } from '@near-wallet-selector/core';
import { setupNearWallet } from '@near-wallet-selector/near-wallet';
import { setupMyNearWallet } from '@near-wallet-selector/my-near-wallet';
import { ThemeProvider } from '@emotion/react';
import { Global, css } from '@emotion/react';
import { theme } from '../styles/theme';

function MyApp({ Component, pageProps }) {
const [walletSelector, setWalletSelector] = useState(null);

useEffect(() => {
const initWalletSelector = async () => {
const selector = await setupWalletSelector({
network: 'mainnet',
modules: [setupNearWallet(), setupMyNearWallet()],
});
setWalletSelector(selector);
};

initWalletSelector();

}, []);

return (

<Global
styles={cssbody { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; }}
/>
<Component {...pageProps} walletSelector={walletSelector} />

);
}

export default MyApp;

// components/Navbar.js
import styled from '@emotion/styled';
import Link from 'next/link';
import { useWalletSelector } from '../hooks/useWalletSelector';

const NavbarContainer = styled.navdisplay: flex; justify-content: space-between; align-items: center; padding: 1rem; background-color: ${props => props.theme.colors.primary};;

const NavLink = styled(Link)color: white; text-decoration: none; margin-right: 1rem;;

const LoginButton = styled.buttonbackground-color: white; color: ${props => props.theme.colors.primary}; border: none; padding: 0.5rem 1rem; cursor: pointer;;

export function Navbar() {
const { selector, accountId, signIn, signOut } = useWalletSelector();

return (


My Lists
Explore
Create List


{accountId ? (
<>
{accountId}
Sign Out
</>
) : (
Login
)}


);
}

// pages/explore.js
import { useState, useEffect } from 'react';
import styled from '@emotion/styled';
import { Navbar } from '../components/Navbar';
import { ListCard } from '../components/ListCard';
import { getLists } from '../utils/nearInteractions';

const ExploreContainer = styled.divpadding: 2rem;;

const FilterContainer = styled.divmargin-bottom: 1rem;;

const ListContainer = styled.divdisplay: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1rem;;

export default function Explore() {
const [lists, setLists] = useState([]);
const [filter, setFilter] = useState('recent');

useEffect(() => {
const fetchLists = async () => {
const fetchedLists = await getLists();
setLists(fetchedLists);
};
fetchLists();
}, []);

const sortedLists = [...lists].sort((a, b) => {
if (filter === 'recent') return b.id - a.id;
if (filter === 'upvotes') return b.upvotes - a.upvotes;
return 0;
});

return (
<>


Explore Lists



<select value={filter} onChange={(e) => setFilter(e.target.value)}>
Most Recent
Most Upvotes



{sortedLists.map((list) => (

))}


</>
);
}

// components/ListCard.js
import styled from '@emotion/styled';
import Link from 'next/link';

const Card = styled.divborder: 1px solid #ddd; border-radius: 4px; padding: 1rem;;

const Banner = styled.imgwidth: 100%; height: 100px; object-fit: cover;;

export function ListCard({ list }) {
return (

<Banner src={list.coverImgUrl || '/placeholder.jpg'} alt={list.name} />

{list.name}


Creator: {list.owner}


Upvotes: {list.upvotes}


{list.description}


Approved Projects: {list.approvedProjectsCount}


<Link href={/list/${list.id}}>View List

);
}

// pages/list/[id].js
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import styled from '@emotion/styled';
import { Navbar } from '../../components/Navbar';
import { getList, updateRegistrationStatus, upvoteList } from '../../utils/nearInteractions';
import { useWalletSelector } from '../../hooks/useWalletSelector';

const ListContainer = styled.divpadding: 2rem;;

const ItemContainer = styled.divdisplay: flex; justify-content: space-between; align-items: center; padding: 0.5rem; border-bottom: 1px solid #ddd;;

export default function ListPage() {
const router = useRouter();
const { id } = router.query;
const [list, setList] = useState(null);
const { accountId, signIn } = useWalletSelector();

useEffect(() => {
if (id) {
const fetchList = async () => {
const fetchedList = await getList(id);
setList(fetchedList);
};
fetchList();
}
}, [id]);

const handleStatusChange = async (registrationId, newStatus) => {
if (!accountId) {
alert('Please sign in to update status');
return;
}
await updateRegistrationStatus(registrationId, newStatus);
// Refetch list to update UI
const updatedList = await getList(id);
setList(updatedList);
};

const handleUpvote = async () => {
if (!accountId) {
alert('Please sign in to upvote');
return;
}
await upvoteList(id);
// Refetch list to update UI
const updatedList = await getList(id);
setList(updatedList);
};

if (!list) return

Loading...
;

return (
<>


{list.name}


{list.description}


Upvote ({list.upvotes})

Items


{list.items.map((item) => (

{item.value}
{(accountId === list.owner || list.admins.includes(accountId)) && (
<select
value={item.status}
onChange={(e) => handleStatusChange(item.id, e.target.value)}
>
Approved
Pending
Rejected
Blacklisted

)}

))}

</>
);
}

// utils/nearInteractions.js
import { connect, Contract, keyStores, WalletConnection } from 'near-api-js';

const CONTRACT_NAME = 'lists.potlock.near';

const getConfig = (env) => {
switch (env) {
case 'production':
case 'mainnet':
return {
networkId: 'mainnet',
nodeUrl: 'https://rpc.mainnet.near.org',
contractName: CONTRACT_NAME,
walletUrl: 'https://wallet.near.org',
helperUrl: 'https://helper.mainnet.near.org',
};
case 'development':
case 'testnet':
return {
networkId: 'testnet',
nodeUrl: 'https://rpc.testnet.near.org',
contractName: 'list.potlock.testnet',
walletUrl: 'https://wallet.testnet.near.org',
helperUrl: 'https://helper.testnet.near.org',
};
default:
throw Error(Unknown environment '${env}'.);
}
};

export async function initContract() {
const nearConfig = getConfig(process.env.NEAR_ENV || 'testnet');
const keyStore = new keyStores.BrowserLocalStorageKeyStore();
const near = await connect({ keyStore, ...nearConfig });
const walletConnection = new WalletConnection(near);
const contract = new Contract(walletConnection.account(), nearConfig.contractName, {
viewMethods: ['get_list', 'get_lists', 'get_registrations_for_list'],
changeMethods: ['create_list', 'update_registration', 'upvote'],
});
return { contract, walletConnection };
}

export async function getLists() {
const { contract } = await initContract();
return contract.get_lists({ from_index: 0, limit: 50 });
}

export async function getList(listId) {
const { contract } = await initContract();
return contract.get_list({ list_id: listId });
}

export async function updateRegistrationStatus(registrationId, newStatus) {
const { contract } = await initContract();
return contract.update_registration({ registration_id: registrationId, status: newStatus });
}

export async function upvoteList(listId) {
const { contract } = await initContract();
return contract.upvote({ list_id: listId });
}

// styles/theme.js
export const theme = {
colors: {
primary: '#0070f3',
secondary: '#ff4081',
background: '#f5f5f5',
text: '#333',
},
};

// hooks/useWalletSelector.js
import { useEffect, useState } from 'react';

export function useWalletSelector(selector) {
const [accountId, setAccountId] = useState(null);

useEffect(() => {
if (!selector) return;

const subscribeToWalletEvents = async () => {
  const state = selector.store.getState();
  setAccountId(state.accounts[0]?.accountId || null);

  selector.on('accountsChanged', (accounts) => {
    setAccountId(accounts[0]?.accountId || null);
  });
};

subscribeToWalletEvents();

}, [selector]);

const signIn = () => {
selector.show();
};

const signOut = () => {
selector.signOut();
};

return { selector, accountId, signIn, signOut };
}

@codingshot codingshot added the template-request Request an entire template label Oct 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
template-request Request an entire template
Projects
Status: Finding Team
Development

No branches or pull requests

1 participant