diff --git a/.bos-loader.toml b/.bos-loader.toml index 8afdb26..f580ee6 100644 --- a/.bos-loader.toml +++ b/.bos-loader.toml @@ -1,3 +1,4 @@ paths = [ - { account = "monkeypatcher.near", path = "./src" }, + { account = "bwe-demos.near", path = "./src" }, + { account = "bwe.near", path = "./ignore" }, ] diff --git a/.github/workflows/deploy-mainnet.yml b/.github/workflows/deploy-mainnet.yml index 0330e8b..a703e3f 100644 --- a/.github/workflows/deploy-mainnet.yml +++ b/.github/workflows/deploy-mainnet.yml @@ -4,11 +4,39 @@ on: branches: [main] concurrency: main # do not allow parallel execution jobs: - deploy-mainnet: - uses: bos-cli-rs/bos-cli-rs/.github/workflows/deploy-mainnet.yml@master - with: - deploy-account-address: monkeypatcher.near - signer-account-address: monkeypatcher.near - signer-public-key: ed25519:2Qhd4nhX8DkRzz7VVWsT8f5L1xNuh9ZmECsfbRBBTBoq - secrets: - SIGNER_PRIVATE_KEY: ${{ secrets.SIGNER_PRIVATE_KEY }} + # We want to leverage the reusable workflow from bos cli as shown here, but + # we need to temporarily use a modified version of that in order to support + # tsx file deployment + # + # deploy-mainnet: + # uses: bos-cli-rs/bos-cli-rs/.github/workflows/deploy-mainnet.yml@master + # with: + # deploy-account-address: bwe-demos.near + # signer-account-address: bwe-demos.near + # signer-public-key: ed25519:8WLckTAFUtT7SADeAw4BF7tUFfgeG5ZW5kFbfi1TQZQB + # secrets: + # SIGNER_PRIVATE_KEY: ${{ secrets.SIGNER_PRIVATE_KEY }} + deploy-widgets: + runs-on: ubuntu-latest + name: Deploy widgets to social.near (mainnet) + env: + BOS_DEPLOY_ACCOUNT_ID: bwe-demos.near + BOS_SIGNER_ACCOUNT_ID: bwe-demos.near + BOS_SIGNER_PUBLIC_KEY: ed25519:8WLckTAFUtT7SADeAw4BF7tUFfgeG5ZW5kFbfi1TQZQB + BOS_SIGNER_PRIVATE_KEY: ${{ secrets.SIGNER_PRIVATE_KEY }} + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Change all .tsx files to .jsx files + run: | + find . -name "*.tsx" -exec sh -c 'mv "$1" "${1%.tsx}.jsx"' _ {} \; + + - name: Install near-social CLI + run: | + curl --proto '=https' --tlsv1.2 -LsSf https://github.com/FroVolod/bos-cli-rs/releases/download/v0.3.6/bos-cli-installer.sh | sh + + - name: Deploy widgets + run: | + bos components deploy "$BOS_DEPLOY_ACCOUNT_ID" sign-as "$BOS_SIGNER_ACCOUNT_ID" network-config mainnet sign-with-plaintext-private-key --signer-public-key "$BOS_SIGNER_PUBLIC_KEY" --signer-private-key "$BOS_SIGNER_PRIVATE_KEY" send diff --git a/src/App/DevForm.jsx b/src/App/DevForm.jsx deleted file mode 100644 index 0db355d..0000000 --- a/src/App/DevForm.jsx +++ /dev/null @@ -1,27 +0,0 @@ -const devStyles = { - header: { - display: "flex", - justifyContent: "center", - alignItems: "center", - height: "100%", - width: "100%", - fontSize: "2rem", - }, - body: { - display: "flex", - flexDirection: "column", - justifyContent: "center", - alignItems: "center", - height: "300px", - width: "100%", - }, - }; - -return ( -
- -

DEV FORM

