Use new frontend (#1941)

* Use new frontend

* Remove whitespace

* Remove more whitespace

* Remove more whitespace

* Add requests file

* Try loading squeaks in home page

* Got basic timeline working

* Fix block time display

* Show author pubkey in tweet card

* Show profile name in tweet card

* Show profile image in tweet card

* Got tweet page working

* Implement part of profile page

* Show profile pubkey in pubkey page

* Show profile name in profile page

* User tweets now show in profile page

* Show reply tweets

* Load ancestor tweets, but show still not working

* Got tweet reply thread working

* Add partial implementation of make squeak

* Add re-usable component for make squeak

* Load signing profiles in middleware

* Implement make squeak

* Update favicon

* Fix issue with updating state of replies on new squeak.

* Remove author pubkey from squeak url path

* Got reply squeak working

* Fix update state with reducer on new reply

* Show author profile image in make squeak component

* Implement get more tweets in timeline

* Fix load more tweets

* Show loading state in timeline get mor

* Clear timeline tweets on page load

* Simplify props in make squeak component

* Remove unused props from make squeak component

* Fix params for tweet cards in profile page

* Got follow and unfollow working

* Fix tweets and replies tab in profile page

* Got load more tweets working for profile page

* Add delay to endpoints

* Clear user tweets on profile page load

* Remove delay from endpoint

* Remove whitespace in explore page

* Show signing profiles in explore page

* Show contact profiles in explore page

* Rename explore page to profiles

* Remove trends from profiles page

* Remove search tab from profiles page

* Fix set follow or unfollow from profiles page

* Change text of profile search box

* Implement name change in edit profile

* Simplify edit and follow buttons

* Remove chat button in profile

* Simplify user pubkey display in profile

* Remove bio and location from profile edit

* Remove banner from profile page

* Got image change working

* Remove log lines

* Remove unused state from profile

* Remove more state from profile page

* Remove content of messages page

* Remove unused state from profiles page

* Remove unused import from profile page

* Rename sats earned and sats spent tabs in profile page

* Remove log line

* Add create signing profile to profiles page

* Remove banner from new profile modal

* Add create contact profile to profiles page

* Add comment for modal in profile page

* Use separate modals in profile page for edit and spending

* Add delete profile button to profile pag

* Remove use of bookmark

* Remove banner from delete profile modal

* Implement delete tweet

* Fix delete tweet for tweet card.

* Add TODO comment

* Got update tweet for like and unlike working

* Simplify update tweet and delete tweet reducerts

* Add comments to tweet component

* Add comment to reducers

* Remove check for user in tweet card.

* Simplify props for tweet card.

* Implement missing tweet component partially

* Got display for missing tweet mostly working

* Fix reducer for delete tweet from ancestors

* Fix display of squeak with unknown author

* Fix handling of unknown author in make squeak to reply

* Got profile page working for missing profile

* Implement create profile from missing profile page

* Fix close make tweet modal on submit

* Remove who to follow from feed

* Fix close modal on submit new tweet in tweet card

* Add new peers page

* Got connected peers showing

* Remove search bar from peers page

* Show saved peers in peers page

* Got save peer working

* Remove input attach button from make squeak component

* Got basic peer page working

* Load peer connection info in peer page

* Remove unused functions in peer page

* Show correct host and port for peer and peers page

* Got connect and disconnect working in peer page

* Got connect and disconnect reducer working

* Remove unused displays from peer page

* Change word in connect button in peer page

* Got tor toggle working

* Add use tor option to create saved peer modal

* Implement delete saved peer

* Fix variable names in peers page

* Fix issue with save peer

* Fix action for save peer

* Show block info and block explorer link

* Get network in tweet page

* Remove log line

* Fix color scheme for missing tweet

* Add download button to missing squeak

* Show locked content in tweet and tweet card

* Partial implementation of buy dialog

* Got buy squeak working

* Reload ancestors in download tweet middleware

* Improve display of offer

* Show payment summary in sidebar

* Implemented beginning of payments page

* Show sent payments in payments page

* Implement clear sent payments

* Fix link to payments in feed

* Got received payments working

* Fix action for load more received payments

* Fix typo in get more received payments

* Remove log lines

* Add header title for peers page

* Remove unused pages from nav

* Show alert when payment fails

* Use more menu in profile page

* Remove icon from more button

* Add export private key

* Hide export private key for contact profile

* Remove log lines

* Got import signing profile working

* Remove log lines

* Improve display of locked content

* Fix comment for edit moda

* Add download squeaks action to profile page

* Remove log lines

* Show button for download action in profile page

* Include time in payments display

* Remove bookmark component

* Remove list component

* Remove chat component

* Remove retweet action

* Remove all use of account attributes

* Change unit to sats for sell dialog

* Fix display of peer page

* Remove usage of account state

* Try to implement logout

* Include prefix in route path

* Got redirect on logout working

* Got logout working in prod

* Remove log lines

* Remove suggestions action

* Show connection info

* Got autoconnect toggle working

* Rearrange peer page

* Got custom icon working

* Use custom icon

* Fix path string match in nav

* Add modal to show external address in peers page

* Remove export button from external address modal

* Update frontend build

* Fix more menu in profile page

* Update frontend build
This commit is contained in:
Jonathan Zernik 2022-02-21 01:56:50 -08:00 committed by GitHub
parent ac3279c532
commit 74a0ee7079
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
372 changed files with 27872 additions and 64548 deletions

View file

@ -1,21 +0,0 @@
The MIT License
Copyright (c) 2019 Flatlogic LLC.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -1,29 +1,17 @@
# frontend
# Twitter-Clone
## Run in dev mode
A website I build based on twitter please enjoy. This website is Not for actual usage only learning purposes.
- Start bitcoin-core and lnd in docker-compose:
```
cd docker
docker-compose up
```
- Start the backend with the `SQUEAKNODE_WEBADMIN_ENABLED`, `SQUEAKNODE_WEBADMIN_LOGIN_DISABLED`, and `SQUEAKNODE_WEBADMIN_ALLOW_CORS` environment variables:
```
python3 -m venv venv
source venv/bin/activate
pip install .
SQUEAKNODE_WEBADMIN_ENABLED=TRUE SQUEAKNODE_WEBADMIN_LOGIN_DISABLED=TRUE SQUEAKNODE_WEBADMIN_ALLOW_CORS=TRUE SQUEAKNODE_NETWORK=testnet squeaknode --config config.ini
```
- Start the frontend in development mode:
```
cd frontend
make rundev
```
[Website Link](https://twitterapp-clone.netlify.app)
## Build for production
[Backend Repo](https://github.com/Ali-hd/TwitterClone-Backend)
- Run the build command:
```
cd frontend
make build
```
## Technologies used
- Reactjs
- Context API
- Node.js express
- MongoDB mongoose
- Image uploaded on Amazon S3
- socket-io for real-time chatting

36592
frontend/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,47 +1,35 @@
{
"name": "squeak-node-frontend",
"version": "1.1.0",
"name": "twitter-frontend",
"version": "0.1.0",
"private": true,
"resolutions": {
"websocket-extensions": "^0.1.4"
},
"dependencies": {
"@emotion/react": "^11.6.0",
"@emotion/styled": "^11.6.0",
"@grpc/grpc-js": "~1.4.4",
"@grpc/proto-loader": "~0.6.7",
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.57",
"@material-ui/styles": "^4.11.4",
"@mui/icons-material": "^5.1.1",
"@mui/material": "^5.1.1",
"@mui/styled-engine": "^5.1.1",
"apexcharts": "^3.22.2",
"async": "~3.2.2",
"classnames": "^2.2.6",
"font-awesome": "^4.7.0",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"axios": "^0.19.2",
"darkreader": "^4.9.10",
"dotenv": "^8.2.0",
"google-protobuf": "^3.12.4",
"grpc-web": "^1.2.1",
"moment": "^2.29.1",
"mui-datatables": "^4.0.0",
"protobufjs": "^6.10.2",
"react": "^17.0.2",
"react-apexcharts": "^1.3.3",
"react-copy-to-clipboard": "^5.0.4",
"react-dom": "^17.0.2",
"react-router-dom": "^5.0.1",
"react-scripts": "^4.0.3",
"react-syntax-highlighter": "^15.4.5",
"react-toastify": "^8.1.0",
"recharts": "^2.1.6",
"tinycolor2": "^1.4.2"
"jwt-decode": "^2.2.0",
"moment": "^2.26.0",
"react": "^16.13.1",
"react-contenteditable": "^3.3.4",
"react-dom": "^16.13.1",
"react-responsive": "^8.1.0",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1",
"react-select": "^5.2.2",
"sass": "^1.49.7",
"serve": "^11.3.2",
"socket.io-client": "^2.3.0"
},
"scripts": {
"dev": "react-scripts start",
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"heroku-postbuild": "npm run build"
},
"eslintConfig": {
"extends": "react-app"
@ -57,9 +45,5 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"prettier": "^2.4.1",
"terser": "^5.10.0"
}
}

View file

@ -0,0 +1 @@
/* /index.html 200

22
frontend/public/index.html Executable file → Normal file
View file

@ -2,12 +2,15 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#1da1f2" />
<meta nam="description" content="Twitter clone!"/>
<meta name="og:title" content="Twitter clone by Ali hd"/>
<meta name="og:description" content="check out my the twitter clone I made"/>
<!-- <meta name="viewport" content="minimum-scale=1"> -->
<link href="https://fonts.googleapis.com/css2?family=Assistant&display=swap" rel="stylesheet">
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
@ -22,12 +25,9 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Squeaknode</title>
<meta name="description" content="Squeaknode is a frontend for accessing a squeak node">
<meta name="keywords" content="squeak, bitcoin, lightning">
<meta name="author" content="Flatlogic LLC.">
<title>Twitter Clone</title>
</head>
<body style="font-family: 'Roboto', sans-serif;">
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--

12
frontend/public/manifest.json Executable file → Normal file
View file

@ -1,15 +1,11 @@
{
"short_name": "Squeaknode",
"name": "Squeaknode is a frontend for accessing a squeak node",
"short_name": "Twitter Clone",
"name": "Twitter Clone",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#536DFE",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View file

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

89
frontend/src/App.js Normal file
View file

@ -0,0 +1,89 @@
import React, { Suspense, lazy } from 'react'
import { Route, Switch, HashRouter, Redirect, withRouter } from 'react-router-dom'
import { StoreProvider } from './store/store'
import 'dotenv/config'
import './App.scss'
import Loader from './components/Loader'
import Nav from './components/Nav'
import Login from './components/Login'
import Signup from './components/Signup'
import Tweet from './components/Tweet'
import Profiles from './components/Profiles'
import Payments from './components/Payments'
import Peers from './components/Peers'
import Feed from './components/Feed'
import Notifications from './components/Notifications'
import Alerts from './components/Alerts'
const Home = lazy(() => import('./components/Home'))
const Profile = lazy(() => import('./components/Profile'))
const Peer = lazy(() => import('./components/Peer'))
const DefaultContainer = withRouter(({ history }) => {
return (<div className="body-wrap">
<main className="main">
<div className={history.location.pathname.slice(0,9) !== '/messages' ? "middle-section ms-width" : "middle-section"}>
<Route path="/" exact>
<Redirect to="/app/home" />
</Route>
<Route path="/app/home" exact>
<Home />
</Route>
<Route path="/app/profile/:username" exact>
<Profile />
</Route>
<Route path="/app/peer/:network/:host/:port" exact>
<Peer />
</Route>
<Route path="/app/tweet/:id" exact>
<Tweet />
</Route>
<Route path="/app/profiles" exact>
<Profiles/>
</Route>
<Route path="/app/payments" exact>
<Payments/>
</Route>
<Route path="/app/peers" exact>
<Peers/>
</Route>
<Route path="/app/notifications" exact>
<Notifications/>
</Route>
</div>
{history.location.pathname.slice(0,9) !== '/messages' &&
<div className="right-section">
<Feed/>
</div>
}
</main>
<nav className="header">
<Nav />
</nav>
</div>)
});
function App() {
return (
<div className="dark-mode">
<StoreProvider>
<HashRouter>
<Suspense fallback={<Loader />}>
<Alerts />
<Switch>
<Route path="/login" exact>
<Login />
</Route>
<Route path="/signup" exact>
<Signup />
</Route>
<Route component={DefaultContainer} />
</Switch>
</Suspense>
</HashRouter>
</StoreProvider>
</div>
)
}
export default App

668
frontend/src/App.scss Normal file
View file

@ -0,0 +1,668 @@
.body-wrap{
display: flex;
flex-direction: row;
justify-content: center;
}
.header{
display: flex;
justify-content: flex-end;
position: relative;
order: -1;
flex-wrap: wrap;
}
.main{
width: 990px;
display: flex;
justify-content: space-between;
}
.middle-section{
max-width: 600px;
min-height: 100vh;
}
.ms-width{
width: 100%;
}
.right-section{
width: 350px;
margin-right: 10px;
min-height: 1000px;
height: 100%;
}
.modal-edit{
// display: none;
position: fixed;
z-index: 250;
// padding-top: 100px;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.4);
}
.modal-content{
min-height: 400px;
max-height: 90vh;
height: 650px;
width: 100%;
max-width: 600px;
border-radius: 14px;
background-color: #fff;
position: fixed;
top: 50%;
left: 50%;
z-index: 50;
transform: translate(-50%, -50%);
overflow: hidden;
}
.modal-header{
height: 53px;
z-index: 3;
display: flex;
align-items: center;
padding: 0 15px;
border-bottom: 1px solid rgb(204, 214, 221);
max-width: 1000px;
width: 100%;
}
.modal-closeIcon{
display: flex;
justify-content: flex-start;
margin-left: 4px;
align-items: center;
min-width: 59px;
min-height: 30px;
}
.modal-closeIcon-wrap{
display: flex;
align-items: center;
justify-content: center;
transition: 0.2s ease-in-out;
border: 1px solid rgba(0,0,0,0);
border-radius: 9999px;
width: 39px;
height: 39px;
cursor: pointer;
}
.modal-closeIcon-wrap:hover{
background-color: rgba(29,161,242,0.1);
}
.modal-closeIcon-wrap svg{
fill: rgb(29, 161, 242);
height: 22.5px;
}
.modal-title{
font-weight: bold;
font-size: 19px;
width: 100%;
}
.save-modal-wrapper{
margin-right: 8px;
min-height: 39px;
min-width: 66px;
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
}
.save-modal-btn{
// width: 48px;
min-height: 30px;
transition: 0.2s ease-in-out;
padding: 0 16px;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
color: #fff;
background-color: rgb(29,161,242);
border: 1px solid rgba(0,0,0,0);
border-radius: 9999px;
line-height: 20px;
cursor: pointer;
}
.save-modal-btn:hover{
background-color: rgb(26, 145, 218);
}
.modal-body{
display: flex;
flex-direction: column;
height: 100%;
}
.modal-banner{
max-height: 200px;
height: 200px;
display: flex;
justify-content: center;
border: 2px solid rgba(0, 0, 0, 0);
background-color: rgba(0, 0, 0, 0.3);
position: relative;
}
.modal-banner img{
max-width: 100%;
width: 100%;
max-height: 100%;
object-fit: cover;
display: block;
opacity: 0.75;
}
.modal-banner div{
position: absolute;
width: 100%;
height: 100%;
top: 0;
display: flex;
align-items: center;
justify-content: center;
}
.modal-banner div input{
width: 22.5px;
min-width: 22.5px;
height: 22.5px;
overflow: hidden;
z-index: 20;
padding: 10px 0 10px 30px;
cursor: pointer;
color: inherit;
background-color: initial;
outline: none;
border: initial;
}
.modal-banner div svg{
cursor: pointer;
position: absolute;
fill: #fff;
width: 22.5px;
min-width: 22.5px;
height: 22.5px;
}
.modal-scroll{
overflow-y: scroll;
height: 100%;
margin-bottom: 55px;
}
.modal-profile-pic{
height: 120px;
width: 120px;
border: 4px solid #fff;
border-radius: 50%;
margin-left: 16px;
margin-top: -48px;
z-index: 5;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
.modal-back-pic{
height: 100%;
width: 100%;
border-radius: 50%;
z-index: 5;
background-color: rgba(0, 0, 0, 1);
position: relative;
}
.modal-back-pic img{
width: 100%;
height: 100%;
object-fit: cover;
display: block;
opacity: 0.6;
border-radius: 50%;
}
.modal-back-pic div{
position: absolute;
width: 100%;
height: 100%;
top: 0;
display: flex;
align-items: center;
justify-content: center;
}
.modal-back-pic div input{
width: 22.5px;
min-width: 22.5px;
height: 22.5px;
overflow: hidden;
z-index: 20;
padding: 10px 0 10px 30px;
cursor: pointer;
color: inherit;
background-color: initial;
outline: none;
border: initial;
}
.modal-back-pic div svg{
cursor: pointer;
position: absolute;
fill: #fff;
width: 22.5px;
min-width: 22.5px;
height: 22.5px;
}
.edit-form{
width: 100%;
}
.edit-input{
background-color: inherit;
border: inherit;
}
.edit-input:focus{
background-color: inherit;
border: inherit;
}
.edit-input-wrap{
padding: 10px 15px;
margin-bottom: 15px;
}
.edit-input-content{
border-bottom: 1px solid rgb(64, 67, 70);
background-color: rgb(245, 248, 250);
label{
color: rgb(101, 119, 134);
display: block;
padding: 5px 10px 0 10px;
}
input{
width: 100%;
outline: none;
font-size: 19px;
padding: 2px 10px 5px 10px;
}
}
//////from home
.Tweet-input-wrapper{
padding: 10px 15px 5px 15px;
display: flex;
margin-bottom: 2px;
}
.Tweet-profile-wrapper{
flex-basis: 49px;
padding-top: 5px;
margin-right: 10px;
img{
object-fit: cover;
}
}
.Tweet-input-side{
display: flex;
flex-direction: column;
justify-content: space-between;
position: static;
width: calc(100% - 49px);
border: 2px solid rgba(0, 0, 0, 0);
border-radius: 5px;
padding-top: 5px;
line-height: 1.3125;
cursor: text;
}
.inner-input-box{
padding: 10px 0;
font-size: 19px;
color: #9197a3;
position: relative;
}
.inner-input-box div{
outline: none;
white-space: pre-wrap;
max-width: 506px;
}
.inner-input-box div:focus{
outline: none;
}
.inner-input-links{
display: flex;
justify-content: space-between;
margin: 0 2px;
}
.input-links-side{
margin-top: 10px;
display: flex;
align-items: center;
}
.input-attach-wrapper{
width: 39px;
height: 39px;
cursor: pointer;
padding: 8.3px;
position: relative;
input{
position: absolute;
width: 0.3px;
height: 0.3px;
overflow: hidden;
z-index: 4;
padding-top: 21px;
padding-bottom: 15px;
padding-right: 0px;
padding-left: 32px;
top: 2px;
left: 3px;
cursor: pointer;
outline: none;
color: inherit;
background-color: initial;
border: initial;
text-align: start !important;
}
}
.input-attach-wrapper:hover{
border-radius: 50%;
background-color: rgba(29, 161, 242,0.1);
}
.tweet-btn-side{
margin-left: 10px;
min-height: 39px;
min-width: calc(62.79px);
background-color: rgb(29, 161, 242);
padding: 0 1em;
border: 1px solid rgba(0, 0, 0, 0);
border-radius: 9999px;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-weight: 700;
// cursor: pointer;
opacity: 0.5;
transition: 0.15s ease-in-out;
}
.tweet-btn-active{
cursor: pointer;
opacity: 1;
&:hover{
background-color: rgba(11, 137, 216, 0.876);
}
}
.Tweet-input-divider{
min-height: 10px;
height: 10px;
background-color: rgb(230, 236, 240);
content: '';
}
[contenteditable=true]{display: inline-block;}
[contenteditable=true]:empty:before{
content: attr(placeholder);
pointer-events: none;
display: block; /* For Firefox */
}
/* */
[contenteditable=true]:empty:focus{
opacity: 0.7;
}
.card-content-info{
word-wrap: break-word;
}
.tweet-input-active{
color: rgb(20, 23, 26);
}
.tweet-upload-image{
margin-top: 10px;
border-radius: 14px;
max-height: 253px;
width: 100%;
height: 100%;
object-fit: cover;
}
.inner-image-box{
position: relative;
}
.cancel-image{
position: absolute;
color: white;
left: 9px;
top: 18px;
width: 33px;
align-items: center;
justify-content: center;
line-height: 23px;
text-align: center;
display: flex;
height: 33px;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 50%;
font-size: 22px;
font-weight: bold;
cursor: pointer;
padding-bottom: 3.5px;
}
.workInProgress{
max-width: 600px;
border-right: 1px solid rgb(230, 236, 240);
width: 100%;
font-weight: bold;
font-size: 17px;
text-align: center;
padding-top: 20%;
color: #657786;
min-height: 2000px;
}
// .dark-mode{
// background-color: #1a1919 !important;
// }
.alert-wrapper{
position: fixed;
top: 0;
}
.tweet-btn-holder{
margin-left: 10px;
display: flex;
align-items: center;
}
.header-back-wrapper{
margin-left: -5px;
width: 39px;
height: 39px;
transition: 0.2s ease-in-out;
will-change: background-color;
border: 1px solid rgba(0, 0, 0, 0);
border-radius: 9999px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
@media only screen and (max-width: 1196px) {
.right-section{
width: 290px !important;
}
.main{
width: 920px;
}
}
@media only screen and (max-width: 1005px) {
.right-section{
display: none;
}
.main{
width: 100%;
}
//flex grow
}
@media only screen and (max-width: 888px){
.chat-right{
display: none;
}
.messages-wrapper{
width: 100% !important;
}
.messages-header-wrapper{
max-width: 100% !important;
}
.middle-section{
width: 100%;
}
.chat-height{
height: 100vh !important;
}
}
@media only screen and (max-width: 450px){
body{
overflow-y: auto !important;
overflow-x: hidden;
width: 100%;
}
.chat-height {
height: calc(100vh - 46px) !important;
}
.chat-bottom-wrapper{
bottom: 50px !important;
}
.body-wrap{
flex-direction: column;
}
.header{
position: sticky;
width: 100vw;
bottom: 0;
height: 53px;
order: 1;
}
.Nav-component{
width: 100vw;
}
.Nav-width{
width: 100vw !important;
height: 53px;
}
.Nav{
top: auto !important;
width: 100vw;
position: relative !important;
}
.Nav-Content{
width: 100% !important;
display: block;
padding: 0 !important;
height: auto;
overflow: hidden;
}
.Nav-wrapper{
background-color: #fff;
border-top: 2px solid rgb(245, 248, 250);
display: flex;
align-content: center;
flex-direction: row !important;
margin: 0;
justify-content: space-evenly;
.Nav-link{
padding: 0;
}
a:nth-child(1){
display: none;
}
a:nth-child(4){
display: none;
}
a:nth-child(6){
display: none;
}
a:nth-child(9){
display: none;
}
}
.Nav-tweet{
display: none !important;
}
.more-menu-content{
top: auto !important;
bottom: 46px !important;
left: 47% !important;
overflow: hidden;
height: 154px;
}
.more-item{
display: flex !important;
}
}

9
frontend/src/App.test.js Normal file
View file

@ -0,0 +1,9 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

162
frontend/src/Icons.js Normal file
View file

@ -0,0 +1,162 @@
import React from 'react'
export const ICON_LOGO = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M23.643 4.937c-.835.37-1.732.62-2.675.733.962-.576 1.7-1.49 2.048-2.578-.9.534-1.897.922-2.958 1.13-.85-.904-2.06-1.47-3.4-1.47-2.572 0-4.658 2.086-4.658 4.66 0 .364.042.718.12 1.06-3.873-.195-7.304-2.05-9.602-4.868-.4.69-.63 1.49-.63 2.342 0 1.616.823 3.043 2.072 3.878-.764-.025-1.482-.234-2.11-.583v.06c0 2.257 1.605 4.14 3.737 4.568-.392.106-.803.162-1.227.162-.3 0-.593-.028-.877-.082.593 1.85 2.313 3.198 4.352 3.234-1.595 1.25-3.604 1.995-5.786 1.995-.376 0-.747-.022-1.112-.065 2.062 1.323 4.51 2.093 7.14 2.093 8.57 0 13.255-7.098 13.255-13.254 0-.2-.005-.402-.014-.602.91-.658 1.7-1.477 2.323-2.41z"></path></g></svg>
}
export const ICON_HOME = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M22.46 7.57L12.357 2.115c-.223-.12-.49-.12-.713 0L1.543 7.57c-.364.197-.5.652-.303 1.017.135.25.394.393.66.393.12 0 .243-.03.356-.09l.815-.44L4.7 19.963c.214 1.215 1.308 2.062 2.658 2.062h9.282c1.352 0 2.445-.848 2.663-2.087l1.626-11.49.818.442c.364.193.82.06 1.017-.304.196-.363.06-.818-.304-1.016zm-4.638 12.133c-.107.606-.703.822-1.18.822H7.36c-.48 0-1.075-.216-1.178-.798L4.48 7.69 12 3.628l7.522 4.06-1.7 12.015z"></path><path d="M8.22 12.184c0 2.084 1.695 3.78 3.78 3.78s3.78-1.696 3.78-3.78-1.695-3.78-3.78-3.78-3.78 1.696-3.78 3.78zm6.06 0c0 1.258-1.022 2.28-2.28 2.28s-2.28-1.022-2.28-2.28 1.022-2.28 2.28-2.28 2.28 1.022 2.28 2.28z"></path></g></svg>
}
export const ICON_HOMEFILL = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M22.58 7.35L12.475 1.897c-.297-.16-.654-.16-.95 0L1.425 7.35c-.486.264-.667.87-.405 1.356.18.335.525.525.88.525.16 0 .324-.038.475-.12l.734-.396 1.59 11.25c.216 1.214 1.31 2.062 2.66 2.062h9.282c1.35 0 2.444-.848 2.662-2.088l1.588-11.225.737.398c.485.263 1.092.082 1.354-.404.263-.486.08-1.093-.404-1.355zM12 15.435c-1.795 0-3.25-1.455-3.25-3.25s1.455-3.25 3.25-3.25 3.25 1.455 3.25 3.25-1.455 3.25-3.25 3.25z"></path></g></svg>
}
export const ICON_HASH = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M21 7.337h-3.93l.372-4.272c.036-.412-.27-.775-.682-.812-.417-.03-.776.27-.812.683l-.383 4.4h-6.32l.37-4.27c.037-.413-.27-.776-.68-.813-.42-.03-.777.27-.813.683l-.382 4.4H3.782c-.414 0-.75.337-.75.75s.336.75.75.75H7.61l-.55 6.327H3c-.414 0-.75.336-.75.75s.336.75.75.75h3.93l-.372 4.272c-.036.412.27.775.682.812l.066.003c.385 0 .712-.295.746-.686l.383-4.4h6.32l-.37 4.27c-.036.413.27.776.682.813l.066.003c.385 0 .712-.295.746-.686l.382-4.4h3.957c.413 0 .75-.337.75-.75s-.337-.75-.75-.75H16.39l.55-6.327H21c.414 0 .75-.336.75-.75s-.336-.75-.75-.75zm-6.115 7.826h-6.32l.55-6.326h6.32l-.55 6.326z"></path></g></svg>
}
export const ICON_HASHFILL = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M20.585 9.468c.66 0 1.2-.538 1.2-1.2 0-.662-.54-1.2-1.2-1.2h-3.22l.31-3.547c.027-.318-.07-.63-.277-.875-.206-.245-.495-.396-.822-.425-.65-.035-1.235.432-1.293 1.093l-.326 3.754H9.9l.308-3.545c.06-.658-.43-1.242-1.097-1.302-.665-.05-1.235.43-1.293 1.092l-.325 3.754h-3.33c-.663 0-1.2.538-1.2 1.2 0 .662.538 1.2 1.2 1.2h3.122l-.44 5.064H3.416c-.662 0-1.2.54-1.2 1.2s.538 1.202 1.2 1.202h3.22l-.31 3.548c-.057.657.432 1.24 1.09 1.3l.106.005c.626 0 1.14-.472 1.195-1.098l.327-3.753H14.1l-.308 3.544c-.06.658.43 1.242 1.09 1.302l.106.005c.617 0 1.142-.482 1.195-1.098l.325-3.753h3.33c.66 0 1.2-.54 1.2-1.2s-.54-1.202-1.2-1.202h-3.122l.44-5.064h3.43zm-5.838 0l-.44 5.063H9.253l.44-5.062h5.055z"></path></g></svg>
}
export const ICON_BELL = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M21.697 16.468c-.02-.016-2.14-1.64-2.103-6.03.02-2.532-.812-4.782-2.347-6.335C15.872 2.71 14.01 1.94 12.005 1.93h-.013c-2.004.01-3.866.78-5.242 2.174-1.534 1.553-2.368 3.802-2.346 6.334.037 4.33-2.02 5.967-2.102 6.03-.26.193-.366.53-.265.838.102.308.39.515.712.515h4.92c.102 2.31 1.997 4.16 4.33 4.16s4.226-1.85 4.327-4.16h4.922c.322 0 .61-.206.71-.514.103-.307-.003-.645-.263-.838zM12 20.478c-1.505 0-2.73-1.177-2.828-2.658h5.656c-.1 1.48-1.323 2.66-2.828 2.66zM4.38 16.32c.74-1.132 1.548-3.028 1.524-5.896-.018-2.16.644-3.982 1.913-5.267C8.91 4.05 10.397 3.437 12 3.43c1.603.008 3.087.62 4.18 1.728 1.27 1.285 1.933 3.106 1.915 5.267-.024 2.868.785 4.765 1.525 5.896H4.38z"></path></g></svg>
}
export const ICON_BELLFILL = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M21.697 16.468c-.02-.016-2.14-1.64-2.103-6.03.02-2.533-.812-4.782-2.347-6.334-1.375-1.393-3.237-2.164-5.242-2.172h-.013c-2.004.008-3.866.78-5.242 2.172-1.534 1.553-2.367 3.802-2.346 6.333.037 4.332-2.02 5.967-2.102 6.03-.26.194-.366.53-.265.838s.39.515.713.515h4.494c.1 2.544 2.188 4.587 4.756 4.587s4.655-2.043 4.756-4.587h4.494c.324 0 .61-.208.712-.515s-.005-.644-.265-.837zM12 20.408c-1.466 0-2.657-1.147-2.756-2.588h5.512c-.1 1.44-1.29 2.587-2.756 2.587z"></path></g></svg>
}
export const ICON_INBOX = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M19.25 3.018H4.75C3.233 3.018 2 4.252 2 5.77v12.495c0 1.518 1.233 2.753 2.75 2.753h14.5c1.517 0 2.75-1.235 2.75-2.753V5.77c0-1.518-1.233-2.752-2.75-2.752zm-14.5 1.5h14.5c.69 0 1.25.56 1.25 1.25v.714l-8.05 5.367c-.273.18-.626.182-.9-.002L3.5 6.482v-.714c0-.69.56-1.25 1.25-1.25zm14.5 14.998H4.75c-.69 0-1.25-.56-1.25-1.25V8.24l7.24 4.83c.383.256.822.384 1.26.384.44 0 .877-.128 1.26-.383l7.24-4.83v10.022c0 .69-.56 1.25-1.25 1.25z"></path></g></svg>
}
export const ICON_INBOXFILL = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M11.55 12.082c.273.182.627.182.9 0L22 5.716V5.5c0-1.24-1.01-2.25-2.25-2.25H4.25C3.01 3.25 2 4.26 2 5.5v.197l9.55 6.385z"></path><path d="M13.26 13.295c-.383.255-.82.382-1.26.382s-.877-.127-1.26-.383L2 7.452v11.67c0 1.24 1.01 2.25 2.25 2.25h15.5c1.24 0 2.25-1.01 2.25-2.25V7.47l-8.74 5.823z"></path></g></svg>
}
export const ICON_BOOKMARK = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M19.9 23.5c-.157 0-.312-.05-.442-.144L12 17.928l-7.458 5.43c-.228.164-.53.19-.782.06-.25-.127-.41-.385-.41-.667V5.6c0-1.24 1.01-2.25 2.25-2.25h12.798c1.24 0 2.25 1.01 2.25 2.25v17.15c0 .282-.158.54-.41.668-.106.055-.223.082-.34.082zM12 16.25c.155 0 .31.048.44.144l6.71 4.883V5.6c0-.412-.337-.75-.75-.75H5.6c-.413 0-.75.338-.75.75v15.677l6.71-4.883c.13-.096.285-.144.44-.144z"></path></g></svg>
}
export const ICON_BOOKMARKFILL = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M19.9 23.5c-.2 0-.3 0-.4-.1L12 17.9l-7.5 5.4c-.2.2-.5.2-.8.1-.2-.1-.4-.4-.4-.7V5.6c0-1.2 1-2.2 2.2-2.2h12.8c1.2 0 2.2 1 2.2 2.2v17.1c0 .3-.2.5-.4.7 0 .1-.1.1-.2.1z"></path></g></svg>
}
export const ICON_LIST = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M19.75 22H4.25C3.01 22 2 20.99 2 19.75V4.25C2 3.01 3.01 2 4.25 2h15.5C20.99 2 22 3.01 22 4.25v15.5c0 1.24-1.01 2.25-2.25 2.25zM4.25 3.5c-.414 0-.75.337-.75.75v15.5c0 .413.336.75.75.75h15.5c.414 0 .75-.337.75-.75V4.25c0-.413-.336-.75-.75-.75H4.25z"></path><path d="M17 8.64H7c-.414 0-.75-.337-.75-.75s.336-.75.75-.75h10c.414 0 .75.335.75.75s-.336.75-.75.75zm0 4.11H7c-.414 0-.75-.336-.75-.75s.336-.75.75-.75h10c.414 0 .75.336.75.75s-.336.75-.75.75zm-5 4.11H7c-.414 0-.75-.335-.75-.75s.336-.75.75-.75h5c.414 0 .75.337.75.75s-.336.75-.75.75z"></path></g></svg>
}
export const ICON_LISTFILL = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M19.75 2H4.25C3.013 2 2 3.013 2 4.25v15.5C2 20.987 3.013 22 4.25 22h15.5c1.237 0 2.25-1.013 2.25-2.25V4.25C22 3.013 20.987 2 19.75 2zM11 16.75H7c-.414 0-.75-.336-.75-.75s.336-.75.75-.75h4c.414 0 .75.336.75.75s-.336.75-.75.75zm6-4H7c-.414 0-.75-.336-.75-.75s.336-.75.75-.75h10c.414 0 .75.336.75.75s-.336.75-.75.75zm0-4H7c-.414 0-.75-.336-.75-.75s.336-.75.75-.75h10c.414 0 .75.336.75.75s-.336.75-.75.75z"></path></g></svg>
}
export const ICON_USER = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M12 11.816c1.355 0 2.872-.15 3.84-1.256.814-.93 1.078-2.368.806-4.392-.38-2.825-2.117-4.512-4.646-4.512S7.734 3.343 7.354 6.17c-.272 2.022-.008 3.46.806 4.39.968 1.107 2.485 1.256 3.84 1.256zM8.84 6.368c.162-1.2.787-3.212 3.16-3.212s2.998 2.013 3.16 3.212c.207 1.55.057 2.627-.45 3.205-.455.52-1.266.743-2.71.743s-2.255-.223-2.71-.743c-.507-.578-.657-1.656-.45-3.205zm11.44 12.868c-.877-3.526-4.282-5.99-8.28-5.99s-7.403 2.464-8.28 5.99c-.172.692-.028 1.4.395 1.94.408.52 1.04.82 1.733.82h12.304c.693 0 1.325-.3 1.733-.82.424-.54.567-1.247.394-1.94zm-1.576 1.016c-.126.16-.316.246-.552.246H5.848c-.235 0-.426-.085-.552-.246-.137-.174-.18-.412-.12-.654.71-2.855 3.517-4.85 6.824-4.85s6.114 1.994 6.824 4.85c.06.242.017.48-.12.654z"></path></g></svg>
}
export const ICON_USERFILL = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M12.225 12.165c-1.356 0-2.872-.15-3.84-1.256-.814-.93-1.077-2.368-.805-4.392.38-2.826 2.116-4.513 4.646-4.513s4.267 1.687 4.646 4.513c.272 2.024.008 3.46-.806 4.392-.97 1.106-2.485 1.255-3.84 1.255zm5.849 9.85H6.376c-.663 0-1.25-.28-1.65-.786-.422-.534-.576-1.27-.41-1.968.834-3.53 4.086-5.997 7.908-5.997s7.074 2.466 7.91 5.997c.164.698.01 1.434-.412 1.967-.4.505-.985.785-1.648.785z"></path></g></svg>
}
export const ICON_LAPTOP = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M13.5 3a.5.5 0 0 1 .5.5V11H2V3.5a.5.5 0 0 1 .5-.5h11zm-11-1A1.5 1.5 0 0 0 1 3.5V12h14V3.5A1.5 1.5 0 0 0 13.5 2h-11zM0 12.5h16a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 12.5z"></path></g></svg>
}
export const ICON_LAPTOPFILL = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M2.5 2A1.5 1.5 0 0 0 1 3.5V12h14V3.5A1.5 1.5 0 0 0 13.5 2h-11zM0 12.5h16a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 12.5z"></path></g></svg>
}
export const ICON_LOCK = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2zM5 8h6a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1z"></path></g></svg>
}
export const ICON_LOCKFILL = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z"></path></g></svg>
}
export const ICON_SETTINGS = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M16.5 10.25c-.965 0-1.75.787-1.75 1.75s.784 1.75 1.75 1.75c.964 0 1.75-.786 1.75-1.75s-.786-1.75-1.75-1.75zm0 2.5c-.414 0-.75-.336-.75-.75 0-.413.337-.75.75-.75s.75.336.75.75c0 .413-.336.75-.75.75zm-4.5-2.5c-.966 0-1.75.787-1.75 1.75s.785 1.75 1.75 1.75 1.75-.786 1.75-1.75-.784-1.75-1.75-1.75zm0 2.5c-.414 0-.75-.336-.75-.75 0-.413.337-.75.75-.75s.75.336.75.75c0 .413-.336.75-.75.75zm-4.5-2.5c-.965 0-1.75.787-1.75 1.75s.785 1.75 1.75 1.75c.964 0 1.75-.786 1.75-1.75s-.787-1.75-1.75-1.75zm0 2.5c-.414 0-.75-.336-.75-.75 0-.413.337-.75.75-.75s.75.336.75.75c0 .413-.336.75-.75.75z"></path><path d="M12 22.75C6.072 22.75 1.25 17.928 1.25 12S6.072 1.25 12 1.25 22.75 6.072 22.75 12 17.928 22.75 12 22.75zm0-20C6.9 2.75 2.75 6.9 2.75 12S6.9 21.25 12 21.25s9.25-4.15 9.25-9.25S17.1 2.75 12 2.75z"></path></g></svg>
}
export const ICON_TWEET = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24" class="r-jwli3a r-4qtqp9 r-yyyyoo r-1q142lx r-50lct3 r-dnmrzs r-bnwqim r-1plcrui r-lrvibr"><g><path d="M8.8 7.2H5.6V3.9c0-.4-.3-.8-.8-.8s-.7.4-.7.8v3.3H.8c-.4 0-.8.3-.8.8s.3.8.8.8h3.3v3.3c0 .4.3.8.8.8s.8-.3.8-.8V8.7H9c.4 0 .8-.3.8-.8s-.5-.7-1-.7zm15-4.9v-.1h-.1c-.1 0-9.2 1.2-14.4 11.7-3.8 7.6-3.6 9.9-3.3 9.9.3.1 3.4-6.5 6.7-9.2 5.2-1.1 6.6-3.6 6.6-3.6s-1.5.2-2.1.2c-.8 0-1.4-.2-1.7-.3 1.3-1.2 2.4-1.5 3.5-1.7.9-.2 1.8-.4 3-1.2 2.2-1.6 1.9-5.5 1.8-5.7z"></path></g></svg>
}
export const ICON_IMGUPLOAD = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M19.75 2H4.25C3.01 2 2 3.01 2 4.25v15.5C2 20.99 3.01 22 4.25 22h15.5c1.24 0 2.25-1.01 2.25-2.25V4.25C22 3.01 20.99 2 19.75 2zM4.25 3.5h15.5c.413 0 .75.337.75.75v9.676l-3.858-3.858c-.14-.14-.33-.22-.53-.22h-.003c-.2 0-.393.08-.532.224l-4.317 4.384-1.813-1.806c-.14-.14-.33-.22-.53-.22-.193-.03-.395.08-.535.227L3.5 17.642V4.25c0-.413.337-.75.75-.75zm-.744 16.28l5.418-5.534 6.282 6.254H4.25c-.402 0-.727-.322-.744-.72zm16.244.72h-2.42l-5.007-4.987 3.792-3.85 4.385 4.384v3.703c0 .413-.337.75-.75.75z"></path><circle cx="8.868" cy="8.309" r="1.542"></circle></g></svg>
}
export const ICON_REPLY = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M14.046 2.242l-4.148-.01h-.002c-4.374 0-7.8 3.427-7.8 7.802 0 4.098 3.186 7.206 7.465 7.37v3.828c0 .108.044.286.12.403.142.225.384.347.632.347.138 0 .277-.038.402-.118.264-.168 6.473-4.14 8.088-5.506 1.902-1.61 3.04-3.97 3.043-6.312v-.017c-.006-4.367-3.43-7.787-7.8-7.788zm3.787 12.972c-1.134.96-4.862 3.405-6.772 4.643V16.67c0-.414-.335-.75-.75-.75h-.396c-3.66 0-6.318-2.476-6.318-5.886 0-3.534 2.768-6.302 6.3-6.302l4.147.01h.002c3.532 0 6.3 2.766 6.302 6.296-.003 1.91-.942 3.844-2.514 5.176z"></path></g></svg>
}
export const ICON_RETWEET = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M23.77 15.67c-.292-.293-.767-.293-1.06 0l-2.22 2.22V7.65c0-2.068-1.683-3.75-3.75-3.75h-5.85c-.414 0-.75.336-.75.75s.336.75.75.75h5.85c1.24 0 2.25 1.01 2.25 2.25v10.24l-2.22-2.22c-.293-.293-.768-.293-1.06 0s-.294.768 0 1.06l3.5 3.5c.145.147.337.22.53.22s.383-.072.53-.22l3.5-3.5c.294-.292.294-.767 0-1.06zm-10.66 3.28H7.26c-1.24 0-2.25-1.01-2.25-2.25V6.46l2.22 2.22c.148.147.34.22.532.22s.384-.073.53-.22c.293-.293.293-.768 0-1.06l-3.5-3.5c-.293-.294-.768-.294-1.06 0l-3.5 3.5c-.294.292-.294.767 0 1.06s.767.293 1.06 0l2.22-2.22V16.7c0 2.068 1.683 3.75 3.75 3.75h5.85c.414 0 .75-.336.75-.75s-.337-.75-.75-.75z"></path></g></svg>
}
export const ICON_HEART = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M12 21.638h-.014C9.403 21.59 1.95 14.856 1.95 8.478c0-3.064 2.525-5.754 5.403-5.754 2.29 0 3.83 1.58 4.646 2.73.814-1.148 2.354-2.73 4.645-2.73 2.88 0 5.404 2.69 5.404 5.755 0 6.376-7.454 13.11-10.037 13.157H12zM7.354 4.225c-2.08 0-3.903 1.988-3.903 4.255 0 5.74 7.034 11.596 8.55 11.658 1.518-.062 8.55-5.917 8.55-11.658 0-2.267-1.823-4.255-3.903-4.255-2.528 0-3.94 2.936-3.952 2.965-.23.562-1.156.562-1.387 0-.014-.03-1.425-2.965-3.954-2.965z"></path></g></svg>
}
export const ICON_HEARTFULL = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M12 21.638h-.014C9.403 21.59 1.95 14.856 1.95 8.478c0-3.064 2.525-5.754 5.403-5.754 2.29 0 3.83 1.58 4.646 2.73.814-1.148 2.354-2.73 4.645-2.73 2.88 0 5.404 2.69 5.404 5.755 0 6.376-7.454 13.11-10.037 13.157H12z"></path></g></svg>
}
export const ICON_SHARE = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M17.53 7.47l-5-5c-.293-.293-.768-.293-1.06 0l-5 5c-.294.293-.294.768 0 1.06s.767.294 1.06 0l3.72-3.72V15c0 .414.336.75.75.75s.75-.336.75-.75V4.81l3.72 3.72c.146.147.338.22.53.22s.384-.072.53-.22c.293-.293.293-.767 0-1.06z"></path><path d="M19.708 21.944H4.292C3.028 21.944 2 20.916 2 19.652V14c0-.414.336-.75.75-.75s.75.336.75.75v5.652c0 .437.355.792.792.792h15.416c.437 0 .792-.355.792-.792V14c0-.414.336-.75.75-.75s.75.336.75.75v5.652c0 1.264-1.028 2.292-2.292 2.292z"></path></g></svg>
}
export const ICON_ARROWBACK = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M20 11H7.414l4.293-4.293c.39-.39.39-1.023 0-1.414s-1.023-.39-1.414 0l-6 6c-.39.39-.39 1.023 0 1.414l6 6c.195.195.45.293.707.293s.512-.098.707-.293c.39-.39.39-1.023 0-1.414L7.414 13H20c.553 0 1-.447 1-1s-.447-1-1-1z"></path></g></svg>
}
export const ICON_MARKDOWN = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M12 14.315c-2.088 0-3.787-1.698-3.787-3.786S9.913 6.74 12 6.74s3.787 1.7 3.787 3.787-1.7 3.785-3.787 3.785zm0-6.073c-1.26 0-2.287 1.026-2.287 2.287S10.74 12.814 12 12.814s2.287-1.025 2.287-2.286S13.26 8.24 12 8.24z"></path><path d="M20.692 10.69C20.692 5.9 16.792 2 12 2s-8.692 3.9-8.692 8.69c0 1.902.603 3.708 1.743 5.223l.003-.002.007.015c1.628 2.07 6.278 5.757 6.475 5.912.138.11.302.163.465.163.163 0 .327-.053.465-.162.197-.155 4.847-3.84 6.475-5.912l.007-.014.002.002c1.14-1.516 1.742-3.32 1.742-5.223zM12 20.29c-1.224-.99-4.52-3.715-5.756-5.285-.94-1.25-1.436-2.742-1.436-4.312C4.808 6.727 8.035 3.5 12 3.5s7.192 3.226 7.192 7.19c0 1.57-.497 3.062-1.436 4.313-1.236 1.57-4.532 4.294-5.756 5.285z"></path></g></svg>
}
export const ICON_DATE = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M19.708 2H4.292C3.028 2 2 3.028 2 4.292v15.416C2 20.972 3.028 22 4.292 22h15.416C20.972 22 22 20.972 22 19.708V4.292C22 3.028 20.972 2 19.708 2zm.792 17.708c0 .437-.355.792-.792.792H4.292c-.437 0-.792-.355-.792-.792V6.418c0-.437.354-.79.79-.792h15.42c.436 0 .79.355.79.79V19.71z"></path><circle cx="7.032" cy="8.75" r="1.285"></circle><circle cx="7.032" cy="13.156" r="1.285"></circle><circle cx="16.968" cy="8.75" r="1.285"></circle><circle cx="16.968" cy="13.156" r="1.285"></circle><circle cx="12" cy="8.75" r="1.285"></circle><circle cx="12" cy="13.156" r="1.285"></circle><circle cx="7.032" cy="17.486" r="1.285"></circle><circle cx="12" cy="17.486" r="1.285"></circle></g></svg>
}
export const ICON_CLOSE = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M13.414 12l5.793-5.793c.39-.39.39-1.023 0-1.414s-1.023-.39-1.414 0L12 10.586 6.207 4.793c-.39-.39-1.023-.39-1.414 0s-.39 1.023 0 1.414L10.586 12l-5.793 5.793c-.39.39-.39 1.023 0 1.414.195.195.45.293.707.293s.512-.098.707-.293L12 13.414l5.793 5.793c.195.195.45.293.707.293s.512-.098.707-.293c.39-.39.39-1.023 0-1.414L13.414 12z"></path></g></svg>
}
export const ICON_DELETE = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M20.746 5.236h-3.75V4.25c0-1.24-1.01-2.25-2.25-2.25h-5.5c-1.24 0-2.25 1.01-2.25 2.25v.986h-3.75c-.414 0-.75.336-.75.75s.336.75.75.75h.368l1.583 13.262c.216 1.193 1.31 2.027 2.658 2.027h8.282c1.35 0 2.442-.834 2.664-2.072l1.577-13.217h.368c.414 0 .75-.336.75-.75s-.335-.75-.75-.75zM8.496 4.25c0-.413.337-.75.75-.75h5.5c.413 0 .75.337.75.75v.986h-7V4.25zm8.822 15.48c-.1.55-.664.795-1.18.795H7.854c-.517 0-1.083-.246-1.175-.75L5.126 6.735h13.74L17.32 19.732z"></path><path d="M10 17.75c.414 0 .75-.336.75-.75v-7c0-.414-.336-.75-.75-.75s-.75.336-.75.75v7c0 .414.336.75.75.75zm4 0c.414 0 .75-.336.75-.75v-7c0-.414-.336-.75-.75-.75s-.75.336-.75.75v7c0 .414.336.75.75.75z"></path></g></svg>
}
export const ICON_UPLOAD = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M19.708 22H4.292C3.028 22 2 20.972 2 19.708V7.375C2 6.11 3.028 5.083 4.292 5.083h2.146C7.633 3.17 9.722 2 12 2c2.277 0 4.367 1.17 5.562 3.083h2.146C20.972 5.083 22 6.11 22 7.375v12.333C22 20.972 20.972 22 19.708 22zM4.292 6.583c-.437 0-.792.355-.792.792v12.333c0 .437.355.792.792.792h15.416c.437 0 .792-.355.792-.792V7.375c0-.437-.355-.792-.792-.792h-2.45c-.317.05-.632-.095-.782-.382-.88-1.665-2.594-2.7-4.476-2.7-1.883 0-3.598 1.035-4.476 2.702-.16.302-.502.46-.833.38H4.293z"></path><path d="M12 8.167c-2.68 0-4.86 2.18-4.86 4.86s2.18 4.86 4.86 4.86 4.86-2.18 4.86-4.86-2.18-4.86-4.86-4.86zm2 5.583h-1.25V15c0 .414-.336.75-.75.75s-.75-.336-.75-.75v-1.25H10c-.414 0-.75-.336-.75-.75s.336-.75.75-.75h1.25V11c0-.414.336-.75.75-.75s.75.336.75.75v1.25H14c.414 0 .75.336.75.75s-.336.75-.75.75z"></path></g></svg>
}
export const ICON_NEWLIST = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M23.25 3.25h-2.425V.825c0-.414-.336-.75-.75-.75s-.75.336-.75.75V3.25H16.9c-.414 0-.75.336-.75.75s.336.75.75.75h2.425v2.425c0 .414.336.75.75.75s.75-.336.75-.75V4.75h2.425c.414 0 .75-.336.75-.75s-.336-.75-.75-.75zM18.575 22H4.25C3.01 22 2 20.99 2 19.75V5.5c0-1.24 1.01-2.25 2.25-2.25h8.947c.414 0 .75.336.75.75s-.336.75-.75.75H4.25c-.413 0-.75.336-.75.75v14.25c0 .414.337.75.75.75h14.325c.413 0 .75-.336.75-.75v-8.872c0-.414.336-.75.75-.75s.75.336.75.75v8.872c0 1.24-1.01 2.25-2.25 2.25z"></path><path d="M16.078 9.583H6.673c-.414 0-.75-.336-.75-.75s.336-.75.75-.75h9.405c.414 0 .75.336.75.75s-.336.75-.75.75zm0 3.867H6.673c-.414 0-.75-.337-.75-.75s.336-.75.75-.75h9.405c.414 0 .75.335.75.75s-.336.75-.75.75zm-4.703 3.866H6.673c-.414 0-.75-.336-.75-.75s.336-.75.75-.75h4.702c.414 0 .75.336.75.75s-.336.75-.75.75z"></path></g></svg>
}
export const ICON_SEARCH = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M21.53 20.47l-3.66-3.66C19.195 15.24 20 13.214 20 11c0-4.97-4.03-9-9-9s-9 4.03-9 9 4.03 9 9 9c2.215 0 4.24-.804 5.808-2.13l3.66 3.66c.147.146.34.22.53.22s.385-.073.53-.22c.295-.293.295-.767.002-1.06zM3.5 11c0-4.135 3.365-7.5 7.5-7.5s7.5 3.365 7.5 7.5-3.365 7.5-7.5 7.5-7.5-3.365-7.5-7.5z"></path></g></svg>
}
export const ICON_FEATHER = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M8.8 7.2H5.6V3.9c0-.4-.3-.8-.8-.8s-.7.4-.7.8v3.3H.8c-.4 0-.8.3-.8.8s.3.8.8.8h3.3v3.3c0 .4.3.8.8.8s.8-.3.8-.8V8.7H9c.4 0 .8-.3.8-.8s-.5-.7-1-.7zm15-4.9v-.1h-.1c-.1 0-9.2 1.2-14.4 11.7-3.8 7.6-3.6 9.9-3.3 9.9.3.1 3.4-6.5 6.7-9.2 5.2-1.1 6.6-3.6 6.6-3.6s-1.5.2-2.1.2c-.8 0-1.4-.2-1.7-.3 1.3-1.2 2.4-1.5 3.5-1.7.9-.2 1.8-.4 3-1.2 2.2-1.6 1.9-5.5 1.8-5.7z"></path></g></svg>
}
export const ICON_LIGHT = (props) => {
return <svg style={props.styles} viewBox="0 0 512 512"><g><path fill="currentColor" d="M502.42 240.5l-94.7-47.3 33.5-100.4c4.5-13.6-8.4-26.5-21.9-21.9l-100.4 33.5-47.41-94.8a17.31 17.31 0 0 0-31 0l-47.3 94.7L92.7 70.8c-13.6-4.5-26.5 8.4-21.9 21.9l33.5 100.4-94.7 47.4a17.31 17.31 0 0 0 0 31l94.7 47.3-33.5 100.5c-4.5 13.6 8.4 26.5 21.9 21.9l100.41-33.5 47.3 94.7a17.31 17.31 0 0 0 31 0l47.31-94.7 100.4 33.5c13.6 4.5 26.5-8.4 21.9-21.9l-33.5-100.4 94.7-47.3a17.33 17.33 0 0 0 .2-31.1zm-155.9 106c-49.91 49.9-131.11 49.9-181 0a128.13 128.13 0 0 1 0-181c49.9-49.9 131.1-49.9 181 0a128.13 128.13 0 0 1 0 181z"></path><path fill="currentColor" d="M352 256a96 96 0 1 1-96-96 96.15 96.15 0 0 1 96 96z"></path></g></svg>
}
export const ICON_DARK = (props) => {
return <svg style={props.styles} viewBox="0 0 512 512"><g><path fill="currentColor" d="M320 32L304 0l-16 32-32 16 32 16 16 32 16-32 32-16zm138.7 149.3L432 128l-26.7 53.3L352 208l53.3 26.7L432 288l26.7-53.3L512 208z" ></path><path fill="currentColor" d="M332.2 426.4c8.1-1.6 13.9 8 8.6 14.5a191.18 191.18 0 0 1-149 71.1C85.8 512 0 426 0 320c0-120 108.7-210.6 227-188.8 8.2 1.6 10.1 12.6 2.8 16.7a150.3 150.3 0 0 0-76.1 130.8c0 94 85.4 165.4 178.5 147.7z"></path></g></svg>
}
export const ICON_NEWMSG = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M23.25 3.25h-2.425V.825c0-.414-.336-.75-.75-.75s-.75.336-.75.75V3.25H16.9c-.414 0-.75.336-.75.75s.336.75.75.75h2.425v2.425c0 .414.336.75.75.75s.75-.336.75-.75V4.75h2.425c.414 0 .75-.336.75-.75s-.336-.75-.75-.75zm-3.175 6.876c-.414 0-.75.336-.75.75v8.078c0 .414-.337.75-.75.75H4.095c-.412 0-.75-.336-.75-.75V8.298l6.778 4.518c.368.246.79.37 1.213.37.422 0 .844-.124 1.212-.37l4.53-3.013c.336-.223.428-.676.204-1.012-.223-.332-.675-.425-1.012-.2l-4.53 3.014c-.246.162-.563.163-.808 0l-7.586-5.06V5.5c0-.414.337-.75.75-.75h9.094c.414 0 .75-.336.75-.75s-.336-.75-.75-.75H4.096c-1.24 0-2.25 1.01-2.25 2.25v13.455c0 1.24 1.01 2.25 2.25 2.25h14.48c1.24 0 2.25-1.01 2.25-2.25v-8.078c0-.415-.337-.75-.75-.75z"></path></g></svg>
}
export const ICON_SEND = (props) => {
return <svg style={props.styles} viewBox="0 0 24 24"><g><path d="M21.13 11.358L3.614 2.108c-.29-.152-.64-.102-.873.126-.23.226-.293.577-.15.868l4.362 8.92-4.362 8.92c-.143.292-.08.643.15.868.145.14.333.212.523.212.12 0 .24-.028.35-.087l17.517-9.25c.245-.13.4-.386.4-.664s-.155-.532-.4-.662zM4.948 4.51l12.804 6.762H8.255l-3.307-6.76zm3.307 8.26h9.498L4.948 19.535l3.307-6.763z"></path></g></svg>
}

