mirror of
https://github.com/aljazceru/landscape-template.git
synced 2025-12-27 11:14:33 +01:00
Merge branch 'bumi-graphql-functions'
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
.netlify
|
||||
.env
|
||||
build
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
|
||||
24
README.md
24
README.md
@@ -2,6 +2,30 @@
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
|
||||
## Database
|
||||
|
||||
Set the `DATABASE_URL` environment variable for your PostgreSQL DB. (e.g. `postgres://bumi@127.0.0.1:5432/bolt_fun_dev`)
|
||||
|
||||
### `prisma studio`
|
||||
|
||||
prisma studio runs an UI for the DB
|
||||
|
||||
### `prisma migrate dev`
|
||||
|
||||
Create a migration from the schema.prisma file
|
||||
|
||||
### `prisma migrate deploy`
|
||||
|
||||
Apply pending migrations to the database
|
||||
|
||||
|
||||
## GraphQL
|
||||
|
||||
GraphQL endpoint is available as netlify function on: `.netlify/functions/graphql`
|
||||
|
||||
Use the Apollo GraphQL Studio to to inspect the GraphQL API: [https://studio.apollographql.com/sandbox/explorer](https://studio.apollographql.com/sandbox/explorer)
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "./static/css/main.72d30049.chunk.css",
|
||||
"main.js": "./static/js/main.2388372a.chunk.js",
|
||||
"main.js.map": "./static/js/main.2388372a.chunk.js.map",
|
||||
"runtime-main.js": "./static/js/runtime-main.ce5efd86.js",
|
||||
"runtime-main.js.map": "./static/js/runtime-main.ce5efd86.js.map",
|
||||
"static/css/2.4cfadce7.chunk.css": "./static/css/2.4cfadce7.chunk.css",
|
||||
"static/js/2.416fbd02.chunk.js": "./static/js/2.416fbd02.chunk.js",
|
||||
"static/js/2.416fbd02.chunk.js.map": "./static/js/2.416fbd02.chunk.js.map",
|
||||
"static/js/3.f6cea3fe.chunk.js": "./static/js/3.f6cea3fe.chunk.js",
|
||||
"static/js/3.f6cea3fe.chunk.js.map": "./static/js/3.f6cea3fe.chunk.js.map",
|
||||
"index.html": "./index.html",
|
||||
"static/css/2.4cfadce7.chunk.css.map": "./static/css/2.4cfadce7.chunk.css.map",
|
||||
"static/css/main.72d30049.chunk.css.map": "./static/css/main.72d30049.chunk.css.map",
|
||||
"static/js/2.416fbd02.chunk.js.LICENSE.txt": "./static/js/2.416fbd02.chunk.js.LICENSE.txt",
|
||||
"static/media/styles.css": "./static/media/revicons.e8746a62.woff"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/js/runtime-main.ce5efd86.js",
|
||||
"static/css/2.4cfadce7.chunk.css",
|
||||
"static/js/2.416fbd02.chunk.js",
|
||||
"static/css/main.72d30049.chunk.css",
|
||||
"static/js/main.2388372a.chunk.js"
|
||||
]
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<svg width="93" height="92" viewBox="0 0 93 92" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="6.5" y="6" width="80" height="80" rx="37.625" fill="#E1DBFF"/>
|
||||
<path d="M39 49C39 49 40 48 43 48C46 48 48 50 51 50C54 50 55 49 55 49V37C55 37 54 38 51 38C48 38 46 36 43 36C40 36 39 37 39 37V49ZM39 49V56" stroke="#7B61FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<rect x="6.5" y="6" width="80" height="80" rx="37.625" stroke="#EDEAFF" stroke-width="12"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 491 B |
@@ -1,5 +0,0 @@
|
||||
<svg width="93" height="92" viewBox="0 0 93 92" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="6.5" y="6" width="80" height="80" rx="37.625" fill="#F7D154"/>
|
||||
<path d="M47.5 36L37.5 48H46.5L45.5 56L55.5 44H46.5L47.5 36Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<rect x="6.5" y="6" width="80" height="80" rx="37.625" stroke="#FFF1C4" stroke-width="12"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 410 B |
@@ -1,5 +0,0 @@
|
||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="4" y="4" width="48" height="48" rx="24" fill="#F7D154"/>
|
||||
<path d="M29 18L19 30H28L27 38L37 26H28L29 18Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<rect x="4" y="4" width="48" height="48" rx="24" stroke="#FFF1C3" stroke-width="8"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 383 B |
@@ -1,5 +0,0 @@
|
||||
<svg width="91" height="92" viewBox="0 0 91 92" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="5.5" y="6" width="80" height="80" rx="37.625" fill="#D1FADF"/>
|
||||
<path d="M58.5625 44.8887V46.125C58.5609 49.0227 57.6225 51.8422 55.8875 54.163C54.1525 56.4839 51.7138 58.1817 48.935 59.0033C46.1562 59.8249 43.1863 59.7263 40.4682 58.7221C37.7501 57.7179 35.4294 55.8619 33.8522 53.431C32.2751 51.0001 31.526 48.1246 31.7166 45.2331C31.9073 42.3417 33.0275 39.5894 34.9102 37.3867C36.7929 35.1839 39.3372 33.6488 42.1636 33.0102C44.9901 32.3716 47.9472 32.6637 50.5941 33.8431M58.5625 35.375L45.125 48.8259L41.0938 44.7947" stroke="#039855" stroke-width="2.6875" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<rect x="5.5" y="6" width="80" height="80" rx="37.625" stroke="#ECFDF3" stroke-width="10.75"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 818 B |
Binary file not shown.
|
Before Width: | Height: | Size: 40 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1 +0,0 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="A place for creators to share theier lightning apps"/><link rel="manifest" href="./manifest.json"/><title>makers.bolt.fun</title><link href="./static/css/2.4cfadce7.chunk.css" rel="stylesheet"><link href="./static/css/main.72d30049.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,a,i=r[0],c=r[1],l=r[2],s=0,p=[];s<i.length;s++)a=i[s],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&p.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);p.length;)p.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var c=t[i];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{3:"f6cea3fe"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="./",a.oe=function(e){throw console.error(e),e};var i=this["webpackJsonpmakers.bolt.fun"]=this["webpackJsonpmakers.bolt.fun"]||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var l=0;l<i.length;l++)r(i[l]);var f=c;t()}([])</script><script src="./static/js/2.416fbd02.chunk.js"></script><script src="./static/js/main.2388372a.chunk.js"></script></body></html>
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"short_name": "makers.bolt.fun",
|
||||
"name": "Makers.Bolt.Fun",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*/
|
||||
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
|
||||
/**
|
||||
* React Router DOM v6.0.2
|
||||
*
|
||||
* Copyright (c) Remix Software Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* React Router v6.0.2
|
||||
*
|
||||
* Copyright (c) Remix Software Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/** @license React v0.20.2
|
||||
* scheduler.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v16.13.1
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-dom.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-jsx-runtime.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
||||
(this["webpackJsonpmakers.bolt.fun"]=this["webpackJsonpmakers.bolt.fun"]||[]).push([[3],{201:function(t,e,n){"use strict";n.r(e),n.d(e,"getCLS",(function(){return d})),n.d(e,"getFCP",(function(){return S})),n.d(e,"getFID",(function(){return k})),n.d(e,"getLCP",(function(){return F})),n.d(e,"getTTFB",(function(){return C}));var i,a,r,o,u=function(t,e){return{name:t,value:void 0===e?-1:e,delta:0,entries:[],id:"v1-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},c=function(t,e){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){if("first-input"===t&&!("PerformanceEventTiming"in self))return;var n=new PerformanceObserver((function(t){return t.getEntries().map(e)}));return n.observe({type:t,buffered:!0}),n}}catch(t){}},f=function(t,e){var n=function n(i){"pagehide"!==i.type&&"hidden"!==document.visibilityState||(t(i),e&&(removeEventListener("visibilitychange",n,!0),removeEventListener("pagehide",n,!0)))};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},s=function(t){addEventListener("pageshow",(function(e){e.persisted&&t(e)}),!0)},m="function"==typeof WeakSet?new WeakSet:new Set,p=function(t,e,n){var i;return function(){e.value>=0&&(n||m.has(e)||"hidden"===document.visibilityState)&&(e.delta=e.value-(i||0),(e.delta||void 0===i)&&(i=e.value,t(e)))}},d=function(t,e){var n,i=u("CLS",0),a=function(t){t.hadRecentInput||(i.value+=t.value,i.entries.push(t),n())},r=c("layout-shift",a);r&&(n=p(t,i,e),f((function(){r.takeRecords().map(a),n()})),s((function(){i=u("CLS",0),n=p(t,i,e)})))},v=-1,l=function(){return"hidden"===document.visibilityState?0:1/0},h=function(){f((function(t){var e=t.timeStamp;v=e}),!0)},g=function(){return v<0&&(v=l(),h(),s((function(){setTimeout((function(){v=l(),h()}),0)}))),{get timeStamp(){return v}}},S=function(t,e){var n,i=g(),a=u("FCP"),r=function(t){"first-contentful-paint"===t.name&&(f&&f.disconnect(),t.startTime<i.timeStamp&&(a.value=t.startTime,a.entries.push(t),m.add(a),n()))},o=performance.getEntriesByName("first-contentful-paint")[0],f=o?null:c("paint",r);(o||f)&&(n=p(t,a,e),o&&r(o),s((function(i){a=u("FCP"),n=p(t,a,e),requestAnimationFrame((function(){requestAnimationFrame((function(){a.value=performance.now()-i.timeStamp,m.add(a),n()}))}))})))},y={passive:!0,capture:!0},E=new Date,w=function(t,e){i||(i=e,a=t,r=new Date,b(removeEventListener),L())},L=function(){if(a>=0&&a<r-E){var t={entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+a};o.forEach((function(e){e(t)})),o=[]}},T=function(t){if(t.cancelable){var e=(t.timeStamp>1e12?new Date:performance.now())-t.timeStamp;"pointerdown"==t.type?function(t,e){var n=function(){w(t,e),a()},i=function(){a()},a=function(){removeEventListener("pointerup",n,y),removeEventListener("pointercancel",i,y)};addEventListener("pointerup",n,y),addEventListener("pointercancel",i,y)}(e,t):w(e,t)}},b=function(t){["mousedown","keydown","touchstart","pointerdown"].forEach((function(e){return t(e,T,y)}))},k=function(t,e){var n,r=g(),d=u("FID"),v=function(t){t.startTime<r.timeStamp&&(d.value=t.processingStart-t.startTime,d.entries.push(t),m.add(d),n())},l=c("first-input",v);n=p(t,d,e),l&&f((function(){l.takeRecords().map(v),l.disconnect()}),!0),l&&s((function(){var r;d=u("FID"),n=p(t,d,e),o=[],a=-1,i=null,b(addEventListener),r=v,o.push(r),L()}))},F=function(t,e){var n,i=g(),a=u("LCP"),r=function(t){var e=t.startTime;e<i.timeStamp&&(a.value=e,a.entries.push(t)),n()},o=c("largest-contentful-paint",r);if(o){n=p(t,a,e);var d=function(){m.has(a)||(o.takeRecords().map(r),o.disconnect(),m.add(a),n())};["keydown","click"].forEach((function(t){addEventListener(t,d,{once:!0,capture:!0})})),f(d,!0),s((function(i){a=u("LCP"),n=p(t,a,e),requestAnimationFrame((function(){requestAnimationFrame((function(){a.value=performance.now()-i.timeStamp,m.add(a),n()}))}))}))}},C=function(t){var e,n=u("TTFB");e=function(){try{var e=performance.getEntriesByType("navigation")[0]||function(){var t=performance.timing,e={entryType:"navigation",startTime:0};for(var n in t)"navigationStart"!==n&&"toJSON"!==n&&(e[n]=Math.max(t[n]-t.navigationStart,0));return e}();if(n.value=n.delta=e.responseStart,n.value<0)return;n.entries=[e],t(n)}catch(t){}},"complete"===document.readyState?setTimeout(e,0):addEventListener("pageshow",e)}}}]);
|
||||
//# sourceMappingURL=3.f6cea3fe.chunk.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
||||
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],l=r[2],s=0,p=[];s<i.length;s++)a=i[s],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&p.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);p.length;)p.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var c=t[i];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{3:"f6cea3fe"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="./",a.oe=function(e){throw console.error(e),e};var i=this["webpackJsonpmakers.bolt.fun"]=this["webpackJsonpmakers.bolt.fun"]||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var l=0;l<i.length;l++)r(i[l]);var f=c;t()}([]);
|
||||
//# sourceMappingURL=runtime-main.ce5efd86.js.map
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
37
functions/graphql/index.mjs
Normal file
37
functions/graphql/index.mjs
Normal file
@@ -0,0 +1,37 @@
|
||||
const { ApolloServer } = require("apollo-server-lambda");
|
||||
const { PrismaClient } = require("@prisma/client");
|
||||
|
||||
const resolvers = require("./resolvers");
|
||||
const typeDefs = require("./typeDefs");
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
const server = new ApolloServer({
|
||||
resolvers,
|
||||
typeDefs,
|
||||
context: () => {
|
||||
return {
|
||||
prisma,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const apolloHandler = server.createHandler({
|
||||
cors: {
|
||||
origin: "*",
|
||||
credentials: true,
|
||||
},
|
||||
});
|
||||
|
||||
// https://github.com/vendia/serverless-express/issues/427#issuecomment-924580007
|
||||
const handler = (event, context, ...args) => {
|
||||
return apolloHandler(
|
||||
{
|
||||
...event,
|
||||
requestContext: context,
|
||||
},
|
||||
context,
|
||||
...args
|
||||
);
|
||||
};
|
||||
|
||||
exports.handler = handler;
|
||||
225
functions/graphql/resolvers.js
Normal file
225
functions/graphql/resolvers.js
Normal file
@@ -0,0 +1,225 @@
|
||||
const { parsePaymentRequest } = require("invoices");
|
||||
const axios = require("axios");
|
||||
const { createHash } = require("crypto");
|
||||
|
||||
function hexToUint8Array(hexString) {
|
||||
const match = hexString.match(/.{1,2}/g);
|
||||
if (match) {
|
||||
return new Uint8Array(match.map((byte) => parseInt(byte, 16)));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: generaly validate LNURL responses
|
||||
// get lnurl params
|
||||
function getLnurlDetails(lnurl) {
|
||||
return axios.get(lnurl);
|
||||
}
|
||||
|
||||
// parse lightning address and return a url that can be
|
||||
// used in a request
|
||||
function lightningAddressToLnurl(lightning_address) {
|
||||
const [name, domain] = lightning_address.split("@");
|
||||
return `https://${domain}/.well-known/lnurlp/${name}`;
|
||||
}
|
||||
|
||||
// when pressing tip or selecting an amount.
|
||||
// this is used for caching so the frontend doesnt
|
||||
// have to make an additional http request to get
|
||||
// the callback url for future visits
|
||||
async function getLnurlCallbackUrl(lightning_address) {
|
||||
return getLnurlDetails(lightningAddressToLnurl(lightning_address)).then(
|
||||
(response) => {
|
||||
return response.data.callback;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function getPaymetRequestForProject(project, amount_in_sat) {
|
||||
// # NOTE: CACHING LNURL CALLBACK URLS + PARAMETERS
|
||||
// LNURL flows have a lot of back and forth and can impact
|
||||
// the load time for your application users.
|
||||
// You may consider caching the callback url, or resolved
|
||||
// parameters but be mindful of this.
|
||||
// The LNURL service provider can change the callback url
|
||||
// details or the paramters that is returned we must be
|
||||
// careful when trying to optimise the amount of
|
||||
// requests so be mindful of this when you are storing
|
||||
// these items.
|
||||
let lnurlCallbackUrl = project.lnurl_callback_url;
|
||||
const amount = amount_in_sat * 1000; // msats
|
||||
if (!lnurlCallbackUrl) {
|
||||
lnurlCallbackUrl = await getLnurlCallbackUrl(project.lightning_address);
|
||||
}
|
||||
return axios
|
||||
.get(lnurlCallbackUrl, { params: { amount } })
|
||||
.then((prResponse) => {
|
||||
console.log(prResponse.data);
|
||||
return prResponse.data.pr;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
allCategories: async (_source, args, context) => {
|
||||
return context.prisma.category.findMany({
|
||||
orderBy: { title: "desc" },
|
||||
include: {
|
||||
project: {
|
||||
take: 5,
|
||||
orderBy: { votes_count: "desc" },
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
getCategory: async (_source, args, context) => {
|
||||
return context.prisma.category.findUnique({
|
||||
where: { id: args.id },
|
||||
include: {
|
||||
project: {
|
||||
take: 5,
|
||||
orderBy: { votes_count: "desc" },
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
newProjects: async (_source, args, context) => {
|
||||
const take = args.take || 50;
|
||||
const skip = args.skip || 0;
|
||||
return context.prisma.project.findMany({
|
||||
orderBy: { created_at: "desc" },
|
||||
include: { category: true },
|
||||
skip,
|
||||
take,
|
||||
});
|
||||
},
|
||||
allProjects: async (_source, args, context) => {
|
||||
const take = args.take || 50;
|
||||
const skip = args.skip || 0;
|
||||
return context.prisma.project.findMany({
|
||||
orderBy: { votes_count: "desc" },
|
||||
include: { category: true },
|
||||
skip,
|
||||
take,
|
||||
});
|
||||
},
|
||||
projectsByCategory: async (_source, args, context) => {
|
||||
const take = args.take || 50;
|
||||
const skip = args.skip || 0;
|
||||
const categoryId = args.category_id;
|
||||
return context.prisma.project.findMany({
|
||||
where: { category_id: categoryId },
|
||||
orderBy: { votes_count: "desc" },
|
||||
include: { category: true },
|
||||
skip,
|
||||
take,
|
||||
});
|
||||
},
|
||||
getProject: async (_source, args, context) => {
|
||||
return context.prisma.project.findUnique({
|
||||
where: {
|
||||
id: args.id,
|
||||
},
|
||||
include: { category: true },
|
||||
});
|
||||
},
|
||||
getLnurlDetailsForProject: async (_source, args, context) => {
|
||||
const project = await context.prisma.project.findUnique({
|
||||
where: {
|
||||
id: args.project_id,
|
||||
},
|
||||
});
|
||||
const lnurlDetails = await getLnurlDetails(
|
||||
lightningAddressToLnurl(project.lightning_address)
|
||||
);
|
||||
if (
|
||||
!lnurlDetails.data ||
|
||||
lnurlDetails.data.status.toLowerCase() !== "ok"
|
||||
) {
|
||||
console.error(lnurlDetails.data);
|
||||
throw new Error("Recipient not available");
|
||||
}
|
||||
|
||||
// cache the callback URL
|
||||
await context.prisma.project.update({
|
||||
where: { id: project.id },
|
||||
data: {
|
||||
lnurl_callback_url: lnurlDetails.data.callback,
|
||||
},
|
||||
});
|
||||
// # SENDING MESSAGES TO THE PROJECT OWNER USING LNURL-PAY COMMENTS
|
||||
// comments in lnurl pay can be used to send a private message or
|
||||
// post on the projcet owners site. could even be used for advertising
|
||||
// or tip messages. or can even be a pay to respond / paid advise
|
||||
return {
|
||||
minSendable: parseInt(lnurlDetails.data.minSendable) / 1000,
|
||||
maxSendable: parseInt(lnurlDetails.data.maxSendable) / 1000,
|
||||
metadata: lnurlDetails.data.metadata,
|
||||
commentAllowed: lnurlDetails.data.commentAllowed,
|
||||
};
|
||||
},
|
||||
},
|
||||
Mutation: {
|
||||
// votes are like BTC Pay Server / ecommerce store "orders"
|
||||
// the amount that needs to be paid is recorded, and the service
|
||||
// awaits the payment. once the payment is made then a verification
|
||||
// is necessary (confirmVote) since lnurl does not give a response
|
||||
// for a successful payment. the payment is asyncronmous so we
|
||||
// dont know when its get paid, and lnurl does not provide a webhook
|
||||
// setup, or something for us to poll or subscribe to so we determine
|
||||
// if a invoice is paid.
|
||||
// the way that we implemented this check is that the client needs
|
||||
// to provide the preimage for their vote to be counted on the site.
|
||||
//
|
||||
vote: async (_source, args, context) => {
|
||||
const project = await context.prisma.project.findUnique({
|
||||
where: { id: args.project_id },
|
||||
});
|
||||
const pr = await getPaymetRequestForProject(project, args.amount_in_sat);
|
||||
const invoice = parsePaymentRequest({ request: pr });
|
||||
return context.prisma.vote.create({
|
||||
data: {
|
||||
project_id: project.id,
|
||||
amount_in_sat: args.amount_in_sat,
|
||||
payment_request: pr,
|
||||
payment_hash: invoice.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
confirmVote: async (_source, args, context) => {
|
||||
const paymentHash = createHash("sha256")
|
||||
.update(hexToUint8Array(args.preimage))
|
||||
.digest("hex");
|
||||
// look for a vote for the payment request and the calculated payment hash
|
||||
const vote = await context.prisma.vote.findFirst({
|
||||
where: {
|
||||
payment_request: args.payment_request,
|
||||
payment_hash: paymentHash,
|
||||
},
|
||||
});
|
||||
// if we find a vote it means the preimage is correct and we update the vote and mark it as paid
|
||||
// can we write this nicer?
|
||||
if (vote) {
|
||||
const project = await context.prisma.project.findUnique({
|
||||
where: { id: vote.project_id },
|
||||
});
|
||||
// count up votes cache
|
||||
await context.prisma.project.update({
|
||||
where: { id: project.id },
|
||||
data: {
|
||||
votes_count: (project.votes_count = vote.amount_in_sat),
|
||||
},
|
||||
});
|
||||
// return the current vote
|
||||
return context.prisma.vote.update({
|
||||
where: { id: vote.id },
|
||||
data: {
|
||||
paid: true,
|
||||
preimage: args.preimage,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
throw new Error("Invalid preimage");
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
50
functions/graphql/typeDefs.js
Normal file
50
functions/graphql/typeDefs.js
Normal file
@@ -0,0 +1,50 @@
|
||||
const { gql } = require("apollo-server-lambda");
|
||||
|
||||
module.exports = gql`
|
||||
type Project {
|
||||
id: Int!
|
||||
cover_image: String!
|
||||
thumbnail_image: String!
|
||||
title: String!
|
||||
website: String!
|
||||
lightning_address: String!
|
||||
votes_count: Int!
|
||||
category: Category!
|
||||
}
|
||||
|
||||
type Category {
|
||||
id: Int!
|
||||
title: String!
|
||||
project: [Project]
|
||||
}
|
||||
|
||||
type Vote {
|
||||
id: Int!
|
||||
project: Project!
|
||||
amount_in_sat: Int!
|
||||
payment_request: String!
|
||||
payment_hash: String!
|
||||
paid: Boolean!
|
||||
}
|
||||
|
||||
type LnurlDetails {
|
||||
minSendable: Int
|
||||
maxSendable: Int
|
||||
metadata: String
|
||||
commentAllowed: Int
|
||||
}
|
||||
|
||||
type Query {
|
||||
allProjects(skip: Int, take: Int): [Project]!
|
||||
newProjects(skip: Int, take: Int): [Project]!
|
||||
projectsByCategory(category_id: Int!, skip: Int, take: Int): [Project]!
|
||||
getProject(id: Int!): Project!
|
||||
allCategories: [Category]!
|
||||
getCategory(id: Int!): Category!
|
||||
getLnurlDetailsForProject(project_id: Int!): LnurlDetails!
|
||||
}
|
||||
type Mutation {
|
||||
vote(project_id: Int!, amount_in_sat: Int!): Vote!
|
||||
confirmVote(payment_request: String!, preimage: String!): Vote!
|
||||
}
|
||||
`;
|
||||
3
netlify.toml
Normal file
3
netlify.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[build]
|
||||
functions = "functions" # netlify-lambda builds to this folder AND Netlify reads functions from here
|
||||
publish = "build" # create-react-app builds to this folder, Netlify should serve all these files statically
|
||||
33142
package-lock.json
generated
33142
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -5,6 +5,7 @@
|
||||
"homepage": ".",
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.5.5",
|
||||
"@prisma/client": "3.5.0",
|
||||
"@reduxjs/toolkit": "^1.6.2",
|
||||
"@testing-library/jest-dom": "^5.15.0",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
@@ -13,10 +14,16 @@
|
||||
"@types/node": "^12.20.36",
|
||||
"@types/react": "^17.0.33",
|
||||
"@types/react-dom": "^17.0.10",
|
||||
"apollo-server": "^3.5.0",
|
||||
"apollo-server-lambda": "^3.5.0",
|
||||
"axios": "^0.24.0",
|
||||
"framer-motion": "^5.3.0",
|
||||
"graphql": "^16.0.1",
|
||||
"invoices": "^2.0.2",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"prisma": "3.5.0",
|
||||
"react": "^17.0.2",
|
||||
"react-confetti": "^6.0.1",
|
||||
"react-copy-to-clipboard": "^5.0.4",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-icons": "^4.3.1",
|
||||
@@ -27,8 +34,10 @@
|
||||
"react-responsive-carousel": "^3.2.22",
|
||||
"react-router-dom": "^6.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"react-use": "^17.3.1",
|
||||
"typescript": "^4.4.4",
|
||||
"web-vitals": "^1.1.2"
|
||||
"web-vitals": "^1.1.2",
|
||||
"webln": "^0.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "craco start",
|
||||
@@ -40,7 +49,14 @@
|
||||
"only-deploy": "gh-pages -d build",
|
||||
"storybook": "start-storybook -p 6006 -s public",
|
||||
"build-storybook": "build-storybook -s public",
|
||||
"codegen": "graphql-codegen --config codegen.yml"
|
||||
"db:init": "prisma migrate dev",
|
||||
"db:reset": "prisma migrate reset",
|
||||
"db:seed": "prisma db seed",
|
||||
"db:gui": "prisma studio",
|
||||
"netlify:start": "netlify dev"
|
||||
},
|
||||
"prisma": {
|
||||
"seed": "node prisma/seed.js"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
@@ -84,6 +100,7 @@
|
||||
"@types/react-copy-to-clipboard": "^5.0.2",
|
||||
"autoprefixer": "^9.8.8",
|
||||
"gh-pages": "^3.2.3",
|
||||
"netlify-cli": "^8.0.3",
|
||||
"postcss": "^7.0.39",
|
||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17",
|
||||
"@graphql-codegen/typescript": "2.4.1",
|
||||
|
||||
41
prisma/migrations/20211128173513_init/migration.sql
Normal file
41
prisma/migrations/20211128173513_init/migration.sql
Normal file
@@ -0,0 +1,41 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Category" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Category_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Project" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"website" TEXT NOT NULL,
|
||||
"thumbnail_image" TEXT,
|
||||
"cover_image" TEXT,
|
||||
"category_id" INTEGER NOT NULL,
|
||||
"votes_count" INTEGER NOT NULL DEFAULT 0,
|
||||
"lightning_address" TEXT,
|
||||
|
||||
CONSTRAINT "Project_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Vote" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"project_id" INTEGER NOT NULL,
|
||||
"amount_in_sat" INTEGER NOT NULL,
|
||||
"payment_request" TEXT,
|
||||
"payment_hash" TEXT,
|
||||
"preimage" TEXT,
|
||||
"paid" BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
CONSTRAINT "Vote_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Project" ADD CONSTRAINT "Project_category_id_fkey" FOREIGN KEY ("category_id") REFERENCES "Category"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Vote" ADD CONSTRAINT "Vote_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "Project"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Project" ADD COLUMN "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Project" ADD COLUMN "lnurl_callback_url" TEXT;
|
||||
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
||||
41
prisma/schema.prisma
Normal file
41
prisma/schema.prisma
Normal file
@@ -0,0 +1,41 @@
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
model Category {
|
||||
id Int @id @default(autoincrement())
|
||||
title String
|
||||
project Project[]
|
||||
}
|
||||
|
||||
model Project {
|
||||
id Int @id @default(autoincrement())
|
||||
title String
|
||||
description String
|
||||
website String
|
||||
thumbnail_image String?
|
||||
cover_image String?
|
||||
lightning_address String?
|
||||
lnurl_callback_url String?
|
||||
category Category @relation(fields: [category_id], references: [id])
|
||||
category_id Int
|
||||
votes_count Int @default(0)
|
||||
vote Vote[]
|
||||
created_at DateTime @default(now())
|
||||
}
|
||||
|
||||
model Vote {
|
||||
id Int @id @default(autoincrement())
|
||||
project Project @relation(fields: [project_id], references: [id])
|
||||
project_id Int
|
||||
amount_in_sat Int
|
||||
payment_request String?
|
||||
payment_hash String?
|
||||
preimage String?
|
||||
paid Boolean @default(false)
|
||||
}
|
||||
29
prisma/seed.js
Normal file
29
prisma/seed.js
Normal file
@@ -0,0 +1,29 @@
|
||||
const { PrismaClient } = require('@prisma/client')
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
async function main() {
|
||||
const category = await prisma.category.create({
|
||||
data: {
|
||||
title: 'El Salvador',
|
||||
},
|
||||
});
|
||||
|
||||
const project = await prisma.project.create({
|
||||
data: {
|
||||
title: "Captain Morgan",
|
||||
description: "HQ on a VULCANO lake",
|
||||
website: "https://github.com/peakshift",
|
||||
category_id: category.id,
|
||||
lightning_address: "johns@getalby.com",
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect()
|
||||
})
|
||||
22
src/App.tsx
22
src/App.tsx
@@ -1,9 +1,29 @@
|
||||
|
||||
import { useEffect } from "react";
|
||||
import Navbar from "./Components/Shared/Navbar/Navbar";
|
||||
import ExplorePage from "./Components/ExplorePage/ExplorePage";
|
||||
import ModalsContainer from "./Components/Shared/ModalsContainer/ModalsContainer";
|
||||
import { useAppDispatch, useAppSelector } from './utils/hooks';
|
||||
import { connectWallet } from './redux/features/wallet.slice';
|
||||
|
||||
function App() {
|
||||
const { isWalletConnected, webln } = useAppSelector(state => ({
|
||||
isWalletConnected: state.wallet.isConnected,
|
||||
webln: state.wallet.provider,
|
||||
}));
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
if(typeof window.webln != "undefined") {
|
||||
window.webln.enable().then((res: any) => {
|
||||
dispatch(connectWallet(window.webln));
|
||||
console.log("called:webln.enable()", res);
|
||||
}).catch((err: any) => {
|
||||
console.log("error:webln.enable()", err);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
return <div id="app" className='w-screen overflow-hidden'>
|
||||
<Navbar />
|
||||
<ExplorePage />
|
||||
|
||||
@@ -20,10 +20,6 @@ export default function Claim_CopySignatureCard({ onClose, direction, ...props }
|
||||
}))
|
||||
}, [dispatch])
|
||||
|
||||
const onCopy = () => {
|
||||
// Copy to Clipboard
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
custom={direction}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { useAppDispatch } from '../../utils/hooks';
|
||||
// import { useAppDispatch } from '../../utils/hooks';
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer'
|
||||
|
||||
export default function Claim_FundWithdrawCard({ onClose, direction, ...props }: ModalCard) {
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
//const dispatch = useAppDispatch();
|
||||
|
||||
|
||||
return (
|
||||
|
||||
@@ -25,10 +25,10 @@ export default function Claim_GenerateSignatureCard({ onClose, direction, ...pro
|
||||
// return () => clearTimeout(timeout)
|
||||
}, [handleNext])
|
||||
|
||||
const onCopy = () => {
|
||||
// Copy to Clipboard
|
||||
setTimeout(handleNext, 2000)
|
||||
}
|
||||
//const onCopy = () => {
|
||||
// // Copy to Clipboard
|
||||
// setTimeout(handleNext, 2000)
|
||||
//}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useAllCategoriesQuery } from 'src/generated/graphql'
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { ALL_CATEGORIES_QUERY, ALL_CATEGORIES_QUERY_RES } from './query';
|
||||
|
||||
export default function Categories() {
|
||||
|
||||
const { data, loading } = useAllCategoriesQuery();
|
||||
const { data, loading } = useQuery<ALL_CATEGORIES_QUERY_RES>(ALL_CATEGORIES_QUERY);
|
||||
|
||||
const handleClick = (categoryId: number) => {
|
||||
const handleClick = (categoryId: string) => {
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (loading)
|
||||
return null;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { gql } from "@apollo/client";
|
||||
import { ProjectCategory } from "src/utils/interfaces";
|
||||
|
||||
export const QUERY_ALL_CATEGORIES = gql`
|
||||
export const ALL_CATEGORIES_QUERY = gql`
|
||||
query AllCategories {
|
||||
allCategories {
|
||||
id
|
||||
@@ -8,3 +9,7 @@ export const QUERY_ALL_CATEGORIES = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export type ALL_CATEGORIES_QUERY_RES = {
|
||||
allCategories: ProjectCategory[];
|
||||
};
|
||||
|
||||
@@ -4,13 +4,13 @@ import { ProjectCard } from "../../../utils/interfaces";
|
||||
|
||||
interface Props {
|
||||
project: ProjectCard
|
||||
onClick: (projectId: number) => void
|
||||
onClick: (projectId: string) => void
|
||||
}
|
||||
|
||||
export default function ProjectCardMini({ project, onClick }: Props) {
|
||||
return (
|
||||
<div className="bg-gray-25 select-none px-16 py-16 flex w-[296px] gap-16 border border-gray-200 rounded-10 hover:cursor-pointer hover:bg-gray-100" onClick={() => onClick(project.id)}>
|
||||
<img src={project.thumbnail_image} draggable="false" className="flex-shrink-0 w-80 h-80 bg-gray-200 border-0 rounded-8"></img>
|
||||
<img src={project.thumbnail_image} alt={project.title} draggable="false" className="flex-shrink-0 w-80 h-80 bg-gray-200 border-0 rounded-8"></img>
|
||||
<div className="justify-around items-start min-w-0">
|
||||
<p className="text-body4 w-full font-bold overflow-ellipsis overflow-hidden whitespace-nowrap">{project.title}</p>
|
||||
<p className="text-body5 text-gray-600 font-light my-[5px]">{project.category.title}</p>
|
||||
|
||||
@@ -20,14 +20,14 @@ Hottest.args = {
|
||||
<MdLocalFireDepartment
|
||||
className='inline-block text-fire align-bottom scale-125 ml-4 origin-bottom'
|
||||
/></>,
|
||||
categoryId: 2,
|
||||
categoryId: '2',
|
||||
projects: mockData.projectsCards
|
||||
}
|
||||
|
||||
export const Defi = Template.bind({});
|
||||
Defi.args = {
|
||||
title: 'DeFi',
|
||||
categoryId: 33,
|
||||
categoryId: '33',
|
||||
projects: mockData.projectsCards
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,12 @@ const calcNumItems = () => {
|
||||
return items;
|
||||
}
|
||||
|
||||
interface Props { title: string | ReactElement, categoryId: number, projects: ProjectCard[] }
|
||||
interface Props { title: string | ReactElement, categoryId: string, projects: ProjectCard[] }
|
||||
|
||||
export default function ProjectsRow({ title, categoryId, projects }: Props) {
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
const [carouselItmsCnt, setCarouselItmsCnt] = useState(calcNumItems);
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
responsive.all.items = carouselItmsCnt
|
||||
|
||||
@@ -33,7 +33,7 @@ export default function ProjectsRow({ title, categoryId, projects }: Props) {
|
||||
document.addEventListener('mousedown', () => drag.current = false);
|
||||
document.addEventListener('mousemove', () => drag.current = true);
|
||||
|
||||
const handleClick = (projectId: number) => {
|
||||
const handleClick = (projectId: string) => {
|
||||
if (!drag.current)
|
||||
dispatch(openModal({ modalId: ModalId.Project, propsToPass: { projectId } }))
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
|
||||
import ProjectsRow from "../ProjectsRow/ProjectsRow";
|
||||
import { MdLocalFireDepartment } from "react-icons/md";
|
||||
import { useAllCategoriesProjectsQuery } from "src/generated/graphql";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { ALL_CATEGORIES_PROJECTS_QUERY, ALL_CATEGORIES_PROJECTS_RES } from "./query";
|
||||
|
||||
|
||||
export default function ProjectsSection() {
|
||||
|
||||
const { data, loading } = useAllCategoriesProjectsQuery()
|
||||
const { data, loading } = useQuery<ALL_CATEGORIES_PROJECTS_RES>(ALL_CATEGORIES_PROJECTS_QUERY);
|
||||
|
||||
if (loading || !data) return null;
|
||||
|
||||
return (
|
||||
<div className='mt-32 lg:mt-48'>
|
||||
<ProjectsRow title={<>Hottest <MdLocalFireDepartment className='inline-block text-fire align-bottom scale-125 origin-bottom' /></>}
|
||||
categoryId={10101}
|
||||
categoryId="133123"
|
||||
projects={data.newProjects} />
|
||||
{data.allCategories.map(({ id, title, project, }) => {
|
||||
if (project)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { gql } from "@apollo/client";
|
||||
import { ProjectCard } from "src/utils/interfaces";
|
||||
|
||||
export const QUERY_ALL_CATEGORIES_PROJECTS = gql`
|
||||
export const ALL_CATEGORIES_PROJECTS_QUERY = gql`
|
||||
query AllCategoriesProjects {
|
||||
allCategories {
|
||||
id
|
||||
@@ -24,3 +25,12 @@ export const QUERY_ALL_CATEGORIES_PROJECTS = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export type ALL_CATEGORIES_PROJECTS_RES = {
|
||||
newProjects: ProjectCard[];
|
||||
allCategories: {
|
||||
id: string;
|
||||
title: string;
|
||||
project: ProjectCard[];
|
||||
}[];
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useAppDispatch } from '../../utils/hooks';
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer'
|
||||
import { AiFillThunderbolt } from 'react-icons/ai';
|
||||
import CopyToClipboard from 'src/Components/Shared/CopyToClipboard/CopyToClipboard';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { IoClose } from 'react-icons/io5';
|
||||
|
||||
export default function Login_ExternalWalletCard({ onClose, direction, ...props }: ModalCard) {
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function Login_SuccessCard({ onClose, direction, ...props }: Moda
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(connectWallet());
|
||||
//dispatch(connectWallet());
|
||||
const timeout = setTimeout(handleNext, 3000)
|
||||
return () => clearTimeout(timeout)
|
||||
}, [handleNext, dispatch])
|
||||
|
||||
@@ -1,30 +1,55 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { BiArrowBack, BiWindowClose } from 'react-icons/bi'
|
||||
import { BsJoystick } from 'react-icons/bs'
|
||||
import { MdClose, MdLocalFireDepartment } from 'react-icons/md';
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer';
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { useAppDispatch, useAppSelector } from '../../utils/hooks';
|
||||
import { ModalId, openModal, scheduleModal } from '../../redux/features/modals.slice';
|
||||
import { setProject } from '../../redux/features/project.slice';
|
||||
import { connectWallet } from '../../redux/features/wallet.slice';
|
||||
import Button from 'src/Components/Shared/Button/Button';
|
||||
import { useGetProjectQuery } from 'src/generated/graphql';
|
||||
import { requestProvider } from 'webln';
|
||||
import { PROJECT_BY_ID_QUERY, PROJECT_BY_ID_RES, PROJECT_BY_ID_VARS } from './query'
|
||||
|
||||
|
||||
export default function ProjectCard({ onClose, direction, ...props }: ModalCard) {
|
||||
|
||||
const { data, loading } = useGetProjectQuery({
|
||||
variables: {
|
||||
getProjectId: props.projectId
|
||||
}
|
||||
})
|
||||
|
||||
const { isWalletConnected } = useAppSelector(state => ({ isWalletConnected: state.wallet.isConnected }))
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const project = data?.getProject;
|
||||
const { loading } = useQuery<PROJECT_BY_ID_RES, PROJECT_BY_ID_VARS>(
|
||||
PROJECT_BY_ID_QUERY,
|
||||
{
|
||||
variables: { projectId: parseInt(props.projectId) },
|
||||
onCompleted: data => {
|
||||
dispatch(setProject(data.getProject))
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const { isWalletConnected, webln, project } = useAppSelector(state => ({
|
||||
isWalletConnected: state.wallet.isConnected,
|
||||
webln: state.wallet.provider,
|
||||
project: state.project.project,
|
||||
}));
|
||||
|
||||
|
||||
|
||||
if (loading || !project) return <></>;
|
||||
|
||||
const onConnectWallet = async () => {
|
||||
try {
|
||||
const webln = await requestProvider();
|
||||
if (webln) {
|
||||
dispatch(connectWallet(webln));
|
||||
alert("wallet connected!");
|
||||
}
|
||||
// Now you can call all of the webln.* methods
|
||||
}
|
||||
catch (err: any) {
|
||||
// Tell the user what went wrong
|
||||
alert(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
const onTip = () => {
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { gql } from "@apollo/client";
|
||||
import { Project } from "src/utils/interfaces";
|
||||
|
||||
export const QUERY_PROJECT_BY_ID = gql`
|
||||
query GetProject($getProjectId: Int!) {
|
||||
getProject(id: $getProjectId) {
|
||||
export const PROJECT_BY_ID_QUERY = gql`
|
||||
query Project($projectId: Int!) {
|
||||
getProject(id: $projectId) {
|
||||
id
|
||||
cover_image
|
||||
thumbnail_image
|
||||
@@ -10,9 +11,17 @@ export const QUERY_PROJECT_BY_ID = gql`
|
||||
website
|
||||
votes_count
|
||||
category {
|
||||
id
|
||||
title
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export interface PROJECT_BY_ID_RES {
|
||||
getProject: Project;
|
||||
}
|
||||
|
||||
export interface PROJECT_BY_ID_VARS {
|
||||
projectId: number;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,11 @@ export default function Navbar() {
|
||||
const [searchInput, setSearchInput] = useState("");
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const dispatch = useAppDispatch()
|
||||
const { isWalletConnected } = useAppSelector(state => ({ isWalletConnected: state.wallet.isConnected }))
|
||||
|
||||
const { isWalletConnected, webln } = useAppSelector(state => ({
|
||||
isWalletConnected: state.wallet.isConnected,
|
||||
webln: state.wallet.provider,
|
||||
}));
|
||||
|
||||
const toggleSearch = () => {
|
||||
if (!searchOpen) {
|
||||
@@ -78,7 +82,7 @@ export default function Navbar() {
|
||||
className="flex">
|
||||
<Button color='primary' size='md' className="lg:px-40">Submit App️</Button>
|
||||
{isWalletConnected ?
|
||||
<Button className="ml-16 py-12 px-16 lg:px-20" onClick={onWithdraw}>2.2k Sats <AiFillThunderbolt className='inline-block text-thunder transform scale-125' /></Button>
|
||||
<Button className="ml-16 py-12 px-16 lg:px-20">Connected <AiFillThunderbolt className='inline-block text-thunder transform scale-125' /></Button>
|
||||
: <Button className="ml-16 py-12 px-16 lg:px-20" onClick={onConnectWallet}><AiFillThunderbolt className='inline-block text-thunder transform scale-125' /> Connect Wallet </Button>
|
||||
}
|
||||
</motion.div>
|
||||
|
||||
@@ -3,6 +3,10 @@ import React, { useState } from 'react';
|
||||
import { AiFillThunderbolt } from 'react-icons/ai'
|
||||
import { IoClose } from 'react-icons/io5'
|
||||
import { ModalCard, modalCardVariants } from '../Shared/ModalsContainer/ModalsContainer';
|
||||
import { useAppDispatch, useAppSelector } from '../../utils/hooks';
|
||||
import { gql, useQuery, useMutation } from "@apollo/client";
|
||||
import useWindowSize from "react-use/lib/useWindowSize";
|
||||
import Confetti from "react-confetti";
|
||||
|
||||
const defaultOptions = [
|
||||
{ text: '10 sat', value: 10 },
|
||||
@@ -10,19 +14,88 @@ const defaultOptions = [
|
||||
{ text: '1k sats', value: 1000 },
|
||||
]
|
||||
|
||||
export default function TipCard({ onClose, direction, ...props }: ModalCard) {
|
||||
enum PaymentStatus {
|
||||
DEFAULT,
|
||||
FETCHING_PAYMENT_DETAILS,
|
||||
PAID,
|
||||
AWAITING_PAYMENT,
|
||||
PAYMENT_CONFIRMED,
|
||||
NOT_PAID,
|
||||
CANCELED
|
||||
}
|
||||
|
||||
const [selectedOption, setSelectedOption] = useState(0);
|
||||
const [input, setInput] = useState<number>();
|
||||
const VOTE = gql`
|
||||
mutation Mutation($projectId: Int!, $amountInSat: Int!) {
|
||||
vote(project_id: $projectId, amount_in_sat: $amountInSat) {
|
||||
id
|
||||
amount_in_sat
|
||||
payment_request
|
||||
payment_hash
|
||||
paid
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const CONFIRM_VOTE = gql`
|
||||
mutation Mutation($paymentRequest: String!, $preimage: String!) {
|
||||
confirmVote(payment_request: $paymentRequest, preimage: $preimage) {
|
||||
id
|
||||
amount_in_sat
|
||||
payment_request
|
||||
payment_hash
|
||||
paid
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function TipCard({ onClose, direction, ...props }: ModalCard) {
|
||||
const { width, height } = useWindowSize()
|
||||
|
||||
const { isWalletConnected, webln } = useAppSelector(state => ({
|
||||
isWalletConnected: state.wallet.isConnected,
|
||||
webln: state.wallet.provider,
|
||||
}));
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const [selectedOption, setSelectedOption] = useState(10);
|
||||
const [voteAmount, setVoteAmount] = useState<number>(10);
|
||||
const [paymentStatus, setPaymentStatus] = useState<PaymentStatus>(PaymentStatus.DEFAULT);
|
||||
|
||||
const [vote, { data }] = useMutation(VOTE, {
|
||||
onCompleted: (votingData) => {
|
||||
setPaymentStatus(PaymentStatus.AWAITING_PAYMENT);
|
||||
webln.sendPayment(votingData.vote.payment_request).then((res: any) => {
|
||||
console.log("waiting for payment", res);
|
||||
confirmVote({variables: { paymentRequest: votingData.vote.payment_request, preimage: res.preimage }});
|
||||
setPaymentStatus(PaymentStatus.PAID);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.log(err);
|
||||
setPaymentStatus(PaymentStatus.NOT_PAID);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const [confirmVote, { data: confirmedVoteData }] = useMutation(CONFIRM_VOTE, {
|
||||
onCompleted: (votingData) => {
|
||||
setPaymentStatus(PaymentStatus.PAYMENT_CONFIRMED);
|
||||
}
|
||||
});
|
||||
|
||||
const onChangeInput = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSelectedOption(-1);
|
||||
setInput(Number(event.target.value));
|
||||
setVoteAmount(Number(event.target.value));
|
||||
};
|
||||
|
||||
const onSelectOption = (idx: number) => {
|
||||
setSelectedOption(idx);
|
||||
setInput(defaultOptions[idx].value);
|
||||
setVoteAmount(defaultOptions[idx].value);
|
||||
}
|
||||
|
||||
const requestPayment = () => {
|
||||
setPaymentStatus(PaymentStatus.FETCHING_PAYMENT_DETAILS);
|
||||
vote({variables: { "amountInSat": voteAmount, "projectId": parseInt("1") }});
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -43,7 +116,7 @@ export default function TipCard({ onClose, direction, ...props }: ModalCard) {
|
||||
<div className="input-wrapper">
|
||||
<input
|
||||
className="input-field"
|
||||
value={input} onChange={onChangeInput}
|
||||
value={voteAmount} onChange={onChangeInput}
|
||||
type="number"
|
||||
placeholder="e.g 5 sats" />
|
||||
{/* <IoCopy className='input-icon' /> */}
|
||||
@@ -60,10 +133,16 @@ export default function TipCard({ onClose, direction, ...props }: ModalCard) {
|
||||
)}
|
||||
</div>
|
||||
<p className="text-body6 mt-12 text-gray-500">1 sat = 1 vote</p>
|
||||
<button className="btn btn-primary w-full mt-32" onClick={onClose}>
|
||||
{paymentStatus === PaymentStatus.FETCHING_PAYMENT_DETAILS && <p className="text-body6 mt-12 text-gray-500">Please wait while we the fetch payment details.</p>}
|
||||
{paymentStatus === PaymentStatus.NOT_PAID && <p className="text-body6 mt-12 text-red-500">You did not confirm the payment. Please try again.</p>}
|
||||
{paymentStatus === PaymentStatus.PAID && <p className="text-body6 mt-12 text-green-500">The invoice was paid! Please wait while we confirm it.</p>}
|
||||
{paymentStatus === PaymentStatus.AWAITING_PAYMENT && <p className="text-body6 mt-12 text-yellow-500">Please confirm the payment in the prompt...</p>}
|
||||
{paymentStatus === PaymentStatus.PAYMENT_CONFIRMED && <p className="text-body6 mt-12 text-green-500">Imagine confetti here</p>}
|
||||
<button className="btn btn-primary w-full mt-32" onClick={requestPayment}>
|
||||
Upvote
|
||||
</button>
|
||||
</div>
|
||||
{paymentStatus === PaymentStatus.PAYMENT_CONFIRMED && <Confetti width={width} height={height} />}
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,53 @@
|
||||
import { Project, ProjectCard, ProjectCategory } from "../utils/interfaces";
|
||||
import { gql, useQuery } from "@apollo/client";
|
||||
import data from "./mockData.json";
|
||||
|
||||
// export async function getAllCategories(): Promise<ProjectCategory[]> {
|
||||
// return data.categories as any;
|
||||
// }
|
||||
export async function getAllCategories(): Promise<ProjectCategory[]> {
|
||||
// let QUERY = gql`
|
||||
// query GetCategories {
|
||||
// allCategories {
|
||||
// id
|
||||
// title
|
||||
// }
|
||||
// }
|
||||
// `;
|
||||
return data.categories;
|
||||
}
|
||||
|
||||
// export async function getHottestProjects(): Promise<ProjectCard[]> {
|
||||
// return data.projectsCards as any;
|
||||
// }
|
||||
export async function getHottestProjects(): Promise<ProjectCard[]> {
|
||||
// let QUERY = gql`
|
||||
// query {
|
||||
// allProject {
|
||||
// id
|
||||
// cover_image
|
||||
// thumbnail_image
|
||||
// title
|
||||
// website
|
||||
// votes_count
|
||||
// }
|
||||
// }
|
||||
// `;
|
||||
return data.projectsCards;
|
||||
}
|
||||
|
||||
// export async function getProjectsByCategory(
|
||||
// categoryId: string
|
||||
// ): Promise<ProjectCard[]> {
|
||||
// return data.projectsCards as any;
|
||||
// }
|
||||
export async function getProjectsByCategory(
|
||||
categoryId: string
|
||||
): Promise<ProjectCard[]> {
|
||||
// let QUERY = gql`
|
||||
// query Categories($categoryId: Int!){
|
||||
// projectsByCategory(category_id: ${categoryId}) {
|
||||
// id
|
||||
// cover_image
|
||||
// thumbnail_image
|
||||
// title
|
||||
// website
|
||||
// lightning_address
|
||||
// votes_count
|
||||
// }
|
||||
// }
|
||||
// `;
|
||||
return data.projectsCards;
|
||||
}
|
||||
|
||||
// // returns the latest bunch of projects in each ( or some ) categories, and returns the hottest projects
|
||||
// export async function getLatestProjects(): Promise<
|
||||
@@ -26,5 +60,17 @@ import data from "./mockData.json";
|
||||
// }
|
||||
|
||||
export async function getProjectById(projectId: string): Promise<Project> {
|
||||
return data.project as any;
|
||||
// let QUERY = gql`
|
||||
// query Project(projectId: String!) {
|
||||
// getProject(id: $projectId) {
|
||||
// id
|
||||
// cover_image
|
||||
// thumbnail_image
|
||||
// title
|
||||
// website
|
||||
// votes_count
|
||||
// }
|
||||
// }
|
||||
// `;
|
||||
return data.project;
|
||||
}
|
||||
|
||||
@@ -1,131 +1,131 @@
|
||||
{
|
||||
"categories": [
|
||||
{
|
||||
"id": 111,
|
||||
"id": "111",
|
||||
"title": "Hottest"
|
||||
},
|
||||
{
|
||||
"id": 123,
|
||||
"id": "123",
|
||||
"title": "Art & Collectibles"
|
||||
},
|
||||
{
|
||||
"id": 124,
|
||||
"id": "124",
|
||||
"title": "DeFi"
|
||||
},
|
||||
{
|
||||
"id": 311,
|
||||
"id": "311",
|
||||
"title": "Entertainment"
|
||||
},
|
||||
{
|
||||
"id": 333,
|
||||
"id": "333",
|
||||
"title": "Exchange"
|
||||
},
|
||||
{
|
||||
"id": 223,
|
||||
"id": "223",
|
||||
"title": "News"
|
||||
},
|
||||
{
|
||||
"id": 451,
|
||||
"id": "451",
|
||||
"title": "Shop"
|
||||
},
|
||||
{
|
||||
"id": 2321,
|
||||
"id": "232",
|
||||
"title": "Social"
|
||||
},
|
||||
{
|
||||
"id": 51231,
|
||||
"id": "512",
|
||||
"title": "Wallet"
|
||||
},
|
||||
{
|
||||
"id": 1321,
|
||||
"id": "132",
|
||||
"title": "Other"
|
||||
}
|
||||
],
|
||||
"projectsCards": [
|
||||
{
|
||||
"id": 123123,
|
||||
"id": "123",
|
||||
"title": "First App",
|
||||
"thumbnail_image": "https://via.placeholder.com/150",
|
||||
"category": {
|
||||
"id": 51231,
|
||||
"id": "512",
|
||||
"title": "{app.category}"
|
||||
},
|
||||
"votes_count": 123
|
||||
},
|
||||
{
|
||||
"id": 765454,
|
||||
"id": "765",
|
||||
"title": "Second App",
|
||||
"thumbnail_image": "https://via.placeholder.com/150",
|
||||
"category": {
|
||||
"id": 51231,
|
||||
"id": "512",
|
||||
"title": "{app.category}"
|
||||
},
|
||||
"votes_count": 123
|
||||
},
|
||||
{
|
||||
"id": 55,
|
||||
"id": "55",
|
||||
"title": "Third App",
|
||||
"thumbnail_image": "https://via.placeholder.com/150",
|
||||
"category": {
|
||||
"id": 51231,
|
||||
"id": "512",
|
||||
"title": "{app.category}"
|
||||
},
|
||||
"votes_count": 123
|
||||
},
|
||||
{
|
||||
"id": 12344123,
|
||||
"id": "12344123",
|
||||
"title": "Fourth App",
|
||||
"thumbnail_image": "https://via.placeholder.com/150",
|
||||
"category": {
|
||||
"id": 51231,
|
||||
"id": "512",
|
||||
"title": "{app.category}"
|
||||
},
|
||||
"votes_count": 123
|
||||
},
|
||||
{
|
||||
"id": 56745,
|
||||
"id": "56745",
|
||||
"title": "Fifth App",
|
||||
"thumbnail_image": "https://via.placeholder.com/150",
|
||||
"category": {
|
||||
"id": 51231,
|
||||
"id": "512",
|
||||
"title": "{app.category}"
|
||||
},
|
||||
"votes_count": 123
|
||||
},
|
||||
{
|
||||
"id": 3312431,
|
||||
"id": "3312431",
|
||||
"title": "Sixth App",
|
||||
"thumbnail_image": "https://via.placeholder.com/150",
|
||||
"category": {
|
||||
"id": 51231,
|
||||
"id": "512",
|
||||
"title": "{app.category}"
|
||||
},
|
||||
"votes_count": 123
|
||||
}
|
||||
],
|
||||
"project": {
|
||||
"id": 123123123,
|
||||
"cover_image": "https://s3-alpha-sig.figma.com/img/07b8/5d84/145942255afd215b3da26dbbf1dd03bd?Expires=1638144000&Signature=Cl1DUQJIUsrrFi48M~qU1r3Z0agGdy-uiNUao5g8-nu34QtoyWTFPXvaH3naSZBYqcPyKFq1jaXF6Mw1uj1hdWwGhXhMPLnKNJFFrGViVXhXu-3YeCPY9p4-IcieFJBZPVA~zDY8zxY5b06loWsINAVx4eMHRAhSWl~~Mca5PjlSXloiYrT00W-6c9m8gevfMMX~PsHQedzwYzg0j2DlnhPX8LbRkli1G2OxtCaFwo3~HGHXIlFGuGU1uXRvi1qBWrdjdsuWgIly1ekcFfJWAKmwYXk06EtCmfWRgGYbD7cBK~lwOkFofbf1LW0yqLv0hr4svwToH~3FiHenrCF-1g__&Key-Pair-Id=APKAINTVSUGEWH5XD5UA",
|
||||
"id": "1233",
|
||||
"cover_image": "https://picsum.photos/id/10/1024/1024",
|
||||
"thumbnail_image": "https://s3-alpha-sig.figma.com/img/be1b/cd75/1baa911b3875134c0889d6755c4ba2cb?Expires=1638748800&Signature=DOiLciAA95w8gOvAowjiiR-ZPbmV1oGSRRK8YpE4ALMoe47pL7DifQxZvL1LQn~NRa0aLMoMk61521fbbGJeDAwk~j6fIm~iZAlMzQn7DdWy0wFR0uLQagOgpIiIXO-w8CeC14VoW-hrjIX5mDmOonJzkfoftGqIF1WCOmP2EuswyJpIngFdLb15gCex4Necs3vH2cuD9iSgWG2za97KfdXZP79ROyk2EN9Q3~a7RT4FTBBIlgKDLuFGSVRxReXVNajn~XSxBJh2de9dFVa3tOXkwJXu3jb0G4x-wRCaG-KmBhUOemuGtu5Fumh6goktGh~bIDwoHeUBVKFHAzaYgw__&Key-Pair-Id=APKAINTVSUGEWH5XD5UA",
|
||||
"title": "{Project Name}",
|
||||
"website": "https://www.project.com",
|
||||
"description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Distinctio nobis aliquam, minima assumenda earum sapiente pariatur cupiditate error nihil, eius corporis ratione unde perspiciatis tenetur ipsa aut ex consequatur maiores eligendi quidem exercitationem suscipit. Ex hic reprehenderit deleniti possimus culpa animi velit? Dolores, nemo quis minima sapiente sed laborum ipsam?",
|
||||
"title": "█████████",
|
||||
"website": "████████",
|
||||
"description": "██████████████████",
|
||||
"category": {
|
||||
"id": 124,
|
||||
"title": "DeFi"
|
||||
"id": "333",
|
||||
"title": "█████"
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"id": 123,
|
||||
"title": "lightning"
|
||||
"id": "123",
|
||||
"title": "█████"
|
||||
},
|
||||
{
|
||||
"id": 333,
|
||||
"title": "payments"
|
||||
"id": "313",
|
||||
"title": "██████"
|
||||
},
|
||||
{
|
||||
"id": 444,
|
||||
"title": "lnurl-auth"
|
||||
"id": "451",
|
||||
"title": "█████"
|
||||
}
|
||||
],
|
||||
"screenShots": [
|
||||
@@ -134,6 +134,6 @@
|
||||
"",
|
||||
""
|
||||
],
|
||||
"votes_count": 123
|
||||
"votes_count": 0
|
||||
}
|
||||
}
|
||||
3
src/global.d.ts
vendored
Normal file
3
src/global.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
declare interface Window {
|
||||
webln: any;
|
||||
}
|
||||
@@ -1,15 +1,27 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import {
|
||||
ApolloClient,
|
||||
InMemoryCache,
|
||||
ApolloProvider,
|
||||
} from "@apollo/client";
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import Wrapper from './utils/Wrapper';
|
||||
|
||||
const client = new ApolloClient({
|
||||
uri: 'https://xenodochial-goldstine-d09942.netlify.app/.netlify/functions/graphql',
|
||||
cache: new InMemoryCache()
|
||||
});
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<Wrapper>
|
||||
<App />
|
||||
</Wrapper>
|
||||
<ApolloProvider client={client}>
|
||||
<Wrapper>
|
||||
<App />
|
||||
</Wrapper>
|
||||
</ApolloProvider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
@@ -5,10 +5,11 @@ import mockData from "../../api/mockData.json";
|
||||
|
||||
interface StoreState {
|
||||
project: Project | null;
|
||||
projectSet: boolean;
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
project: mockData.project,
|
||||
project: null,
|
||||
} as StoreState;
|
||||
|
||||
export const projectSlice = createSlice({
|
||||
|
||||
@@ -1,21 +1,39 @@
|
||||
import { requestProvider } from "webln";
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
interface StoreState {
|
||||
isConnected: boolean;
|
||||
isLoading: boolean;
|
||||
provider: any;
|
||||
}
|
||||
|
||||
const isWebLNConnected = () => {
|
||||
// since webln spec expects webln.enable() to be called on each load
|
||||
// and extensions like alby do not inject the webln object with true
|
||||
// every refresh requires the user to re-enable, even if its been
|
||||
// given premission previously.
|
||||
//
|
||||
// that is to say ... this function is quite useless
|
||||
if (typeof window.webln === 'undefined') {
|
||||
return false;
|
||||
} else if (window.webln.enabled === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
isConnected: false,
|
||||
isLoading: false,
|
||||
provider: null,
|
||||
} as StoreState;
|
||||
|
||||
export const walletSlice = createSlice({
|
||||
name: "wallet",
|
||||
initialState,
|
||||
reducers: {
|
||||
connectWallet(state) {
|
||||
state.isConnected = true;
|
||||
connectWallet(state, action: PayloadAction<any>) {
|
||||
state.isConnected = action.payload ? true : false;
|
||||
state.provider = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
export interface AllCategoriesData {
|
||||
allCategories: ProjectCategory[];
|
||||
}
|
||||
|
||||
export interface ProjectCategory {
|
||||
id: number;
|
||||
id: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface ProjectCard {
|
||||
id: number;
|
||||
id: string;
|
||||
title: string;
|
||||
thumbnail_image: string;
|
||||
category: ProjectCategory;
|
||||
@@ -12,14 +16,14 @@ export interface ProjectCard {
|
||||
}
|
||||
|
||||
export interface Tag {
|
||||
id: number;
|
||||
id: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export type Image = string;
|
||||
|
||||
export interface Project {
|
||||
id: number;
|
||||
id: string;
|
||||
title: string;
|
||||
category: ProjectCategory;
|
||||
website?: string;
|
||||
|
||||
Reference in New Issue
Block a user