-
-
-
-); \ No newline at end of file diff --git a/src/App/DevResultPage.jsx b/src/App/DevResultPage.jsx deleted file mode 100644 index b082285..0000000 --- a/src/App/DevResultPage.jsx +++ /dev/null @@ -1,28 +0,0 @@ -const devResultStyles = { - header: { - display: "flex", - justifyContent: "center", - alignItems: "center", - height: "100%", - width: "100%", - fontSize: "2rem", - }, - body: { - display: "flex", - flexDirection: "column", - justifyContent: "center", - alignItems: "center", - height: "300px", - width: "100%", - }, - }; - -return ( -
- -

DEV RESULT

-

Result line graph from data for dev usage.

-
-
- -) diff --git a/src/App/Footer.jsx b/src/App/Footer.jsx deleted file mode 100644 index 153d3f5..0000000 --- a/src/App/Footer.jsx +++ /dev/null @@ -1,32 +0,0 @@ -const footerStyles = { - row: { - display: "flex", - flexDirection: "row", - justifyContent: "space-around", - alignItems: "center", - padding: "10px", - backgroundColor: 'black', - color: 'white', - }, - column: { - display: "flex", - flexDirection: "column", - justifyContent: "space-around", - }, -} - -return ( -
-
-
- HOME -
-
- PROVIDER FORM -
-
- DEVELOPER FORM -
-
-
-); diff --git a/src/App/Header.jsx b/src/App/Header.jsx deleted file mode 100644 index 2aae6f5..0000000 --- a/src/App/Header.jsx +++ /dev/null @@ -1,32 +0,0 @@ -const headerStyles = { - header: { - display: "flex", - justifyContent: "center", - alignItems: "center", - height: "100%", - width: "100%", - fontSize: "2rem", - }, - row: { - display: "flex", - flexDirection: "row", - justifyContent: "space-around", - alignItems: "center", - padding: "10px", - backgroundColor: 'black', - color: 'white', - }, - column: { - display: "flex", - flexDirection: "column", - justifyContent: "space-around", - }, -} - -return ( -
-
-

BOS Services Calculator

-
-
-); diff --git a/src/App/ProviderForm.jsx b/src/App/ProviderForm.jsx deleted file mode 100644 index 46f5951..0000000 --- a/src/App/ProviderForm.jsx +++ /dev/null @@ -1,28 +0,0 @@ -const providerStyles = { - header: { - display: "flex", - justifyContent: "center", - alignItems: "center", - height: "100%", - width: "100%", - fontSize: "2rem", - }, - body: { - display: "flex", - flexDirection: "column", - justifyContent: "center", - alignItems: "center", - height: "300px", - width: "100%", - }, - }; - -return ( -
- -

PROVIDER FORM

-
-
-
-); -ß \ No newline at end of file diff --git a/src/App/ProviderResultPage.jsx b/src/App/ProviderResultPage.jsx deleted file mode 100644 index c4bf9b9..0000000 --- a/src/App/ProviderResultPage.jsx +++ /dev/null @@ -1,28 +0,0 @@ -const providerResultStyles = { - header: { - display: "flex", - justifyContent: "center", - alignItems: "center", - height: "100%", - width: "100%", - fontSize: "2rem", - }, - body: { - display: "flex", - flexDirection: "column", - justifyContent: "center", - alignItems: "center", - height: "300px", - width: "100%", - }, -}; - - -return ( -
- -

PROVIDER RESULT

-

Result line graph and info for provider form data.

-
-
-); \ No newline at end of file diff --git a/src/HelloWorld.jsx b/src/HelloWorld.jsx new file mode 100644 index 0000000..fd0f68e --- /dev/null +++ b/src/HelloWorld.jsx @@ -0,0 +1 @@ +return

Hello world