View file

@ -1,10 +0,0 @@
export function getBlockDetailUrl(blockHash, network) {
switch (network) {
case 'mainnet':
return `https://blockstream.info/block/${blockHash}`;
case 'testnet':
return `https://blockstream.info/testnet/block/${blockHash}`;
default:
return '';
}
}

View file

@ -1,195 +0,0 @@
import React, { useState } from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
TextField,
DialogActions,
Button,
FormControl,
InputLabel,
Select,
MenuItem,
} from '@material-ui/core';
// styles
import useStyles from './styles';
import {
addTwitterAccountRequest,
getSigningProfilesRequest,
} from '../../squeakclient/requests';
export default function AddTwitterAccountDialog({
open,
handleClose,
reloadAccountsFn,
...props
}) {
const classes = useStyles();
const [twitterHandle, setTwitterHandle] = useState('');
const [bearerToken, setBearerToken] = useState('');
const [profileId, setProfileId] = useState(-1);
const [signingProfiles, setSigningProfiles] = useState([]);
const resetFields = () => {
setTwitterHandle('');
setBearerToken('');
setProfileId(-1);
};
const handleChangeTwitterHandle = (event) => {
setTwitterHandle(event.target.value);
};
const handleChangeBearerToken = (event) => {
setBearerToken(event.target.value);
};
const handleChangeProfileId = (event) => {
setProfileId(event.target.value);
};
const handleResponse = (response) => {
reloadAccountsFn();
};
const handleErr = (err) => {
alert(`Error adding twitter account: ${err}`);
};
const addTwitterAccount = (twitterHandle, profileId, bearerToken) => {
addTwitterAccountRequest(twitterHandle, profileId, bearerToken, handleResponse, handleErr);
};
const loadSigningProfiles = () => {
getSigningProfilesRequest(setSigningProfiles);
};
function handleSubmit(event) {
event.preventDefault();
console.log('twitterHandle:', twitterHandle);
console.log('profileId:', profileId);
if (profileId === -1) {
alert('Signing profile must be selected.');
return;
}
if (!twitterHandle) {
alert('twitterHandle cannot be empty.');
return;
}
if (!bearerToken) {
alert('bearerToken cannot be empty.');
return;
}
addTwitterAccount(twitterHandle, profileId, bearerToken);
handleClose();
}
function load(event) {
loadSigningProfiles();
}
function cancel(event) {
event.stopPropagation();
handleClose();
}
function ignore(event) {
event.stopPropagation();
}
function AccountHandleInput() {
return (
<TextField
id="standard-textarea"
label="Twitter Handle"
variant="outlined"
margin="normal"
required
autoFocus
value={twitterHandle}
onChange={handleChangeTwitterHandle}
fullWidth
inputProps={{ maxLength: 128 }}
/>
);
}
function BearerTokenInput() {
return (
<TextField
id="standard-textarea-bearer-token"
label="Bearer Token"
variant="outlined"
margin="normal"
required
value={bearerToken}
onChange={handleChangeBearerToken}
fullWidth
inputProps={{ maxLength: 128 }}
/>
);
}
function SelectSigningProfile() {
return (
<FormControl className={classes.formControl} required style={{ minWidth: 120 }}>
<InputLabel id="demo-simple-select-label">Signing Profile</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
variant="outlined"
margin="normal"
value={profileId}
onChange={handleChangeProfileId}
>
{signingProfiles.map((p) => <MenuItem key={p.getProfileId()} value={p.getProfileId()}>{p.getProfileName()}</MenuItem>)}
</Select>
</FormControl>
);
}
function CancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function AddTwitterAccountButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Add Twitter Account
</Button>
);
}
return (
<Dialog open={open} onRendered={load} onEnter={resetFields} onClose={cancel} onClick={ignore} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Add Twitter Account</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
{AccountHandleInput()}
{BearerTokenInput()}
{SelectSigningProfile()}
</DialogContent>
<DialogActions>
{CancelButton()}
{AddTwitterAccountButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "AddTwitterAccountDialog",
"version": "0.0.0",
"private": true,
"main": "AddTwitterAccountDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -0,0 +1,17 @@
import React, {useContext} from 'react'
import { StoreContext } from '../../store/store'
import './style.scss'
const Alert = (props) => {
const { state, actions } = useContext(StoreContext)
return(
<div style={{top: state.top}} className="alert-wrapper">
<div className="alert-content">
{state.msg}
</div>
</div>
)
}
export default Alert

View file

@ -0,0 +1,23 @@
.alert-wrapper{
position: fixed;
top: 100px;
width: 150px;
left: 50%;
transform: translate(-50%, 0);
text-align: center;
z-index: 1000;
position: fixed;
transition: top 0.4s ease-in;
}
.alert-content{
background-color: #1da1f2;
color: #fff;
font-weight: 600;
border: 1px solid #e6ecf0;
-webkit-box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
display:inline-block;
padding: 8px 14px;
border-radius: 200px;
}

View file

@ -1,69 +0,0 @@
import React from 'react';
import {
HashRouter, Route, Switch, Redirect,
} from 'react-router-dom';
// components
import Layout from './Layout';
// pages
import Error from '../pages/error';
import Login from '../pages/login';
// context
// import { useUserState } from '../context/UserContext';
export default function App() {
// global
// const { isAuthenticated } = useUserState();
return (
<HashRouter>
<Switch>
<Route exact path="/" render={() => <Redirect to="/app/timeline" />} />
<Route
exact
path="/app"
render={() => <Redirect to="/app/timeline" />}
/>
<PublicRoute path="/app" component={Layout} />
{' '}
<PublicRoute path="/login" component={Login} />
<Route component={Error} />
</Switch>
</HashRouter>
);
// #######################################################################
// function PrivateRoute({ component, ...rest }) {
// return (
// <Route
// {...rest}
// render={props =>
// isAuthenticated ? (
// React.createElement(component, props)
// ) : (
// <Redirect
// to={{
// pathname: "/login",
// state: {
// from: props.location,
// },
// }}
// />
// )
// }
// />
// );
// }
function PublicRoute({ component, ...rest }) {
return (
<Route
{...rest}
render={(props) => React.createElement(component, props)}
/>
);
}
}

View file

@ -1,162 +0,0 @@
import React from 'react';
import {
Typography,
Grid,
Box,
Link,
} from '@material-ui/core';
import { useHistory } from 'react-router-dom';
// styles
import moment from 'moment';
import {
goToPeerAddressPage,
goToLightningNodePage,
} from '../../navigation/navigation';
export default function BuyOfferDetailItem({
offer,
...props
}) {
const history = useHistory();
const onPeerClick = (event) => {
event.preventDefault();
goToPeerAddressPage(
history,
offer.getPeerAddress().getNetwork(),
offer.getPeerAddress().getHost(),
offer.getPeerAddress().getPort(),
);
};
const onLightningNodeClick = (event) => {
event.preventDefault();
goToLightningNodePage(
history,
offer.getNodePubkey(),
offer.getNodeHost(),
offer.getNodePort(),
);
};
function OfferContent() {
return (
<Typography
variant="h4"
>
Price:
{' '}
{offer.getPriceMsat() / 1000}
{' '}
sats
</Typography>
);
}
function PeerInfoContent() {
console.log(offer);
const peerAddress = offer.getPeerAddress();
const peerAddressText = `${peerAddress.getHost()}:${peerAddress.getPort()}`;
return (
<Box>
<Typography
size="md"
>
Peer:
{' '}
<Link href="#" onClick={onPeerClick}>
{peerAddressText}
</Link>
</Typography>
</Box>
);
}
function ExpiresInfoContent(offer) {
const invoiceTime = offer.getInvoiceTimestamp();
const invoiceExpiry = offer.getInvoiceExpiry();
const expireTime = invoiceTime + invoiceExpiry;
return (
<Box>
<Typography
size="md"
>
Expires:
{' '}
{moment(expireTime * 1000).fromNow()}
</Typography>
</Box>
);
}
function LightningPeerInfoContent(offer) {
const lightningAddress = `${offer.getNodeHost()}:${offer.getNodePort()}`;
const lightningPubkey = offer.getNodePubkey();
return (
<Box>
<Typography
size="md"
>
Lightning Node:
{' '}
<Link href="#" onClick={onLightningNodeClick}>
{`${lightningPubkey}@${lightningAddress}`}
</Link>
</Typography>
</Box>
);
}
return (
<>
<Box
p={1}
m={0}
style={{ backgroundColor: 'white' }}
>
<Grid
container
direction="row"
justify="flex-start"
alignItems="flex-start"
>
<Grid item>
{OfferContent()}
</Grid>
</Grid>
<Grid
container
direction="row"
justify="flex-start"
alignItems="flex-start"
>
<Grid item>
{PeerInfoContent()}
</Grid>
</Grid>
<Grid
container
direction="row"
justify="flex-start"
alignItems="flex-start"
>
<Grid item>
{LightningPeerInfoContent(offer)}
</Grid>
</Grid>
<Grid
container
direction="row"
justify="flex-start"
alignItems="flex-start"
>
<Grid item>
{ExpiresInfoContent(offer)}
</Grid>
</Grid>
</Box>
</>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "BuyOfferDetailItem",
"version": "0.0.0",
"private": true,
"main": "BuyOfferDetailItem.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,113 +0,0 @@
import React from 'react';
import {
Typography,
Grid,
Box,
} from '@material-ui/core';
import { useHistory } from 'react-router-dom';
// styles
import moment from 'moment';
import useStyles from './styles';
export default function BuyOfferItem({
offer,
handleOfferClick,
...props
}) {
const classes = useStyles();
const history = useHistory();
const onOfferClick = (event) => {
event.preventDefault();
console.log('Handling offer click...');
if (handleOfferClick) {
handleOfferClick();
}
};
function OfferContent() {
return (
<Typography
size="md"
>
{offer.getPriceMsat()}
{' '}
msats (
{offer.getPriceMsat() / 1000}
{' '}
sats)
</Typography>
);
}
function ProfileInfoContent(peer) {
return (
<Box>
<Typography
size="md"
>
{peer.getPeerName()}
</Typography>
</Box>
);
}
function ExpiresInfoContent(offer) {
const invoiceTime = offer.getInvoiceTimestamp();
const invoiceExpiry = offer.getInvoiceExpiry();
const expireTime = invoiceTime + invoiceExpiry;
return (
<Box>
<Typography
size="md"
>
Expires:
{' '}
{moment(expireTime * 1000).fromNow()}
</Typography>
</Box>
);
}
return (
<Box
p={1}
m={0}
style={{ backgroundColor: 'white' }}
onClick={onOfferClick}
>
<Grid
container
direction="row"
justify="flex-start"
alignItems="flex-start"
>
<Grid item>
{OfferContent()}
</Grid>
</Grid>
<Grid
container
direction="row"
justify="flex-start"
alignItems="flex-start"
>
<Grid item>
{ProfileInfoContent(offer.getPeer())}
</Grid>
</Grid>
<Grid
container
direction="row"
justify="flex-start"
alignItems="flex-start"
>
<Grid item>
{ExpiresInfoContent(offer)}
</Grid>
</Grid>
</Box>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "BuyOfferItem",
"version": "0.0.0",
"private": true,
"main": "BuyOfferItem.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,275 +0,0 @@
import React, { useState, useEffect, useCallback } from 'react';
import {
MenuItem,
Typography,
Box,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
FormControl,
InputLabel,
Select,
CircularProgress,
} from '@material-ui/core';
// styles
import useStyles from './styles';
import BuyOfferDetailItem from '../BuyOfferDetailItem';
import DownloadInProgressDialog from '../../components/DownloadInProgressDialog';
import {
downloadOffersRequest,
getBuyOffersRequest,
// subscribeBuyOffersRequest,
payOfferRequest,
} from '../../squeakclient/requests';
export default function BuySqueakDialog({
open,
handleClose,
handlePaymentComplete,
hash,
...props
}) {
const classes = useStyles();
const [selectedOfferId, setSelectedOfferId] = useState('');
const [offers, setOffers] = useState([]);
const [loading, setLoading] = useState(false);
const [waitingForDownloadOffers, setWaitingForDownloadOffers] = useState(false);
const resetFields = () => {
setSelectedOfferId('');
};
const handleChange = (event) => {
setSelectedOfferId(event.target.value);
};
const loadOffers = useCallback(() => {
getBuyOffersRequest(hash, setOffers);
},
[hash, setOffers]);
// const subscribeOffers = useCallback(() => subscribeBuyOffersRequest(hash, (offer) => {
// setOffers((prevOffers) => prevOffers.concat([offer]));
// }),
// [hash, setOffers]);
const handleDownloadOffers = () => {
console.log(`downloadOffersRequest with hash: ${hash}`);
setWaitingForDownloadOffers(true);
console.log(`downloadOffersRequest with hash: ${hash}`);
downloadOffersRequest(hash, (response) => {
setWaitingForDownloadOffers(false);
const downloadResult = response.getDownloadResult();
const numPeers = downloadResult.getNumberPeers();
const numDownloaded = downloadResult.getNumberDownloaded();
if (numPeers === 0) {
alert("Unable to download because zero connected peers.");
} else {
alert(`Downloaded ${numDownloaded} offers from ${numPeers} connected peers.`);
}
getBuyOffersRequest(hash, setOffers);
});
};
const handleCloseOffersDownloadInProgressDialog = () => {
setWaitingForDownloadOffers(false);
};
const handlePayResponse = (response) => {
handlePaymentComplete();
setLoading(false);
handleClose();
};
const handlePayErr = (err) => {
alert(`Payment failure: ${err}`);
setLoading(false);
handleClose();
};
const pay = (offerId) => {
setLoading(true);
payOfferRequest(offerId, handlePayResponse, handlePayErr);
};
const onDownloadClick = (event) => {
event.preventDefault();
console.log('Handling download click...');
console.log(`downloadOffersRequest with hash: ${hash}`);
// downloadOffers();
handleDownloadOffers();
};
const getSelectedOffer = () => {
let offer;
for (offer of offers) {
if (offer.getOfferId() === selectedOfferId) {
return offer;
}
}
return null;
};
const getPeerAddressText = (offer) => {
const peerAddress = offer.getPeerAddress();
return `${peerAddress.getHost()}:${peerAddress.getPort()}`;
};
useEffect(() => {
loadOffers();
}, [loadOffers]);
// useEffect(() => {
// const stream = subscribeOffers();
// return () => stream.cancel();
// }, [subscribeOffers, hash]);
function handleSubmit(event) {
event.preventDefault();
console.log('selectedOfferId:', selectedOfferId);
if (selectedOfferId === '') {
alert('Offer must be selected.');
return;
}
pay(selectedOfferId);
// handleClose();
}
function cancel(event) {
event.stopPropagation();
handleClose();
}
function ignore(event) {
event.stopPropagation();
}
function SelectOfferContent() {
return (
<FormControl className={classes.formControl} required style={{ minWidth: 120 }}>
<InputLabel id="offer-select-label">Offer</InputLabel>
<Select
labelId="offer-select-label"
id="offer-select"
value={selectedOfferId}
onChange={handleChange}
>
{offers.map((offer) => (
<MenuItem key={offer.getOfferId()} value={offer.getOfferId()}>
{getPeerAddressText(offer)}
{' '}
(
{offer.getPriceMsat() / 1000}
{' '}
sats)
</MenuItem>
))}
</Select>
</FormControl>
);
}
function LoadOffersButton() {
return (
<Button
variant="contained"
onClick={onDownloadClick}
>
Re-download offers
</Button>
);
}
function SelectedOfferContentInput() {
const selectedOffer = getSelectedOffer();
if (selectedOffer == null) {
return <></>;
}
return (
<Box
p={1}
>
<BuyOfferDetailItem
offer={selectedOffer}
/>
</Box>
);
}
function CancelBuyButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function BuySqueakButton() {
return (
<div className={classes.wrapper}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={loading}
>
Buy Squeak
</Button>
{loading && <CircularProgress size={24} className={classes.buttonProgress} />}
</div>
);
}
function OffersDownloadInProgressDialogContent() {
return (
<>
<DownloadInProgressDialog
open={waitingForDownloadOffers}
handleClose={handleCloseOffersDownloadInProgressDialog}
/>
</>
);
}
return (
<>
<Dialog open={open} onEnter={resetFields} onClose={cancel} onClick={ignore} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">
Buy Squeak
</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
<Box>
{LoadOffersButton()}
</Box>
<Box>
<Typography variant="body1" color="textSecondary" component="p">
{offers.length}
{' '}
offers
</Typography>
</Box>
<Box>
{SelectOfferContent()}
</Box>
{SelectedOfferContentInput()}
</DialogContent>
<DialogActions>
{CancelBuyButton()}
{BuySqueakButton()}
</DialogActions>
</form>
</Dialog>
{OffersDownloadInProgressDialogContent()}
</>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "BuySqueakDialog",
"version": "0.0.0",
"private": true,
"main": "BuySqueakDialog.js"
}

View file

@ -1,73 +0,0 @@
import { makeStyles } from '@material-ui/styles';
import { green } from '@material-ui/core/colors';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
root: {
display: 'flex',
alignItems: 'center',
},
wrapper: {
margin: theme.spacing(1),
position: 'relative',
},
buttonSuccess: {
backgroundColor: green[500],
'&:hover': {
backgroundColor: green[700],
},
},
fabProgress: {
color: green[500],
position: 'absolute',
top: -6,
left: -6,
zIndex: 1,
},
buttonProgress: {
color: green[500],
position: 'absolute',
top: '50%',
left: '50%',
marginTop: -12,
marginLeft: -12,
},
}));

View file

@ -1,48 +0,0 @@
import React from 'react';
import {
Box,
LinearProgress,
Typography,
} from '@material-ui/core';
import useStyles from '../../pages/wallet/styles';
export default function ChannelBalanceBar({
channel,
...props
}) {
const classes = useStyles();
return (
<Box className={classes.channelBalanceBarContainer}>
<Box className={classes.channelBalanceDetailsContainer}>
<Box className={classes.balanceContainerCapacity}>
<Typography className={classes.balanceLabel}>Capacity</Typography>
<Typography className={classes.balanceValue}>{channel.getCapacity()}</Typography>
</Box>
</Box>
<Box className={classes.channelBalanceBarRow}>
<Box className={classes.balanceContainerLocal}>
<Typography className={classes.balanceLabel}>Local</Typography>
<Typography className={classes.balanceValue} style={{ color: 'darkblue' }}>{channel.getLocalBalance()}</Typography>
</Box>
<LinearProgress
variant="buffer"
value={channel.getLocalBalance() / channel.getCapacity() * 100}
valueBuffer={channel.getRemoteBalance() / channel.getCapacity() * 100}
className={classes.channelBalanceProgressBar}
classes={{
buffer: classes.channelBalanceBuffer,
dashed: classes.channelBalanceDashed,
bar: classes.channelBalanceBar,
bar1Buffer: classes.channelBalanceBar1Buffer,
bar2Buffer: classes.channelBalanceBar2Buffer,
}}
/>
<Box className={classes.balanceContainerRemote}>
<Typography className={classes.balanceLabel}>Remote</Typography>
<Typography className={classes.balanceValue} style={{ color: 'darkred' }}>{channel.getRemoteBalance()}</Typography>
</Box>
</Box>
</Box>
);
}

View file

@ -1,103 +0,0 @@
import React from 'react';
import {
Typography,
Box,
Chip,
Card,
} from '@material-ui/core';
import { useHistory } from 'react-router-dom';
import moment from 'moment';
import useStyles from '../../pages/wallet/styles';
import ChannelBalanceBar from './ChannelBalanceBar';
import {
goToChannelPage,
} from '../../navigation/navigation';
export default function ChannelItem({
channel,
...props
}) {
const classes = useStyles({
channelStatus: 'open',
clickable: true,
});
const history = useHistory();
const getTxId = (channel) => {
const channelPoint = channel.getChannelPoint();
if (channelPoint == null) {
return null;
}
const pieces = channelPoint.split(':');
return pieces[0];
};
const getOutputIndex = (channel) => {
const channelPoint = channel.getChannelPoint();
if (channelPoint == null) {
return null;
}
const pieces = channelPoint.split(':');
if (pieces.length < 2) {
return null;
}
return pieces[1];
};
const onChannelClick = (event) => {
event.preventDefault();
console.log('Handling channel click...');
const txId = getTxId(channel);
const outputIndex = getOutputIndex(channel);
goToChannelPage(history, txId, outputIndex);
};
function ChannelDetailItem(label, value) {
return (
<Box className={classes.detailItem}>
<Typography className={classes.detailItemLabel}>
{label}
</Typography>
<Typography className={classes.detailItemValue}>
{value}
</Typography>
</Box>
);
}
return (
<Card
className={classes.root}
onClick={onChannelClick}
>
<Box className={classes.cardContentContainerVertical}>
<Box className={classes.cardContentFlexColumn}>
<Box className={classes.cardHeaderContainer}>
<Box className={classes.cardHeaderRow}>
<Chip
label="Open"
size="small"
className={classes.channelStatusChip}
/>
</Box>
</Box>
<Box className={classes.cardContentSection}>
{ChannelDetailItem('Pubkey', `${channel.getRemotePubkey()}`)}
{ChannelDetailItem('Channel Point', `${channel.getChannelPoint()}`)}
{/* {ChannelDetailItem("Total sats sent", `${channel.getTotalSatoshisSent()}`)} */}
{/* {ChannelDetailItem("Total sats received", `${channel.getTotalSatoshisReceived()}`)} */}
{ChannelDetailItem('Lifetime', `${moment.duration(channel.getLifetime(), 'seconds').humanize()}`)}
{ChannelDetailItem('Uptime', `${moment.duration(channel.getUptime(), 'seconds').humanize()}`)}
</Box>
</Box>
<Box className={classes.cardContentFlexColumn}>
<ChannelBalanceBar
channel={channel}
/>
</Box>
</Box>
</Card>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "ChannelItem",
"version": "0.0.0",
"private": true,
"main": "ChannelItem.js"
}

View file

@ -1,87 +0,0 @@
import React from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
} from '@material-ui/core';
// styles
import useStyles from './styles';
import {
clearSqueakProfileImageRequest,
} from '../../squeakclient/requests';
export default function ClearProfileImageDialog({
open,
handleClose,
squeakProfile,
reloadProfile,
...props
}) {
const classes = useStyles();
const handleResponse = (response) => {
reloadProfile();
};
const handleErr = (err) => {
alert(`Error clearing profile image: ${err}`);
};
const clearProfileImage = () => {
clearSqueakProfileImageRequest(
squeakProfile.getProfileId(),
handleResponse,
handleErr,
);
};
function handleSubmit(event) {
event.preventDefault();
clearProfileImage();
handleClose();
}
function CancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function ClearProfileImageButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Clear Profile Image
</Button>
);
}
return (
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Clear Profile Image</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
Are you sure you want to clear the image for this profile?
</DialogContent>
<DialogActions>
{CancelButton()}
{ClearProfileImageButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "ClearProfileImageDialog",
"version": "0.0.0",
"private": true,
"main": "ClearProfileImageDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,88 +0,0 @@
import React from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
FormControl,
FormLabel,
} from '@material-ui/core';
// styles
import useStyles from './styles';
import {
clearSellPriceRequest,
} from '../../squeakclient/requests';
export default function ClearSellPriceDialog({
open,
handleClose,
reloadSellPriceFn,
...props
}) {
const classes = useStyles();
const clearSellPrice = () => {
clearSellPriceRequest(() => {
reloadSellPriceFn();
});
};
function handleSubmit(event) {
event.preventDefault();
console.log('clear price msat');
clearSellPrice();
handleClose();
}
function CancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function SubmitButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Clear Sell Price
</Button>
);
}
function SellPriceForm() {
return (
<FormControl component="fieldset">
<FormLabel component="legend">Clear Sell Price</FormLabel>
</FormControl>
);
}
return (
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Clear Sell Price</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
{SellPriceForm()}
</DialogContent>
<DialogActions>
{CancelButton()}
{SubmitButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "ClearSellPriceDialog",
"version": "0.0.0",
"private": true,
"main": "ClearSellPriceDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,126 +0,0 @@
import React from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
TextField,
DialogActions,
Button,
} from '@material-ui/core';
// styles
import useStyles from './styles';
import {
lndCloseChannelRequest,
} from '../../squeakclient/requests';
export default function CloseChannelDialog({
open,
txId,
outputIndex,
handleClose,
...props
}) {
const classes = useStyles();
const resetFields = () => {
// TODO
};
const handleResponse = (response) => {
// TODO: handle streaming response.
// TODO: go to channel page instead of showing alert.
};
const handleErr = (err) => {
alert(`Error closing channel: ${err}`);
};
const closeChannel = (txId, outputIndex) => {
lndCloseChannelRequest(txId, outputIndex, handleResponse, handleErr);
};
function handleSubmit(event) {
event.preventDefault();
console.log('txId:', txId);
console.log('outputIndex:', outputIndex);
closeChannel(txId, outputIndex);
handleClose();
}
function TxIdInput() {
return (
<TextField
id="txid-textarea"
label="TxId"
required
autoFocus
value={txId}
fullWidth
inputProps={{
readOnly: true,
}}
/>
);
}
function OutputIndexInput() {
return (
<TextField
id="outputindex-textarea"
label="Output Index"
required
autoFocus
value={outputIndex}
fullWidth
inputProps={{
readOnly: true,
}}
/>
);
}
function CancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function CloseChannelButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Close Channel
</Button>
);
}
return (
<Dialog open={open} onEnter={resetFields} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Close Channel</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
{TxIdInput()}
</DialogContent>
<DialogContent>
{OutputIndexInput()}
</DialogContent>
<DialogActions>
{CancelButton()}
{CloseChannelButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "CloseChannelDialog",
"version": "0.0.0",
"private": true,
"main": "CloseChannelDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,102 +0,0 @@
import React from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
FormControl,
FormLabel,
FormGroup,
FormControlLabel,
Switch,
} from '@material-ui/core';
// styles
import useStyles from './styles';
import {
setPeerAutoconnectRequest,
setPeerShareForFreeRequest,
} from '../../squeakclient/requests';
export default function ConfigurePeerDialog({
open,
handleClose,
savedPeer,
reloadPeer,
...props
}) {
const classes = useStyles();
const setAutoconnect = (id, autoconnect) => {
setPeerAutoconnectRequest(id, autoconnect, () => {
reloadPeer();
});
};
const setShareForFree = (id, shareForFree) => {
setPeerShareForFreeRequest(id, shareForFree, () => {
reloadPeer();
});
};
const handleSettingsAutoconnectChange = (event) => {
console.log(`Autoconnect changed for peer id: ${savedPeer.getPeerId()}`);
console.log(`Autoconnect changed to: ${event.target.checked}`);
setAutoconnect(savedPeer.getPeerId(), event.target.checked);
};
const handleSettingsShareForFreeChange = (event) => {
console.log(`ShareForFree changed for peer id: ${savedPeer.getPeerId()}`);
console.log(`ShareForFree changed to: ${event.target.checked}`);
setShareForFree(savedPeer.getPeerId(), event.target.checked);
};
function MakeCancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function PeerSettingsForm() {
return (
<FormControl component="fieldset">
<FormLabel component="legend">Peer settings</FormLabel>
<FormGroup>
<FormControlLabel
control={<Switch checked={savedPeer.getAutoconnect()} onChange={handleSettingsAutoconnectChange} />}
label="Autoconnect"
/>
</FormGroup>
<FormGroup>
<FormControlLabel
control={<Switch checked={savedPeer.getShareForFree()} onChange={handleSettingsShareForFreeChange} />}
label="Share for free"
/>
</FormGroup>
</FormControl>
);
}
return (
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Configure Peer</DialogTitle>
<form className={classes.root} noValidate autoComplete="off">
<DialogContent>
{savedPeer
&& PeerSettingsForm()}
</DialogContent>
<DialogActions>
{MakeCancelButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "ConfigurePeerDialog",
"version": "0.0.0",
"private": true,
"main": "ConfigurePeerDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,83 +0,0 @@
import React from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
FormControl,
FormLabel,
FormGroup,
FormControlLabel,
Switch,
} from '@material-ui/core';
// styles
import useStyles from './styles';
import {
setSqueakProfileFollowingRequest,
} from '../../squeakclient/requests';
export default function ConfigureProfileDialog({
open,
handleClose,
squeakProfile,
reloadProfile,
...props
}) {
const classes = useStyles();
const setFollowing = (id, following) => {
setSqueakProfileFollowingRequest(id, following, () => {
reloadProfile();
});
};
const handleSettingsFollowingChange = (event) => {
console.log(`Following changed for profile id: ${squeakProfile.getProfileId()}`);
console.log(`Following changed to: ${event.target.checked}`);
setFollowing(squeakProfile.getProfileId(), event.target.checked);
};
function MakeCancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function ProfileSettingsForm() {
return (
<FormControl component="fieldset">
<FormLabel component="legend">Profile settings</FormLabel>
<FormGroup>
<FormControlLabel
control={<Switch checked={squeakProfile.getFollowing()} onChange={handleSettingsFollowingChange} />}
label="Following"
/>
</FormGroup>
</FormControl>
);
}
return (
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Configure Profile</DialogTitle>
<form className={classes.root} noValidate autoComplete="off">
<DialogContent>
{squeakProfile
&& ProfileSettingsForm()}
</DialogContent>
<DialogActions>
{MakeCancelButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "ConfigureProfileDialog",
"version": "0.0.0",
"private": true,
"main": "ConfigureProfileDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,249 +0,0 @@
import React, { useState } from 'react';
import {
Button,
Dialog,
DialogActions,
DialogTitle,
DialogContent,
FormControlLabel,
Switch,
TextField,
CircularProgress,
} from '@material-ui/core';
import {
getDefaultPeerPortRequest,
connectSqueakPeerRequest,
} from '../../squeakclient/requests';
// styles
import useStyles from './styles';
export default function ConnectPeerDialog({
open,
handleClose,
handlePeerConnected,
...props
}) {
const classes = useStyles();
const [defaultPeerPort, setDefaultPeerPort] = useState(null);
const [host, setHost] = useState('');
const [port, setPort] = useState('');
const [customPortChecked, setCustomPortChecked] = useState(false);
const [useTorChecked, setUseTorChecked] = useState(false);
const [loading, setLoading] = useState(false);
const getDefaultPeerPort = () => {
getDefaultPeerPortRequest(setDefaultPeerPort);
};
const resetFields = () => {
setHost('');
setPort('');
setCustomPortChecked(false);
};
function load(event) {
getDefaultPeerPort();
resetFields();
}
const handleChangeHost = (event) => {
setHost(event.target.value);
};
const handleChangeCustomPortChecked = (event) => {
// setPort(
// event.target.checked ? '' : defaultPeerPort,
// );
setPort('');
setCustomPortChecked(event.target.checked);
};
const handleChangePort = (event) => {
setPort(event.target.value);
};
const handleChangeUseTorChecked = (event) => {
setUseTorChecked(event.target.checked);
};
const getNetwork = (useTor) => {
if (useTor) {
return 'TORV3';
}
return 'IPV4';
};
const connectPeer = (network, host, port) => {
setLoading(true);
console.log('Calling connectSqueakPeerRequest with:', network, host, port);
connectSqueakPeerRequest(network, host, port, (response) => {
// goToPeerPage(history, response.getPeerId());
// handlePeerConnected();
handlePeerConnectedResponse();
},
handlePeerConnectedErr);
};
const handlePeerConnectedResponse = (response) => {
handlePeerConnected();
setLoading(false);
handleClose();
};
const handlePeerConnectedErr = (err) => {
alert(`Connect Peer failure: ${err}`);
setLoading(false);
handleClose();
};
function handleSubmit(event) {
event.preventDefault();
console.log('useTorChecked:', useTorChecked);
console.log('host:', host);
console.log('port:', port);
console.log('customPortChecked:', customPortChecked);
const network = getNetwork(useTorChecked);
if (!host) {
alert('Host cannot be empty.');
return;
}
if (!port && customPortChecked) {
alert('port cannot be empty if using custom port.');
return;
}
const portToUse = (customPortChecked ? port : defaultPeerPort);
connectPeer(network, host, portToUse);
// handleClose();
}
function ConnectHostInput() {
return (
<TextField
required
variant="outlined"
label="Host"
autoFocus
value={host}
onChange={handleChangeHost}
inputProps={{ maxLength: 128 }}
margin="normal"
/>
);
}
// Explanation for value: https://stackoverflow.com/questions/50955603/react-material-ui-label-overlaps-with-text#comment123095620_54226170
function ConnectPortInput() {
return (
<TextField
required={customPortChecked}
variant="outlined"
label="Port"
value={customPortChecked ? port : defaultPeerPort || ''}
onChange={handleChangePort}
inputProps={{ maxLength: 8 }}
disabled={!customPortChecked}
margin="normal"
/>
);
}
function CustomPortSwitch() {
return (
<FormControlLabel
className={classes.formControlLabel}
control={(
<Switch
checked={customPortChecked}
onChange={handleChangeCustomPortChecked}
name="use-custom-port"
size="small"
/>
)}
label="Use custom port"
/>
);
}
function UseTorSwitch() {
return (
<FormControlLabel
className={classes.formControlLabel}
control={(
<Switch
checked={useTorChecked}
onChange={handleChangeUseTorChecked}
name="use-tor"
size="small"
/>
)}
label="Use Tor"
/>
);
}
function CancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function ConnectPeerButton() {
return (
<div className={classes.wrapper}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={loading}
>
Connect Peer
</Button>
{loading && <CircularProgress size={24} className={classes.buttonProgress} />}
</div>
);
}
return (
<Dialog
open={open}
onEnter={load}
onClose={handleClose}
aria-labelledby="form-dialog-title"
maxWidth="sm"
>
<DialogTitle id="form-dialog-title">
Connect Peer
</DialogTitle>
<form
className={classes.form}
onSubmit={handleSubmit}
noValidate
autoComplete="off"
>
<DialogContent>
{ConnectHostInput()}
{ConnectPortInput()}
</DialogContent>
<DialogActions>
{CustomPortSwitch()}
</DialogActions>
<DialogActions>
{UseTorSwitch()}
</DialogActions>
<DialogActions>
{CancelButton()}
{ConnectPeerButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "ConnectPeerDialog",
"version": "0.0.0",
"private": true,
"main": "ConnectPeerDialog.js"
}

View file

@ -1,90 +0,0 @@
import { makeStyles } from '@material-ui/styles';
import { green } from '@material-ui/core/colors';
export default makeStyles((theme) => ({
form: {
margin: 'auto',
width: 'fit-content',
'& .MuiDialogContent-root': {
overflow: 'hidden',
},
'& .MuiTextField-root': {
margin: theme.spacing(1),
},
'& .MuiDialogActions-root': {
padding: '1rem',
},
},
formControlLabel: {
position: 'absolute',
left: '2rem',
},
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
root: {
display: 'flex',
alignItems: 'center',
},
wrapper: {
margin: theme.spacing(1),
position: 'relative',
},
buttonSuccess: {
backgroundColor: green[500],
'&:hover': {
backgroundColor: green[700],
},
},
fabProgress: {
color: green[500],
position: 'absolute',
top: -6,
left: -6,
zIndex: 1,
},
buttonProgress: {
color: green[500],
position: 'absolute',
top: '50%',
left: '50%',
marginTop: -12,
marginLeft: -12,
},
}));

View file

@ -1,147 +0,0 @@
import React, { useState } from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
TextField,
DialogActions,
Button,
} from '@material-ui/core';
import { useHistory } from 'react-router-dom';
// styles
import useStyles from './styles';
import {
createContactProfileRequest,
} from '../../squeakclient/requests';
import {
goToProfilePage,
} from '../../navigation/navigation';
export default function CreateContactProfileDialog({
open,
handleClose,
initialPubkey = '',
...props
}) {
const classes = useStyles();
const history = useHistory();
const [profileName, setProfileName] = useState('');
const [pubkey, setPubkey] = useState(initialPubkey);
const resetFields = () => {
setProfileName('');
setPubkey(initialPubkey);
};
const handleChangeProfileName = (event) => {
setProfileName(event.target.value);
};
const handleChangePubkey = (event) => {
setPubkey(event.target.value);
};
const handleResponse = (response) => {
goToProfilePage(history, response.getProfileId());
};
const handleErr = (err) => {
alert(`Error creating contact profile: ${err}`);
};
const createContactProfile = (profileName, squeakPubkey) => {
createContactProfileRequest(profileName, squeakPubkey, handleResponse, handleErr);
};
function handleSubmit(event) {
event.preventDefault();
console.log('profileName:', profileName);
console.log('pubkey:', pubkey);
if (!profileName) {
alert('Profile Name cannot be empty.');
return;
}
if (!pubkey) {
alert('Pubkey Name cannot be empty.');
return;
}
createContactProfile(profileName, pubkey);
handleClose();
}
function CreateContactProfileNameInput() {
return (
<TextField
id="standard-textarea"
label="Profile Name"
variant="outlined"
required
autoFocus
value={profileName}
onChange={handleChangeProfileName}
fullWidth
margin="normal"
inputProps={{ maxLength: 64 }}
/>
);
}
function CreateContactPubkeyInput() {
return (
<TextField
required
id="standard-textarea"
label="Pubkey"
variant="outlined"
margin="normal"
value={pubkey}
onChange={handleChangePubkey}
inputProps={{ maxLength: 66 }}
/>
);
}
function CancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function CreateContactProfilButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Create Contact Profile
</Button>
);
}
return (
<Dialog open={open} onEnter={resetFields} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Create Contact Profile</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
{CreateContactProfileNameInput()}
{CreateContactPubkeyInput()}
</DialogContent>
<DialogActions>
{CancelButton()}
{CreateContactProfilButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "CreateContactProfileDialog",
"version": "0.0.0",
"private": true,
"main": "CreateContactProfileDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,285 +0,0 @@
import React, { useState } from 'react';
import {
Button,
Dialog,
DialogActions,
DialogTitle,
DialogContent,
FormControlLabel,
Switch,
TextField,
} from '@material-ui/core';
import { useHistory } from 'react-router-dom';
// styles
import { makeStyles } from '@material-ui/core/styles';
import {
getDefaultPeerPortRequest,
createPeerRequest,
} from '../../squeakclient/requests';
import {
goToPeerPage,
} from '../../navigation/navigation';
const useStyles = makeStyles((theme) => ({
form: {
margin: 'auto',
width: 'fit-content',
'& .MuiDialogContent-root': {
overflow: 'hidden',
},
'& .MuiTextField-root': {
margin: theme.spacing(1),
},
'& .MuiDialogActions-root': {
padding: '1rem',
},
},
formControlLabel: {
position: 'absolute',
left: '2rem',
},
}));
export default function CreatePeerDialog({
open,
handleClose,
initialNetwork = '',
initialHost = '',
initialPort = '',
...props
}) {
const classes = useStyles();
const history = useHistory();
const [defaultPeerPort, setDefaultPeerPort] = useState(null);
const [peerName, setPeerName] = useState('');
const [host, setHost] = useState('');
const [port, setPort] = useState('');
const [customPortChecked, setCustomPortChecked] = useState(false);
const [useTorChecked, setUseTorChecked] = useState(false);
const getDefaultPeerPort = () => {
getDefaultPeerPortRequest(setDefaultPeerPort);
};
const resetFields = () => {
setPeerName('');
setHost('');
if (initialNetwork === 'TORV3') {
setUseTorChecked(true);
}
if (initialHost) {
setHost(initialHost);
}
setPort('');
setCustomPortChecked(false);
if (initialPort) {
setPort(initialPort);
setCustomPortChecked(true);
}
};
function load(event) {
getDefaultPeerPort();
resetFields();
}
const handleChangePeerName = (event) => {
setPeerName(event.target.value);
};
const handleChangeHost = (event) => {
setHost(event.target.value);
};
const handleChangeCustomPortChecked = (event) => {
// setPort(
// event.target.checked ? '' : portDefaultValue,
// );
setPort('');
setCustomPortChecked(event.target.checked);
};
const handleChangePort = (event) => {
setPort(event.target.value);
};
const handleChangeUseTorChecked = (event) => {
setUseTorChecked(event.target.checked);
};
const getNetwork = (useTor) => {
if (useTor) {
return 'TORV3';
}
return 'IPV4';
};
const createPeer = (network, peerName, host, port) => {
createPeerRequest(peerName, network, host, port, (response) => {
goToPeerPage(history, response.getPeerId());
});
};
function handleSubmit(event) {
event.preventDefault();
console.log('peerName:', peerName);
console.log('host:', host);
console.log('port:', port);
const network = getNetwork(useTorChecked);
if (!host) {
alert('Host cannot be empty.');
return;
}
if (!port && customPortChecked) {
alert('port cannot be empty if using custom port.');
return;
}
const portToUse = (customPortChecked ? port : defaultPeerPort);
createPeer(network, peerName, host, portToUse);
handleClose();
}
function CreatePeerNameInput() {
return (
<TextField
required
variant="outlined"
label="Peer Name"
autoFocus
value={peerName}
onChange={handleChangePeerName}
fullWidth
inputProps={{ maxLength: 64 }}
margin="normal"
/>
);
}
function CreateHostInput() {
return (
<TextField
required
variant="outlined"
label="Host"
value={host}
onChange={handleChangeHost}
inputProps={{ maxLength: 128 }}
margin="normal"
/>
);
}
// Explanation for value: https://stackoverflow.com/questions/50955603/react-material-ui-label-overlaps-with-text#comment123095620_54226170
function CreatePortInput() {
return (
<TextField
required={customPortChecked}
variant="outlined"
label="Port"
value={customPortChecked ? port : defaultPeerPort || ''}
onChange={handleChangePort}
inputProps={{ maxLength: 8 }}
disabled={!customPortChecked}
margin="normal"
/>
);
}
function CustomPortSwitch() {
return (
<FormControlLabel
className={classes.formControlLabel}
control={(
<Switch
checked={customPortChecked}
onChange={handleChangeCustomPortChecked}
name="use-custom-port"
size="small"
/>
)}
label="Use custom port"
/>
);
}
function UseTorSwitch() {
return (
<FormControlLabel
className={classes.formControlLabel}
control={(
<Switch
checked={useTorChecked}
onChange={handleChangeUseTorChecked}
name="use-tor"
size="small"
/>
)}
label="Use Tor"
/>
);
}
function CancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function CreatePeerButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Create Peer
</Button>
);
}
return (
<Dialog
open={open}
onEnter={load}
onClose={handleClose}
aria-labelledby="form-dialog-title"
maxWidth="sm"
>
<DialogTitle id="form-dialog-title">
Create Peer
</DialogTitle>
<form
className={classes.form}
onSubmit={handleSubmit}
noValidate
autoComplete="off"
>
<DialogContent>
{CreatePeerNameInput()}
{CreateHostInput()}
{CreatePortInput()}
</DialogContent>
<DialogActions>
{CustomPortSwitch()}
</DialogActions>
<DialogActions>
{UseTorSwitch()}
</DialogActions>
<DialogActions>
{CancelButton()}
{CreatePeerButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "CreatePeerDialog",
"version": "0.0.0",
"private": true,
"main": "CreatePeerDialog.js"
}

View file

@ -1,119 +0,0 @@
import React, { useState } from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
TextField,
DialogActions,
Button,
} from '@material-ui/core';
import { useHistory } from 'react-router-dom';
// styles
import useStyles from './styles';
import {
createSigningProfileRequest,
} from '../../squeakclient/requests';
import {
goToProfilePage,
} from '../../navigation/navigation';
export default function CreateSigningProfileDialog({
open,
handleClose,
...props
}) {
const classes = useStyles();
const history = useHistory();
const [profileName, setProfileName] = useState('');
const resetFields = () => {
setProfileName('');
};
const handleChangeProfileName = (event) => {
setProfileName(event.target.value);
};
const handleResponse = (response) => {
goToProfilePage(history, response.getProfileId());
};
const handleErr = (err) => {
alert(`Error creating signing profile: ${err}`);
};
const createSigningProfile = (profileName) => {
createSigningProfileRequest(profileName, handleResponse, handleErr);
};
function handleSubmit(event) {
event.preventDefault();
console.log('profileName:', profileName);
if (!profileName) {
alert('Profile Name cannot be empty.');
return;
}
createSigningProfile(profileName);
handleClose();
}
function CreateSigningProfileNameInput() {
return (
<TextField
id="standard-textarea"
label="Profile Name"
variant="outlined"
margin="normal"
required
autoFocus
value={profileName}
onChange={handleChangeProfileName}
fullWidth
inputProps={{ maxLength: 64 }}
/>
);
}
function CancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function CreateSigningProfilButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Create Signing Profile
</Button>
);
}
return (
<Dialog open={open} onEnter={resetFields} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Create Signing Profile</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
{CreateSigningProfileNameInput()}
</DialogContent>
<DialogActions>
{CancelButton()}
{CreateSigningProfilButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "CreateSigningProfileDialog",
"version": "0.0.0",
"private": true,
"main": "CreateSigningProfileDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,160 +0,0 @@
import React, { useState, useEffect, useCallback } from 'react';
import {
MenuItem,
Box,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
FormControl,
InputLabel,
Select,
CircularProgress,
} from '@material-ui/core';
// styles
import useStyles from './styles';
import {
getSigningProfilesRequest,
// decryptSqueakRequest,
decryptRequest,
} from '../../squeakclient/requests';
export default function DecryptSqueakDialog({
open,
handleClose,
handleDecryptComplete,
hash,
squeak,
...props
}) {
const classes = useStyles();
const [profileId, setProfileId] = useState(-1);
const [signingProfiles, setSigningProfiles] = useState([]);
const [loading, setLoading] = useState(false);
const resetFields = () => {
setProfileId('');
};
const handleChangeProfileId = (event) => {
setProfileId(event.target.value);
};
const loadSigningProfiles = useCallback(() => {
getSigningProfilesRequest(setSigningProfiles);
},
[setSigningProfiles]);
const handleDecryptResponse = (response) => {
handleDecryptComplete();
setLoading(false);
handleClose();
};
const handleDecryptErr = (err) => {
alert(`Decryption failure: ${err}`);
setLoading(false);
handleClose();
};
const decrypt = (profileId) => {
const hasRecipient = profileId !== -1;
setLoading(true);
decryptRequest(hash, null, -1, hasRecipient, profileId, handleDecryptResponse, handleDecryptErr);
};
useEffect(() => {
loadSigningProfiles();
}, [loadSigningProfiles]);
function handleSubmit(event) {
event.preventDefault();
console.log('profileId:', profileId);
if (profileId === '') {
alert('Profile must be selected.');
return;
}
decrypt(profileId);
// handleClose();
}
function cancel(event) {
event.stopPropagation();
handleClose();
}
function ignore(event) {
event.stopPropagation();
}
function SelectProfileContent() {
return (
<FormControl className={classes.formControl} required style={{ minWidth: 120 }}>
<InputLabel id="demo-simple-select-label">Signing Profile</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
variant="outlined"
margin="normal"
value={profileId}
onChange={handleChangeProfileId}
>
{signingProfiles.map((p) => <MenuItem key={p.getProfileId()} value={p.getProfileId()}>{p.getProfileName()}</MenuItem>)}
</Select>
</FormControl>
);
}
function CancelDecryptButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function DecryptSqueakButton() {
return (
<div className={classes.wrapper}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={loading}
>
Decrypt Squeak
</Button>
{loading && <CircularProgress size={24} className={classes.buttonProgress} />}
</div>
);
}
return (
<>
<Dialog open={open} onEnter={resetFields} onClose={cancel} onClick={ignore} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">
Decrypt Squeak
</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
<Box>
{SelectProfileContent()}
</Box>
</DialogContent>
<DialogActions>
{CancelDecryptButton()}
{DecryptSqueakButton()}
</DialogActions>
</form>
</Dialog>
</>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "DecryptSqueakDialog",
"version": "0.0.0",
"private": true,
"main": "DecryptSqueakDialog.js"
}

View file

@ -1,73 +0,0 @@
import { makeStyles } from '@material-ui/styles';
import { green } from '@material-ui/core/colors';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
root: {
display: 'flex',
alignItems: 'center',
},
wrapper: {
margin: theme.spacing(1),
position: 'relative',
},
buttonSuccess: {
backgroundColor: green[500],
'&:hover': {
backgroundColor: green[700],
},
},
fabProgress: {
color: green[500],
position: 'absolute',
top: -6,
left: -6,
zIndex: 1,
},
buttonProgress: {
color: green[500],
position: 'absolute',
top: '50%',
left: '50%',
marginTop: -12,
marginLeft: -12,
},
}));

View file

@ -1,81 +0,0 @@
import React from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
} from '@material-ui/core';
// styles
import useStyles from './styles';
import {
deletePeerRequest,
} from '../../squeakclient/requests';
export default function DeletePeerDialog({
open,
handleClose,
peer,
reloadPeer,
...props
}) {
const classes = useStyles();
const deletePeer = (peerId) => {
deletePeerRequest(peerId, (response) => {
reloadPeer();
});
};
function handleSubmit(event) {
event.preventDefault();
console.log('peer:', peer);
const peerId = peer.getPeerId();
console.log('peerId:', peerId);
deletePeer(peerId);
handleClose();
}
function MakeCancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function DeletePeerButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Delete peer
</Button>
);
}
return (
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Delete peer</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
Are you sure you want to delete this peer?
</DialogContent>
<DialogActions>
{MakeCancelButton()}
{DeletePeerButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "DeletePeerDialog",
"version": "0.0.0",
"private": true,
"main": "DeletePeerDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,80 +0,0 @@
import React from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
} from '@material-ui/core';
// styles
import useStyles from './styles';
import {
deleteProfileRequest,
} from '../../squeakclient/requests';
export default function DeleteProfileDialog({
open,
handleClose,
profile,
reloadProfile,
...props
}) {
const classes = useStyles();
const deleteProfile = (profileId) => {
deleteProfileRequest(profileId, (response) => {
reloadProfile();
});
};
function handleSubmit(event) {
event.preventDefault();
console.log('profile:', profile);
const profileId = profile.getProfileId();
console.log('profileId:', profileId);
deleteProfile(profileId);
handleClose();
}
function MakeCancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function DeleteProfileButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Delete Profile
</Button>
);
}
return (
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Delete Profile</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
Are you sure you want to delete this profile?
</DialogContent>
<DialogActions>
{MakeCancelButton()}
{DeleteProfileButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "DeleteProfileDialog",
"version": "0.0.0",
"private": true,
"main": "DeleteProfileDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,94 +0,0 @@
import React from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
} from '@material-ui/core';
import { useHistory } from 'react-router-dom';
// styles
import useStyles from './styles';
import {
deleteSqueakRequest,
} from '../../squeakclient/requests';
import {
reloadRoute,
} from '../../navigation/navigation';
export default function DeleteSqueakDialog({
open,
handleClose,
squeakToDelete,
...props
}) {
const classes = useStyles();
const history = useHistory();
const deleteSqueak = (squeakHash) => {
deleteSqueakRequest(squeakHash, (response) => {
reloadRoute(history);
});
};
function handleSubmit(event) {
event.preventDefault();
event.stopPropagation();
console.log('squeakToDelete:', squeakToDelete);
const squeakHash = squeakToDelete.getSqueakHash();
console.log('squeakHash:', squeakHash);
deleteSqueak(squeakHash);
handleClose();
}
function cancel(event) {
event.stopPropagation();
handleClose();
}
function ignore(event) {
event.stopPropagation();
}
function MakeCancelButton() {
return (
<Button
onClick={cancel}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function DeleteSqueakButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Delete Squeak
</Button>
);
}
return (
<Dialog open={open} onClose={cancel} onBackdropClick={cancel} onClick={ignore} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Delete Squeak</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
Are you sure you want to delete this squeak?
</DialogContent>
<DialogActions>
{MakeCancelButton()}
{DeleteSqueakButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "DeleteSqueakDialog",
"version": "0.0.0",
"private": true,
"main": "DeleteSqueakDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,80 +0,0 @@
import React from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
} from '@material-ui/core';
// styles
import useStyles from './styles';
import {
deleteTwitterAccountRequest,
} from '../../squeakclient/requests';
export default function DeleteTwitterAccountDialog({
open,
handleClose,
twitterAccount,
reloadAccountsFn,
...props
}) {
const classes = useStyles();
const deleteTwitterAccount = (twitterAccountId) => {
deleteTwitterAccountRequest(twitterAccountId, (response) => {
reloadAccountsFn();
});
};
function handleSubmit(event) {
event.preventDefault();
console.log('twitter account:', twitterAccount);
const twitterAccountId = twitterAccount.getTwitterAccountId();
console.log('twitterAccountId:', twitterAccountId);
deleteTwitterAccount(twitterAccountId);
handleClose();
}
function MakeCancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function DeleteTwitterAccountButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Delete Twitter Account
</Button>
);
}
return (
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Delete Twitter Account Mapping</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
Are you sure you want to delete this twitter account mapping?
</DialogContent>
<DialogActions>
{MakeCancelButton()}
{DeleteTwitterAccountButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "DeleteTwitterAccountDialog",
"version": "0.0.0",
"private": true,
"main": "DeleteTwitterAccountDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,32 +0,0 @@
import React from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
CircularProgress,
} from '@material-ui/core';
// styles
import useStyles from './styles';
export default function DownloadInProgressDialog({
open,
handleClose,
...props
}) {
const classes = useStyles();
return (
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Download in progress</DialogTitle>
<form className={classes.root} noValidate autoComplete="off">
<DialogContent>
Downloading...
</DialogContent>
<DialogContent>
<CircularProgress size={48} className={classes.buttonProgress} />
</DialogContent>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "DownloadInProgressDialog",
"version": "0.0.0",
"private": true,
"main": "DownloadInProgressDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,101 +0,0 @@
import React, { useState } from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
TextField,
DialogActions,
Button,
} from '@material-ui/core';
// styles
import useStyles from './styles';
import {
getSqueakProfilePrivateKey,
} from '../../squeakclient/requests';
export default function ExportPrivateKeyDialog({
open,
handleClose,
profile,
...props
}) {
const classes = useStyles();
const [privateKey, setPrivateKey] = useState('');
const resetFields = () => {
setPrivateKey('');
};
const getPrivateKey = () => {
const profileId = profile.getProfileId();
getSqueakProfilePrivateKey(profileId, (response) => {
setPrivateKey(response.getPrivateKey());
});
};
function handleSubmit(event) {
event.preventDefault();
getPrivateKey();
}
function DisplayPrivateKey() {
return (
<TextField
id="standard-textarea"
label="private-key"
variant="outlined"
margin="normal"
required
autoFocus
value={privateKey}
fullWidth
inputProps={{
readOnly: true,
}}
/>
);
}
function CancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function ShowPrivateKeyButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Show private key
</Button>
);
}
return (
<Dialog open={open} onEnter={resetFields} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Export Private Key</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
{DisplayPrivateKey()}
</DialogContent>
<DialogActions>
{CancelButton()}
{ShowPrivateKeyButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "ExportPrivateKeyDialog",
"version": "0.0.0",
"private": true,
"main": "ExportPrivateKeyDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -0,0 +1,51 @@
import React , { useEffect, useContext } from 'react'
import './style.scss'
import { withRouter, Link } from 'react-router-dom'
import { StoreContext } from '../../store/store'
import Loader from '../Loader'
const Feed = (props) => {
const { state, actions } = useContext(StoreContext)
const {paymentSummary, trends, session} = state
// const userParam = props.match.params.username
useEffect(() => {
actions.getPaymentSummary();
actions.getTrend()
}, [])
const goToUser = (id) => {
props.history.push(`/app/profile/${id}`)
}
const followUser = (e, id) => {
e.stopPropagation()
actions.followUser(id)
}
return(
<div className="feed-wrapper">
{paymentSummary ?
<div className="feed-trending-card">
<h3 className="feed-card-header">Payments</h3>
<div onClick={()=>props.history.push('/app/payments')}className="feed-card-trend">
<div>Amount Spent</div>
<div>{paymentSummary.getAmountSpentMsat() / 1000} sats</div>
<div>{paymentSummary.getNumSentPayments()} squeaks</div>
</div>
<div onClick={()=>props.history.push('/app/payments')}className="feed-card-trend">
<div>Amount Earned</div>
<div>{paymentSummary.getAmountEarnedMsat() / 1000} sats</div>
<div>{paymentSummary.getNumReceivedPayments()} squeaks</div>
</div>
</div> :
<Loader/>
}
</div>
)
}
export default withRouter(Feed)

View file

@ -0,0 +1,133 @@
.feed-wrapper{
display: flex;
justify-content: center;
flex-direction: column;
position: sticky;
top: 5px;
}
.feed-trending-card{
width: 100%;
background-color: rgb(245, 248, 250);
margin-top: 10px;
margin-bottom: 15px;
border: 1px solid rgb(245, 248, 250);
border-top-left-radius: 14px;
border-top-right-radius: 14px;
border-bottom-left-radius: 14px;
border-bottom-right-radius: 14px;
}
.feed-card-header{
border-bottom: 1px solid rgb(230, 236, 240);
padding: 10px 15px;
display: flex;
align-items: center;
font-size: 19px;
font-weight: bold;
}
.feed-card-trend{
border-bottom: 1px solid rgb(230, 236, 240);
padding: 10px 15px;
cursor: pointer;
transition: 0.2s ease-in-out;
&:hover{
background-color: rgba(0, 0, 0, 0.03);
}
div:nth-child(1){
font-size: 13px;
color: rgb(101, 119, 134);
}
div:nth-child(2){
font-size: 15px;
color: rgb(20, 23, 26);
font-weight: bold;
padding-top: 2px;
}
div:nth-child(3){
font-size: 15px;
color: rgb(101, 119, 134);
padding-top: 2px;
}
}
.feed-more{
padding: 15px;
transition: 0.2s ease-in-out;
cursor: pointer;
font-size: 15px;
color: rgb(29,161,242);
}
////@extend
.sugg-result-wapper{
cursor: pointer;
display: flex;
}
.search-userPic-wrapper{
flex-basis: 49px;
margin-right: 10px;
}
.search-user-details{
display: flex;
flex-direction: column;
width: 100%;
}
.search-user-info{
display: flex;
flex-direction: column;
}
.search-user-name{
font-weight: bold;
color: rgb(20, 23, 26) !important;
font-weight: 600 !important;
}
.search-user-username{
color: #657786 !important;
font-weight: 400 !important;
line-height: 1;
}
.search-user-bio{
margin-top: 7px;
}
.search-user-warp{
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.follow-btn-wrap{
min-height: 30px;
min-width: 70px;
transition: 0.2s ease-in-out;
cursor: pointer;
border: 1px solid #1da1f2;
border-radius: 9999px;
display: flex;
justify-content: center;
align-items: center;
padding-left: 1em;
padding-right: 1em;
&:hover{
background-color: rgba(29, 161, 242, 0.1);
}
}
.follow-btn-wrap span{
text-align: center;
font-weight: 800;
color: #1da1f2;
width: 100%;
}

View file

@ -1,249 +0,0 @@
import React, { useState, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import {
AppBar,
Toolbar,
IconButton,
InputBase,
Menu,
MenuItem,
} from '@material-ui/core';
import {
Menu as MenuIcon,
NotificationsNone as NotificationsIcon,
Person as AccountIcon,
Search as SearchIcon,
ArrowBack as ArrowBackIcon,
} from '@material-ui/icons';
import classNames from 'classnames';
// styles
import useStyles from './styles';
// components
import { Badge, Typography } from '../Wrappers/Wrappers';
import Notification from '../Notification/Notification';
// context
import {
useLayoutState,
useLayoutDispatch,
toggleSidebar,
} from '../../context/LayoutContext';
// import { useUserDispatch } from '../../context/UserContext';
import {
logoutRequest,
getUserRequest,
getNetworkRequest,
} from '../../squeakclient/requests';
import {
reloadRoute,
goToSearchPage,
goToSettingsPage,
} from '../../navigation/navigation';
const notifications = [];
export default function Header(props) {
const classes = useStyles();
const history = useHistory();
// global
const layoutState = useLayoutState();
const layoutDispatch = useLayoutDispatch();
// const userDispatch = useUserDispatch();
// local
const [notificationsMenu, setNotificationsMenu] = useState(null);
const [isNotificationsUnread, setIsNotificationsUnread] = useState(true);
const [profileMenu, setProfileMenu] = useState(null);
const [username, setUsername] = useState('bob smith');
const [network, setNetwork] = useState('');
const [value, setValue] = useState();
const isSearchOpen = true;
const getUser = () => {
getUserRequest(setUsername);
};
const getNetwork = () => {
getNetworkRequest(setNetwork);
};
const handleChangeSearchText = (event) => {
setValue(event.target.value);
};
useEffect(() => {
getUser();
}, []);
useEffect(() => {
getNetwork();
}, []);
return (
<AppBar position="fixed" className={classes.appBar}>
<Toolbar className={classes.toolbar}>
<IconButton
color="inherit"
onClick={() => toggleSidebar(layoutDispatch)}
className={classNames(
classes.headerMenuButton,
classes.headerMenuButtonCollapse,
)}
>
{layoutState.isSidebarOpened ? (
<ArrowBackIcon
classes={{
root: classNames(
classes.headerIcon,
classes.headerIconCollapse,
),
}}
/>
) : (
<MenuIcon
classes={{
root: classNames(
classes.headerIcon,
classes.headerIconCollapse,
),
}}
/>
)}
</IconButton>
<Typography variant="h6" weight="medium" className={classes.logotype}>
Squeaknode
</Typography>
<div className={classes.grow} />
<div
className={classNames(classes.search, {
[classes.searchFocused]: isSearchOpen,
})}
>
<div
className={classNames(classes.searchIcon, {
[classes.searchIconOpened]: isSearchOpen,
})}
>
<SearchIcon classes={{ root: classes.headerIcon }} />
</div>
<InputBase
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
value={value}
placeholder="Search…"
inputProps={{ 'aria-label': 'search google maps' }}
onChange={handleChangeSearchText}
onKeyPress={(ev) => {
if (ev.key === 'Enter') {
ev.preventDefault();
const encodedText = encodeURIComponent(ev.target.value);
setValue('');
goToSearchPage(history, encodedText);
}
}}
/>
</div>
<IconButton
color="inherit"
aria-haspopup="true"
aria-controls="mail-menu"
onClick={(e) => {
setNotificationsMenu(e.currentTarget);
setIsNotificationsUnread(false);
}}
className={classes.headerMenuButton}
>
<Badge
badgeContent={isNotificationsUnread ? notifications.length : null}
color="warning"
>
<NotificationsIcon classes={{ root: classes.headerIcon }} />
</Badge>
</IconButton>
<IconButton
aria-haspopup="true"
color="inherit"
className={classes.headerMenuButton}
aria-controls="profile-menu"
onClick={(e) => setProfileMenu(e.currentTarget)}
>
<AccountIcon classes={{ root: classes.headerIcon }} />
</IconButton>
<Menu
id="notifications-menu"
open={Boolean(notificationsMenu)}
anchorEl={notificationsMenu}
onClose={() => setNotificationsMenu(null)}
className={classes.headerMenu}
disableAutoFocusItem
>
{notifications.map((notification) => (
<MenuItem
key={notification.id}
onClick={() => setNotificationsMenu(null)}
className={classes.headerMenuItem}
>
<Notification {...notification} typographyVariant="inherit" />
</MenuItem>
))}
</Menu>
<Menu
id="profile-menu"
open={Boolean(profileMenu)}
anchorEl={profileMenu}
onClose={() => setProfileMenu(null)}
className={classes.headerMenu}
classes={{ paper: classes.profileMenu }}
disableAutoFocusItem
>
<div className={classes.profileMenuUser}>
<Typography variant="h5" weight="medium">
{username}
</Typography>
</div>
<div className={classes.profileMenuUser}>
<Typography variant="h6" weight="medium">
{network}
</Typography>
</div>
<div className={classes.profileMenuUser}>
<Typography
className={classes.settingsMenuLink}
color="primary"
onClick={() => {
goToSettingsPage(history)
setProfileMenu(null);
}
}
>
Settings
</Typography>
</div>
<div className={classes.profileMenuUser}>
<Typography
className={classes.profileMenuLink}
color="primary"
onClick={() => logoutRequest(
() => {
reloadRoute(history);
},
)}
>
Sign Out
</Typography>
</div>
</Menu>
</Toolbar>
</AppBar>
);
}

View file

@ -1,466 +0,0 @@
import React from 'react';
import {
AppBar,
Toolbar,
IconButton,
InputBase,
Menu,
MenuItem,
Fab,
withStyles,
} from '@material-ui/core';
import {
Menu as MenuIcon,
MailOutline as MailIcon,
NotificationsNone as NotificationsIcon,
Person as AccountIcon,
Search as SearchIcon,
Send as SendIcon,
ArrowBack as ArrowBackIcon,
} from '@material-ui/icons';
import { fade } from '@material-ui/core/styles/colorManipulator';
import classNames from 'classnames';
import { Badge, Typography } from '../Wrappers';
import Notification from '../Notification';
import UserAvatar from '../UserAvatar';
const messages = [
{
id: 0,
variant: 'warning',
name: 'Jane Hew',
message: 'Hey! How is it going?',
time: '9:32',
},
{
id: 1,
variant: 'success',
name: 'Lloyd Brown',
message: 'Check out my new Dashboard',
time: '9:18',
},
{
id: 2,
variant: 'primary',
name: 'Mark Winstein',
message: 'I want rearrange the appointment',
time: '9:15',
},
{
id: 3,
variant: 'secondary',
name: 'Liana Dutti',
message: 'Good news from sale department',
time: '9:09',
},
];
const notifications = [
{ id: 0, color: 'warning', message: 'Check out this awesome ticket' },
{
id: 1,
color: 'success',
type: 'info',
message: 'What is the best way to get ...',
},
{
id: 2,
color: 'secondary',
type: 'notification',
message: 'This is just a simple notification',
},
{
id: 3,
color: 'primary',
type: 'e-commerce',
message: '12 new orders has arrived today',
},
];
const Header = ({
classes, isSidebarOpened, toggleSidebar, ...props
}) => (
<AppBar position="fixed" className={classes.appBar}>
<Toolbar className={classes.toolbar}>
<IconButton
color="inherit"
onClick={toggleSidebar}
className={classNames(
classes.headerMenuButton,
classes.headerMenuButtonCollapse,
)}
>
{isSidebarOpened ? (
<ArrowBackIcon
classes={{
root: classNames(classes.headerIcon, classes.headerIconCollapse),
}}
/>
) : (
<MenuIcon
classes={{
root: classNames(classes.headerIcon, classes.headerIconCollapse),
}}
/>
)}
</IconButton>
<Typography variant="h6" weight="medium" className={classes.logotype}>React Material Admin</Typography>
<div className={classes.grow} />
<div
className={classNames(classes.search, {
[classes.searchFocused]: props.isSearchOpen,
})}
>
<div
className={classNames(classes.searchIcon, {
[classes.searchIconOpened]: props.isSearchOpen,
})}
onClick={props.toggleSearch}
>
<SearchIcon classes={{ root: classes.headerIcon }} />
</div>
<InputBase
placeholder="Search…"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
/>
</div>
<IconButton
color="inherit"
aria-haspopup="true"
aria-controls="mail-menu"
onClick={props.openNotificationsMenu}
className={classes.headerMenuButton}
>
<Badge
badgeContent={
props.isNotificationsUnread ? notifications.length : null
}
colortheme="warning"
>
<NotificationsIcon classes={{ root: classes.headerIcon }} />
</Badge>
</IconButton>
<IconButton
color="inherit"
aria-haspopup="true"
aria-controls="mail-menu"
onClick={props.openMailMenu}
className={classes.headerMenuButton}
>
<Badge
badgeContent={props.isMailsUnread ? messages.length : null}
color="secondary"
>
<MailIcon classes={{ root: classes.headerIcon }} />
</Badge>
</IconButton>
<IconButton
aria-haspopup="true"
color="inherit"
className={classes.headerMenuButton}
aria-controls="profile-menu"
onClick={props.openProfileMenu}
>
<AccountIcon classes={{ root: classes.headerIcon }} />
</IconButton>
<Menu
id="mail-menu"
open={Boolean(props.mailMenu)}
anchorEl={props.mailMenu}
onClose={props.closeMailMenu}
MenuListProps={{ className: classes.headerMenuList }}
className={classes.headerMenu}
classes={{ paper: classes.profileMenu }}
disableAutoFocusItem
>
<div className={classes.profileMenuUser}>
<Typography variant="h4" weight="medium">
New Messages
</Typography>
<Typography
className={classes.profileMenuLink}
component="a"
color="secondary"
>
{messages.length}
{' '}
New Messages
</Typography>
</div>
{messages.map((message) => (
<MenuItem key={message.id} className={classes.messageNotification}>
<div className={classes.messageNotificationSide}>
<UserAvatar color={message.variant} name={message.name} />
<Typography size="sm" color="textSecondary">
{message.time}
</Typography>
</div>
<div
className={classNames(
classes.messageNotificationSide,
classes.messageNotificationBodySide,
)}
>
<Typography weight="medium" gutterBottom>
{message.name}
</Typography>
<Typography color="textSecondary">{message.message}</Typography>
</div>
</MenuItem>
))}
<Fab
variant="extended"
color="primary"
aria-label="Add"
className={classes.sendMessageButton}
>
Send New Message
<SendIcon className={classes.sendButtonIcon} />
</Fab>
</Menu>
<Menu
id="notifications-menu"
open={Boolean(props.notificationsMenu)}
anchorEl={props.notificationsMenu}
onClose={props.closeNotificationsMenu}
className={classes.headerMenu}
disableAutoFocusItem
>
{notifications.map((notification) => (
<MenuItem
key={notification.id}
onClick={props.closeNotificationsMenu}
className={classes.headerMenuItem}
>
<Notification {...notification} typographyVariant="inherit" />
</MenuItem>
))}
</Menu>
<Menu
id="profile-menu"
open={Boolean(props.profileMenu)}
anchorEl={props.profileMenu}
onClose={props.closeProfileMenu}
className={classes.headerMenu}
classes={{ paper: classes.profileMenu }}
disableAutoFocusItem
>
<div className={classes.profileMenuUser}>
<Typography variant="h4" weight="medium">
John Smith
</Typography>
<Typography
className={classes.profileMenuLink}
component="a"
color="primary"
href="https://flatlogic.com"
>
Flalogic.com
</Typography>
</div>
<MenuItem
className={classNames(
classes.profileMenuItem,
classes.headerMenuItem,
)}
>
<AccountIcon className={classes.profileMenuIcon} />
{' '}
Profile
</MenuItem>
<MenuItem
className={classNames(
classes.profileMenuItem,
classes.headerMenuItem,
)}
>
<AccountIcon className={classes.profileMenuIcon} />
{' '}
Tasks
</MenuItem>
<MenuItem
className={classNames(
classes.profileMenuItem,
classes.headerMenuItem,
)}
>
<AccountIcon className={classes.profileMenuIcon} />
{' '}
Messages
</MenuItem>
<div className={classes.profileMenuUser}>
<Typography
className={classes.profileMenuLink}
color="primary"
onClick={props.signOut}
>
Sign Out
</Typography>
</div>
</Menu>
</Toolbar>
</AppBar>
);
const styles = (theme) => ({
logotype: {
color: 'white',
marginLeft: theme.spacing.unit * 2.5,
marginRight: theme.spacing.unit * 2.5,
fontWeight: 500,
fontSize: 18,
whiteSpace: 'nowrap',
[theme.breakpoints.down('xs')]: {
display: 'none',
},
},
appBar: {
width: '100vw',
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(['margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
toolbar: {
paddingLeft: theme.spacing.unit * 2,
paddingRight: theme.spacing.unit * 2,
},
hide: {
display: 'none',
},
grow: {
flexGrow: 1,
},
search: {
position: 'relative',
borderRadius: 25,
paddingLeft: theme.spacing.unit * 2.5,
width: 36,
backgroundColor: fade(theme.palette.common.black, 0),
transition: theme.transitions.create(['background-color', 'width']),
'&:hover': {
cursor: 'pointer',
backgroundColor: fade(theme.palette.common.black, 0.08),
},
},
searchFocused: {
backgroundColor: fade(theme.palette.common.black, 0.08),
width: '100%',
[theme.breakpoints.up('md')]: {
width: 250,
},
},
searchIcon: {
width: 36,
right: 0,
height: '100%',
position: 'absolute',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: theme.transitions.create('right'),
'&:hover': {
cursor: 'pointer',
},
},
searchIconOpened: {
right: theme.spacing.unit * 1.25,
},
inputRoot: {
color: 'inherit',
width: '100%',
},
inputInput: {
height: 36,
padding: 0,
paddingRight: 36 + theme.spacing.unit * 1.25,
width: '100%',
},
messageContent: {
display: 'flex',
flexDirection: 'column',
},
headerMenu: {
marginTop: theme.spacing.unit * 7,
},
headerMenuList: {
display: 'flex',
flexDirection: 'column',
},
headerMenuItem: {
'&:hover, &:focus': {
backgroundColor: theme.palette.primary.main,
color: 'white',
},
},
headerMenuButton: {
marginLeft: theme.spacing.unit * 2,
padding: theme.spacing.unit / 2,
},
headerMenuButtonCollapse: {
marginRight: theme.spacing.unit * 2,
},
headerIcon: {
fontSize: 28,
color: 'rgba(255, 255, 255, 0.35)',
},
headerIconCollapse: {
color: 'white',
},
profileMenu: {
minWidth: 265,
},
profileMenuUser: {
display: 'flex',
flexDirection: 'column',
padding: theme.spacing.unit * 2,
},
profileMenuItem: {
color: theme.palette.text.hint,
},
profileMenuIcon: {
marginRight: theme.spacing.unit * 2,
color: theme.palette.text.hint,
},
profileMenuLink: {
fontSize: 16,
textDecoration: 'none',
'&:hover': {
cursor: 'pointer',
},
},
messageNotification: {
height: 'auto',
display: 'flex',
alignItems: 'center',
'&:hover, &:focus': {
backgroundColor: theme.palette.background.light,
},
},
messageNotificationSide: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
marginRight: theme.spacing.unit * 2,
},
messageNotificationBodySide: {
alignItems: 'flex-start',
marginRight: 0,
},
sendMessageButton: {
margin: theme.spacing.unit * 4,
marginTop: theme.spacing.unit * 2,
marginBottom: theme.spacing.unit * 2,
textTransform: 'none',
},
sendButtonIcon: {
marginLeft: theme.spacing.unit * 2,
},
});
export default withStyles(styles)(Header);

View file

@ -1,6 +0,0 @@
{
"name": "Header",
"version": "0.0.0",
"private": true,
"main": "Header.js"
}

View file

@ -1,166 +0,0 @@
import { makeStyles } from '@material-ui/styles';
import { fade } from '@material-ui/core/styles/colorManipulator';
export default makeStyles((theme) => ({
logotype: {
color: 'white',
marginLeft: theme.spacing(2.5),
marginRight: theme.spacing(2.5),
fontWeight: 500,
fontSize: 18,
whiteSpace: 'nowrap',
[theme.breakpoints.down('xs')]: {
display: 'none',
},
},
appBar: {
width: '100vw',
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(['margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
toolbar: {
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2),
},
hide: {
display: 'none',
},
grow: {
flexGrow: 1,
},
search: {
position: 'relative',
borderRadius: 25,
paddingLeft: theme.spacing(2.5),
width: 36,
backgroundColor: fade(theme.palette.common.black, 0),
transition: theme.transitions.create(['background-color', 'width']),
'&:hover': {
cursor: 'pointer',
backgroundColor: fade(theme.palette.common.black, 0.08),
},
},
searchFocused: {
backgroundColor: fade(theme.palette.common.black, 0.08),
width: '100%',
[theme.breakpoints.up('md')]: {
width: 250,
},
},
searchIcon: {
width: 36,
right: 0,
height: '100%',
position: 'absolute',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: theme.transitions.create('right'),
'&:hover': {
cursor: 'pointer',
},
},
searchIconOpened: {
right: theme.spacing(1.25),
},
inputRoot: {
color: 'inherit',
width: '100%',
},
inputInput: {
height: 36,
padding: 0,
paddingRight: 36 + theme.spacing(1.25),
width: '100%',
},
messageContent: {
display: 'flex',
flexDirection: 'column',
},
headerMenu: {
marginTop: theme.spacing(7),
},
headerMenuList: {
display: 'flex',
flexDirection: 'column',
},
headerMenuItem: {
'&:hover, &:focus': {
backgroundColor: theme.palette.primary.main,
color: 'white',
},
},
headerMenuButton: {
marginLeft: theme.spacing(2),
padding: theme.spacing(0.5),
},
headerMenuButtonCollapse: {
marginRight: theme.spacing(2),
},
headerIcon: {
fontSize: 28,
color: 'rgba(255, 255, 255, 0.35)',
},
headerIconCollapse: {
color: 'white',
},
profileMenu: {
minWidth: 265,
},
profileMenuUser: {
display: 'flex',
flexDirection: 'column',
padding: theme.spacing(2),
},
profileMenuItem: {
color: theme.palette.text.hint,
},
profileMenuIcon: {
marginRight: theme.spacing(2),
color: theme.palette.text.hint,
},
profileMenuLink: {
fontSize: 16,
textDecoration: 'none',
'&:hover': {
cursor: 'pointer',
},
},
settingsMenuLink: {
fontSize: 16,
textDecoration: 'none',
'&:hover': {
cursor: 'pointer',
},
},
messageNotification: {
height: 'auto',
display: 'flex',
alignItems: 'center',
'&:hover, &:focus': {
backgroundColor: theme.palette.background.light,
},
},
messageNotificationSide: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
marginRight: theme.spacing(2),
},
messageNotificationBodySide: {
alignItems: 'flex-start',
marginRight: 0,
},
sendMessageButton: {
margin: theme.spacing(4),
marginTop: theme.spacing(2),
marginBottom: theme.spacing(2),
textTransform: 'none',
},
sendButtonIcon: {
marginLeft: theme.spacing(2),
},
}));

View file

@ -0,0 +1,64 @@
import React, { useEffect, useState, useContext, useRef } from 'react'
import { StoreContext } from '../../store/store'
import './style.scss'
import axios from 'axios'
import ContentEditable from 'react-contenteditable'
import { ICON_IMGUPLOAD } from '../../Icons'
import { Link } from 'react-router-dom'
import { API_URL } from '../../config'
import Loader from '../Loader'
import TweetCard from '../TweetCard'
import MakeSqueak from '../MakeSqueak'
const Home = () => {
const { state, actions } = useContext(StoreContext)
const { session } = state
useEffect(() => {
window.scrollTo(0, 0)
// actions.getTweets({lastTweet: null})
reloadTweets();
}, [])
const getLastSqueak = (squeakLst) => {
if (squeakLst == null) {
return null;
} if (squeakLst.length === 0) {
return null;
}
return squeakLst.slice(-1)[0];
};
const getMoreTweets = () => {
let lastTweet = getLastSqueak(state.tweets);
actions.getTweets({lastTweet: lastTweet});
}
const reloadTweets = () => {
actions.clearTweets();
actions.getTweets({lastTweet: null});
}
return (
<div className="Home-wrapper">
<div className="Home-header-wrapper">
<h2 className="Home-header">
Latest Tweets
</h2>
</div>
{session ? <MakeSqueak /> : null }
<div className="Tweet-input-divider"></div>
{state.tweets.map(t => {
return <TweetCard tweet={t} key={t.getSqueakHash()} id={t.getSqueakHash()} user={t.getAuthor()} />
})}
{/* TODO: fix get loading state by doing this: https://medium.com/stashaway-engineering/react-redux-tips-better-way-to-handle-loading-flags-in-your-reducers-afda42a804c6 */}
{state.loading ? <Loader /> : <div onClick={() => getMoreTweets()} className='tweet-btn-side tweet-btn-active'>
Load more
</div>}
</div>
)
}
export default Home

View file

@ -0,0 +1,40 @@
.Home-wrapper{
max-width: 600px;
border-right: 1px solid rgb(230, 236, 240);
width: 100%;
//but doesnt show when there are less elements
// height: 100vh;
display: flex;
flex-direction: column;
min-height: 2000px;
}
.Home-header-wrapper{
position: sticky;
border-bottom: 1px solid rgb(230, 236, 240);
border-left: 1px solid rgb(230, 236, 240);
background-color: rgb(255, 255, 255);
z-index: 3;
top: 0px;
display: flex;
align-items: center;
cursor: pointer;
height: 53px;
min-height: 53px;
padding-left: 15px;
padding-right: 15px;
max-width: 1000px;
margin: 0 auto;
width: 100%;
}
.Home-header{
font-weight: 800;
font-size: 19px;
color: rgb(20, 23, 26);
line-height: 1.3125;
}
.blue{
color: rgb(29, 161, 242);
}

View file

@ -1,144 +0,0 @@
import React, { useState } from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
TextField,
DialogActions,
Button,
} from '@material-ui/core';
import { useHistory } from 'react-router-dom';
// styles
import useStyles from './styles';
import {
importSigningProfileRequest,
} from '../../squeakclient/requests';
import {
goToProfilePage,
} from '../../navigation/navigation';
export default function ImportSigningProfileDialog({
open,
handleClose,
...props
}) {
const classes = useStyles();
const history = useHistory();
const [profileName, setProfileName] = useState('');
const [privateKey, setPrivateKey] = useState('');
const resetFields = () => {
setProfileName('');
};
const handleChangeProfileName = (event) => {
setProfileName(event.target.value);
};
const handleChangePrivateKey = (event) => {
setPrivateKey(event.target.value);
};
const handleResponse = (response) => {
goToProfilePage(history, response.getProfileId());
};
const handleErr = (err) => {
alert(`Error creating contact profile: ${err}`);
};
function handleSubmit(event) {
event.preventDefault();
console.log('profileName:', profileName);
console.log('privateKey:', privateKey);
if (!profileName) {
alert('Profile Name cannot be empty.');
return;
}
if (!privateKey) {
alert('Private Key cannot be empty.');
return;
}
importSigningProfileRequest(profileName, privateKey, handleResponse, handleErr);
handleClose();
}
function ImportSigningProfileNameInput() {
return (
<TextField
id="standard-textarea"
label="Profile Name"
variant="outlined"
margin="normal"
required
autoFocus
value={profileName}
onChange={handleChangeProfileName}
fullWidth
inputProps={{ maxLength: 64 }}
/>
);
}
function ImportSigningProfilePrivateKeyInput() {
return (
<TextField
id="standard-textarea"
label="Private Key"
variant="outlined"
margin="normal"
required
value={privateKey}
onChange={handleChangePrivateKey}
fullWidth
inputProps={{ maxLength: 64 }}
/>
);
}
function CancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
);
}
function ImportSigningProfilButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Import Signing Profile
</Button>
);
}
return (
<Dialog open={open} onEnter={resetFields} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Import Signing Profile</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
{ImportSigningProfileNameInput()}
</DialogContent>
<DialogContent>
{ImportSigningProfilePrivateKeyInput()}
</DialogContent>
<DialogActions>
{CancelButton()}
{ImportSigningProfilButton()}
</DialogActions>
</form>
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "ImportSigningProfileDialog",
"version": "0.0.0",
"private": true,
"main": "ImportSigningProfileDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -1,97 +0,0 @@
import React from 'react';
import {
Route,
Switch,
Redirect,
withRouter,
} from 'react-router-dom';
import classnames from 'classnames';
// styles
import useStyles from './styles';
// components
import Header from '../Header';
import Sidebar from '../Sidebar';
// pages
import Timeline from '../../pages/timeline';
import SqueakAddress from '../../pages/squeakaddress';
import Search from '../../pages/search';
import Squeak from '../../pages/squeak';
import Profile from '../../pages/profile';
import Wallet from '../../pages/wallet';
import LightningNode from '../../pages/lightningnode';
import Channel from '../../pages/channel';
import Notifications from '../../pages/notifications';
import Profiles from '../../pages/profiles';
import Payments from '../../pages/payments';
import SentPayments from '../../pages/sentpayments';
import ReceivedPayments from '../../pages/receivedpayments';
import Liked from '../../pages/liked';
import Icons from '../../pages/icons';
import Charts from '../../pages/charts';
import Peers from '../../pages/peers';
import Peer from '../../pages/peer';
import PeerAddress from '../../pages/peeraddress';
import Twitter from '../../pages/twitter';
import Settings from '../../pages/settings';
// context
import { useLayoutState } from '../../context/LayoutContext';
function Layout(props) {
const classes = useStyles();
// global
const layoutState = useLayoutState();
return (
<div className={classes.root}>
<>
<Header history={props.history} />
<Sidebar />
<div
className={classnames(classes.content, {
[classes.contentShift]: layoutState.isSidebarOpened,
})}
>
<div className={classes.fakeToolbar} />
<Switch>
<Route path="/app/timeline" component={Timeline} />
<Route path="/app/pubkey/:pubkey" component={SqueakAddress} />
<Route path="/app/search/:searchText" component={Search} />
<Route path="/app/search" component={Search} />
<Route path="/app/squeak/:hash" component={Squeak} />
<Route path="/app/profile/:id" component={Profile} />
<Route path="/app/profiles" component={Profiles} />
<Route path="/app/payments" component={Payments} />
<Route path="/app/sentpayments" component={SentPayments} />
<Route path="/app/receivedpayments" component={ReceivedPayments} />
<Route path="/app/liked" component={Liked} />
<Route path="/app/wallet" component={Wallet} />
<Route path="/app/lightningnode/:pubkey/:host/:port" component={LightningNode} />
<Route path="/app/lightningnode/:pubkey/:host" component={LightningNode} />
<Route path="/app/lightningnode/:pubkey" component={LightningNode} />
<Route path="/app/channel/:txId/:outputIndex" component={Channel} />
<Route path="/app/peers" component={Peers} />
<Route path="/app/peer/:id" component={Peer} />
<Route path="/app/peeraddress/:network/:host/:port" component={PeerAddress} />
<Route path="/app/notifications" component={Notifications} />
<Route path="/app/twitter" component={Twitter} />
<Route path="/app/settings" component={Settings} />
<Route
exact
path="/app/ui"
render={() => <Redirect to="/app/ui/icons" />}
/>
<Route path="/app/ui/icons" component={Icons} />
<Route path="/app/ui/charts" component={Charts} />
</Switch>
</div>
</>
</div>
);
}
export default withRouter(Layout);

View file

@ -1,6 +0,0 @@
{
"name": "Layout",
"version": "1.0.0",
"private": true,
"main": "Layout.js"
}

View file

@ -1,25 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
root: {
display: 'flex',
maxWidth: '100vw',
overflowX: 'hidden',
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
width: 'calc(100vw - 240px)',
minHeight: '100vh',
},
contentShift: {
width: `calc(100vw - ${240 + theme.spacing(6)}px)`,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
fakeToolbar: {
...theme.mixins.toolbar,
},
}));

View file

@ -1,68 +0,0 @@
import React from 'react';
import { useHistory } from 'react-router-dom';
import Card from '@material-ui/core/Card';
import CardHeader from '@material-ui/core/CardHeader';
// icons
import ComputerIcon from '@material-ui/icons/Computer';
import useStyles from '../../pages/wallet/styles';
import {
goToLightningNodePage,
} from '../../navigation/navigation';
export default function LightningPeerListItem({
peer,
...props
}) {
const classes = useStyles({
clickable: true,
});
const history = useHistory();
const onPeerClick = (event) => {
event.preventDefault();
console.log('Handling peer click...');
const pubkey = peer.getPubKey();
const host = getPeerHost();
const port = getPeerPort();
goToLightningNodePage(history, pubkey, host, port);
};
const getPeerHost = () => {
const address = peer.getAddress();
if (address == null) {
return null;
}
const pieces = address.split(':');
return pieces[0];
};
const getPeerPort = () => {
const address = peer.getAddress();
if (address == null) {
return null;
}
const pieces = address.split(':');
if (pieces.length < 2) {
return null;
}
return pieces[1];
};
return (
<Card
className={classes.root}
onClick={onPeerClick}
>
<CardHeader
avatar={<ComputerIcon />}
title={`Pubkey: ${peer.getPubKey()}`}
subheader={`Address: ${peer.getAddress()}`}
/>
</Card>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "LightningPeerListItem",
"version": "0.0.0",
"private": true,
"main": "LightningPeerListItem.js"
}

View file

@ -1,26 +0,0 @@
import React from 'react';
import {
Dialog,
DialogTitle,
} from '@material-ui/core';
import { useHistory } from 'react-router-dom';
// styles
import useStyles from './styles';
export default function LndUnavailableDialog({
open,
handleClose,
...props
}) {
const classes = useStyles();
const history = useHistory();
return (
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Lnd Unavailable</DialogTitle>
Unable to connect to lnd node.
Make sure that lnd node is avaialable, and then reload page.
</Dialog>
);
}

View file

@ -1,6 +0,0 @@
{
"name": "LndUnavailableDialog",
"version": "0.0.0",
"private": true,
"main": "LndUnavailableDialog.js"
}

View file

@ -1,43 +0,0 @@
import { makeStyles } from '@material-ui/styles';
export default makeStyles((theme) => ({
widgetWrapper: {
display: 'flex',
minHeight: '100%',
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
'&:hover': {
backgroundColor: theme.palette.primary.main,
color: 'rgba(255, 255, 255, 0.35)',
},
},
}));

View file

@ -0,0 +1,23 @@
import React from 'react'
import './style.css'
const Loader = () => {
return(
<div className="loader-wrapper">
<svg className="loader_svg" version="1.1" id="loader-1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="40px" height="40px" viewBox="0 0 50 50" xmlSpace="preserve">
<path fill="#1da1f2" d="M25.251,6.461c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615V6.461z">
<animateTransform attributeType="xml"
attributeName="transform"
type="rotate"
from="0 25 25"
to="360 25 25"
dur="0.6s"
repeatCount="indefinite"/>
</path>
</svg>
</div>
)
}
export default Loader

View file

@ -0,0 +1,15 @@
.loader-wrapper{
position: relative;
height: 50%;
margin: 50px 0;
}
.loader_svg{
fill: yellow;
enable-background:new 0 0 50 50;
position: absolute;
left: 50%;
top: 15%;
transform: translate(-50%,50%);
}

View file

@ -0,0 +1,58 @@
import React, { useState, useContext } from 'react'
import { StoreContext } from '../../store/store'
import './style.scss'
import { Link, Redirect } from 'react-router-dom'
import { ICON_LOGO } from '../../Icons'
const LoginPage = () => {
const { state, actions } = useContext(StoreContext)
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const Login = (e) => {
e.preventDefault()
if(username.length && password.length){
const values = {
username,
password
}
actions.login(values)
}
}
return(
<div className="login-wrapper">
{state.loggedin && <Redirect to="/home" />}
<ICON_LOGO/>
<h1 className="login-header">
Log in to Twitter
</h1>
{state.msg === 'Incorrect email or password' && <p className="login-error"> The username/email or password you entered is incorrect. </p>}
<form id="loginForm" onSubmit={(e)=>Login(e)} className="login-form">
<div className="login-input-wrap">
<div className="login-input-content">
<label>Email or username</label>
<input onChange={(e)=>setUsername(e.target.value)} type="text" name="username" className="login-input"/>
</div>
</div>
<div className="login-input-wrap">
<div className="login-input-content">
<label>Password</label>
<input onChange={(e)=>setPassword(e.target.value)} type="password" name="password" className="login-input"/>
</div>
</div>
<button type="submit" form="loginForm" className={username.length && password.length > 0 ? "login-btn-wrap button-active": "login-btn-wrap"}>
Log in
</button>
</form>
<p className="signup-option">
<Link to="/app/signup">
Sign up for Twitter
</Link>
</p>
</div>
)
}
export default LoginPage

Some files were not shown because too many files have changed in this diff Show more