; diff --git a/src/LandingPage.jsx b/src/LandingPage.jsx new file mode 100644 index 0000000..ab114b8 --- /dev/null +++ b/src/LandingPage.jsx @@ -0,0 +1,87 @@ +const [isDebug, setIsDebug] = useState(true); +const [showMonitor, setShowMonitor] = useState(true); + +const buildUrl = (componentPath) => { + return `/${componentPath}?isDebug=${isDebug}&showMonitor=${showMonitor}`; +}; + +return ( +
+
+ + + Bootstrap + + + BOS Web Engine + +
+
+

Get Started

+

+ Click the links below to try out demo components +

+ +
+ +
+
+ +
+

Components

+
+ setIsDebug(!isDebug)} + /> + +
+
+ setShowMonitor(!showMonitor)} + /> + +
+ +
+
+
+
+); diff --git a/src/Posts/Account.tsx b/src/Posts/Account.tsx new file mode 100644 index 0000000..4e36d53 --- /dev/null +++ b/src/Posts/Account.tsx @@ -0,0 +1,93 @@ +interface Profile { + description?: string; + name?: string; + image?: { + ipfs_cid?: string; + url?: string; + }; + tags?: string[]; +} + +const defaults = { + name: props.account, + image: { + ipfs_cid: "bafkreibiyqabm3kl24gcb2oegb7pmwdi6wwrpui62iwb44l7uomnn3lhbi", + }, +}; + +const [profile, setProfile] = useState(); + +const fetchProfile = async () => { + try { + const response = await fetch("https://api.near.social/get", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + keys: [`${props.account}/profile/**`], + }), + }); + if (!response.ok) throw new Error("profile fetch error"); + + let p: Profile = (await response.json())?.[props.account]?.profile; + // if (!p && response.ok) { + // // defaults + // p = { + // name: props.account, + // image: defaultImage, + // }; + // } + // debugger; + + p = { ...defaults, ...p }; + console.log(p); + setProfile(p); + } catch (err) { + console.log("profile fetch error", err); + } +}; + +useEffect(() => { + fetchProfile(); +}, []); + +// @ts-expect-error +return profile ? ( +
+
+ +
+
{profile.name}
+
+) : ( + <>... +); diff --git a/src/Posts/Feed.tsx b/src/Posts/Feed.tsx new file mode 100644 index 0000000..d6ebab7 --- /dev/null +++ b/src/Posts/Feed.tsx @@ -0,0 +1,249 @@ +const GRAPHQL_ENDPOINT = "https://near-queryapi.api.pagoda.co"; + +const [sort, setSort] = useState(""); +const [loading, setLoading] = useState(false); +const [posts, setPosts] = useState([]); + +interface PostsResponse { + data: { + dataplatform_near_social_feed_moderated_posts: Post[]; + dataplatform_near_social_feed_moderated_posts_aggregate: { + aggregate: { + count: number; + }; + }; + }; +} + +interface Post { + account_id: string; + block_height: number; + block_timestamp: number; + content: string; + receipt_id: string; + accounts_liked: string[]; + last_comment_timestamp: number; + comments: { + account_id: string; + block_height: number; + block_timestamp: string; + content: string; + }[]; + verifications: { + human_provider: string; + human_valid_until: string; + human_verification_level: string; + }[]; +} + +const a = 1; +console.log(a); + +async function fetchGraphQL( + operationsDoc, + operationName, + variables +): Promise { + const response = await fetch(`${GRAPHQL_ENDPOINT}/v1/graphql`, { + method: "POST", + headers: { "x-hasura-role": "dataplatform_near" }, + body: JSON.stringify({ + query: operationsDoc, + variables: variables, + operationName: operationName, + }), + }); + const result: PostsResponse = await response.json(); + return result; +} + +const createQuery = (type) => { + let querySortOption = ""; + switch (sort) { + case "recentcommentdesc": + querySortOption = `{ last_comment_timestamp: desc_nulls_last },`; + break; + // More options... + default: + querySortOption = ""; + } + + let queryFilter = ""; + // switch (type) { + // case "following": + // let queryAccountsString = accountsFollowing + // .map((account) => `"${account}"`) + // .join(", "); + // queryFilter = `account_id: { _in: [${queryAccountsString}]}`; + // break; + // // More options... + // default: + // queryFilter = ""; + // } + + const indexerQueries = ` +query GetPostsQuery($offset: Int, $limit: Int) { + dataplatform_near_social_feed_moderated_posts(order_by: [${querySortOption} { block_height: desc }], offset: $offset, limit: $limit) { + account_id + block_height + block_timestamp + content + receipt_id + accounts_liked + last_comment_timestamp + comments(order_by: {block_height: asc}) { + account_id + block_height + block_timestamp + content + } + verifications { + human_provider + human_valid_until + human_verification_level + } + } + dataplatform_near_social_feed_moderated_posts_aggregate { + aggregate { + count + } + } +} +query GetFollowingPosts($offset: Int, $limit: Int) { + dataplatform_near_social_feed_moderated_posts(where: {${queryFilter}}, order_by: [{ block_height: desc }], offset: $offset, limit: $limit) { + account_id + block_height + block_timestamp + content + receipt_id + accounts_liked + last_comment_timestamp + comments(order_by: {block_height: asc}) { + account_id + block_height + block_timestamp + content + } + verifications { + human_provider + human_valid_until + human_verification_level + } + + } + dataplatform_near_social_feed_moderated_posts_aggregate(where: {${queryFilter}}) { + aggregate { + count + } + } +} +`; + return indexerQueries; +}; + +const loadMorePosts = () => { + // const queryName = + // state.selectedTab === "following" ? "GetFollowingPosts" : "GetPostsQuery"; + const queryName = "GetPostsQuery"; + // const type = state.selectedTab; + const type = null; + + // if (state.selectedTab === "following" && !accountsFollowing) { + // return; + // } + + // State.update({ + // isLoading: true, + // }); + setLoading(true); + + fetchGraphQL(createQuery(type), queryName, { + offset: posts.length, + // limit: LIMIT, + limit: 10, + }).then(({ data }) => { + // if (result.status === 200 && result.body) { + // if (result.body.errors) { + // console.log("error:", result.body.errors); + // return; + // } + // let data = result.body.data; + if (data) { + const newPosts = data.dataplatform_near_social_feed_moderated_posts; + const postsCountLeft = + data.dataplatform_near_social_feed_moderated_posts_aggregate.aggregate + .count; + if (newPosts.length > 0) { + // let filteredPosts = newPosts.filter((i) => !shouldFilter(i)); + // filteredPosts = filteredPosts.map((post) => { + // const prevComments = post.comments; + // const filteredComments = prevComments.filter( + // (comment) => !shouldFilter(comment) + // ); + // post.comments = filteredComments; + // return post; + // }); + let filteredPosts = newPosts; + + // State.update({ + // isLoading: false, + // posts: [...state.posts, ...filteredPosts], + // postsCountLeft, + // }); + console.log("filteredPosts", filteredPosts); + setPosts([...posts, ...filteredPosts]); + setLoading(false); + } + } + // } + }); +}; + +useEffect(() => { + loadMorePosts(); +}, []); + +// @ts-ignore +return ( +
+ +
+ {posts?.length ? ( + posts.map((post) => { + return ( +
+ + {/*
{post.comments?.length}
*/} +
+ ); + }) + ) : ( + <> + )} + {!loading && } +
+ +
+); diff --git a/src/Posts/Markdown.jsx b/src/Posts/Markdown.jsx new file mode 100644 index 0000000..d089a5f --- /dev/null +++ b/src/Posts/Markdown.jsx @@ -0,0 +1,41 @@ +// ! This component uses a package imported from a cdn which is not +// ! yet officially supported by BOS Web Engine. It is meant for +// ! demonstration purposes only and will likely change in the future. + +// Originally tried to use the `marked-react` package but custom React +// components (e.g. ) do not function under BWE. Ideally +// that would be a valid approach eventually. + +// }; +const [markdown, setMarkdown] = useState(null); +const importMarkdown = async () => { + try { + // ! This will kick of an individual net request for every instance of + // ! this component rendered to the page since iframes do not share + // ! cached resources + const markdownDyn = await import("https://esm.sh/markdown-to-jsx@7.3.2"); + console.log("markdown imported"); + setMarkdown(markdownDyn); + } catch (err) { + console.log("markdown import error", err); + } +}; + +useEffect(() => { + importMarkdown(); +}, []); + +const [rendered, setRendered] = useState(null); + +useEffect(() => { + if (!markdown) return; + + setRendered( + markdown.compiler(props.content, { + forceBlock: true, + enforceAtxHeadings: true, + }) + ); +}, [markdown]); + +return markdown ?
{rendered}
:
Loading
; diff --git a/src/Posts/Post.jsx b/src/Posts/Post.jsx new file mode 100644 index 0000000..762624e --- /dev/null +++ b/src/Posts/Post.jsx @@ -0,0 +1,41 @@ +const { content: contentRaw, account_id: author, block_timestamp } = props; + +let content = "bad content"; +console.log(contentRaw); +try { + content = JSON.parse(contentRaw).text; +} catch (e) { + console.log("error parsing content", e); +} + +// debugger; + +return ( +
+
+ + + {new Date(block_timestamp / 1000000).toLocaleString()} + +
+ +
+); diff --git a/src/Posts/Sidebar.jsx b/src/Posts/Sidebar.jsx new file mode 100644 index 0000000..5a6dd5b --- /dev/null +++ b/src/Posts/Sidebar.jsx @@ -0,0 +1,12 @@ +return ( +
+ Sidebar +
+); diff --git a/src/StateAndTrust/Root.jsx b/src/StateAndTrust/Root.jsx new file mode 100644 index 0000000..7a73fd0 --- /dev/null +++ b/src/StateAndTrust/Root.jsx @@ -0,0 +1,28 @@ +return ( + <> +
+

+ Click on any shape to trigger a state change in the root Component. + These state changes will then propagate down to its descendants as + props. +

+
+
+
+ +
+
+ +
+
+ +); diff --git a/src/StateAndTrust/SetParent.jsx b/src/StateAndTrust/SetParent.jsx new file mode 100644 index 0000000..a050251 --- /dev/null +++ b/src/StateAndTrust/SetParent.jsx @@ -0,0 +1,26 @@ +const { + circle, + square, + triangle, + updateCircle, + updateSquare, + updateTriangle, + id, +} = props; + +return ( +
+ +
+); diff --git a/src/StateAndTrust/Shape/Circle.jsx b/src/StateAndTrust/Shape/Circle.jsx new file mode 100644 index 0000000..1fdd632 --- /dev/null +++ b/src/StateAndTrust/Shape/Circle.jsx @@ -0,0 +1,22 @@ +const diameterPx = `${props.radius * 2}px`; +return ( +
+ +
+); diff --git a/src/StateAndTrust/Shape/Square.jsx b/src/StateAndTrust/Shape/Square.jsx new file mode 100644 index 0000000..e60b2d7 --- /dev/null +++ b/src/StateAndTrust/Shape/Square.jsx @@ -0,0 +1,21 @@ +const lengthPx = `${props.length}px`; +return ( +
+ +
+); diff --git a/src/StateAndTrust/Shape/Triangle.jsx b/src/StateAndTrust/Shape/Triangle.jsx new file mode 100644 index 0000000..15b7bf4 --- /dev/null +++ b/src/StateAndTrust/Shape/Triangle.jsx @@ -0,0 +1,23 @@ +return ( +
+ +
+); diff --git a/src/StateAndTrust/ShapeSet.jsx b/src/StateAndTrust/ShapeSet.jsx new file mode 100644 index 0000000..2528e63 --- /dev/null +++ b/src/StateAndTrust/ShapeSet.jsx @@ -0,0 +1,42 @@ +return ( +
+
+ +
+
+ +
+
+ +
+
+); diff --git a/src/StateAndTrust/TrustTree.jsx b/src/StateAndTrust/TrustTree.jsx new file mode 100644 index 0000000..8347d2f --- /dev/null +++ b/src/StateAndTrust/TrustTree.jsx @@ -0,0 +1,170 @@ +const [circle, setCircle] = useState("circle"); +const [triangle, setTriangle] = useState("triangle"); +const [square, setSquare] = useState("square"); + +const icons = [ + "alarm", + "archive", + "arrow-counterclockwise", + "arrow-down-right-square-fill", + "arrow-left-square-fill", + "arrow-right-short", + "arrow-up-right-square-fill", + "arrows-move", + "backspace-reverse-fill", + "badge-cc", + "badge-wc-fill", + "bank", + "basket3", + "bezier2", + "bookmark-check-fill", + "bookmark-x-fill", + "border-inner", + "box-arrow-down-left", + "box-arrow-left", + "brightness-alt-high-fill", + "bucket", + "calendar-date", + "calendar-plus", + "calendar2-date", + "calendar2-plus", + "calendar3-fill", + "camera-fill", + "card-image", + "caret-right", + "cart-dash", + "cash-coin", + "chat-left-quote", + "chat-right-quote-fill", + "chat-text", + "check2-circle", + "chevron-compact-up", + "clock", + "cloud-download-fill", + "cloud-haze-1", + "cloud-plus", + "cloud-snow-fill", + "code-square", + "cone", + "crop", + "cursor-fill", + "diagram-2-fill", + "dice-3-fill", + "display-fill", + "droplet-half", + "emoji-dizzy", + "emoji-neutral-fill", + "envelope-open", + "exclamation-octagon-fill", + "eyeglasses", + "file-break", + "file-earmark-arrow-up", + "file-earmark-code-fill", + "file-earmark-image-fill", + "file-earmark-pdf", + "file-earmark-ppt-fill", + "file-earmark-word", + "file-font", + "file-minus-fill", + "file-post", + "file-spreadsheet-fill", + "film", + "flower2", + "folder2-open", + "gear-wide", + "gift", + "grid-3x2", + "hand-index-fill", + "hdd-fill", + "heart-fill", + "hourglass-top", + "inbox", + "input-cursor-text", + "journal-medical", + "kanban", + "layer-backward", + "layout-text-sidebar-reverse", + "lightning-charge", + "list-task", + "mastodon", + "messenger", + "moon-stars-fill", + "music-player-fill", + "option", + "patch-exclamation-fill", + "pause-circle-fill", + "pentagon-half", + "person-dash", + "phone-landscape", + "pin-fill", + "plug", + "printer", + "question-octagon-fill", + "record", + "reply-all", + "save-fill", + "share-fill", + "shield-lock-fill", + "shuffle", + "skip-backward-btn-fill", + "skip-forward-btn", + "skype", + "snow3", + "sort-up", + "stack", + "stop-btn-fill", + "suit-diamond", + "sunset", + "tags", + "telephone-outbound", + "text-left", + "thermometer-sun", + "translate", + "trophy-fill", + "type-h1", + "umbrella-fill", + "vinyl-fill", + "wallet", + "window", + "x-octagon-fill", +]; + +const getRandomIcon = useCallback(() => icons[Math.floor(Math.random() * icons.length)]); +const updateCircle = useCallback(() => setCircle(getRandomIcon())); +const updateSquare = useCallback(() => setSquare(getRandomIcon())); +const updateTriangle = useCallback(() => setTriangle(getRandomIcon())); + +return ( +
+
+

{props.title}

+
+
+ + +
+
+); diff --git a/src/scc.jsx b/src/scc.jsx deleted file mode 100644 index e05b72c..0000000 --- a/src/scc.jsx +++ /dev/null @@ -1,31 +0,0 @@ -const sccStyles = { - row: { - display: "flex", - flexDirection: "row", - justifyContent: "space-around", - alignItems: "center", - padding: "10px", - }, - column: { - display: "flex", - flexDirection: "column", - justifyContent: "space-around", - }, -}; - -// - -return ( - -); \ No newline at end of file