WIP React structure
This commit is contained in:
parent
fd954bf05b
commit
9a24326f5f
12 changed files with 2937 additions and 43 deletions
|
@ -12,7 +12,7 @@
|
||||||
"in-source": false
|
"in-source": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"suffix": ".bs.js",
|
"suffix": ".mjs",
|
||||||
"reason": { "react-jsx": 3 },
|
"reason": { "react-jsx": 3 },
|
||||||
"bs-dependencies": ["@rescript/react"]
|
"bs-dependencies": ["@rescript/react", "@ryyppy/rescript-promise", "rescript-aws-sdk-v3"]
|
||||||
}
|
}
|
||||||
|
|
2698
package-lock.json
generated
2698
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -2,13 +2,15 @@
|
||||||
"name": "fanzine",
|
"name": "fanzine",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "A web extension to publish content on a S3 website",
|
"description": "A web extension to publish content on a S3 website",
|
||||||
"main": "src/main.bs.js",
|
"main": "s3.mjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.58.0",
|
"@aws-sdk/client-s3": "^3.58.0",
|
||||||
"@rescript/react": "^0.10.3",
|
"@rescript/react": "^0.10.3",
|
||||||
|
"@ryyppy/rescript-promise": "^2.1.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^18.0.0"
|
"react-dom": "^18.0.0",
|
||||||
|
"rescript-aws-sdk-v3": "0.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"rescript": "^9.1.4",
|
"rescript": "^9.1.4",
|
||||||
|
|
31
src/App.res
31
src/App.res
|
@ -1,4 +1,33 @@
|
||||||
|
type appState = {
|
||||||
|
client: option<S3.client>,
|
||||||
|
config: option<S3.config>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@scope("JSON") @val
|
||||||
|
external parseS3Config: string => S3.config = "parse"
|
||||||
|
|
||||||
|
let buildState = () => {
|
||||||
|
let maybeConfig = Dom.Storage2.localStorage -> Dom.Storage2.getItem("s3.config.json")
|
||||||
|
|
||||||
|
let config = switch (maybeConfig) {
|
||||||
|
| Some(data) => Some(data -> parseS3Config)
|
||||||
|
| None => None
|
||||||
|
}
|
||||||
|
|
||||||
|
let client = switch (config) {
|
||||||
|
| Some(data) => Some(data -> S3.createClientWithConfig)
|
||||||
|
| None => None
|
||||||
|
}
|
||||||
|
|
||||||
|
{ client: client, config: config }
|
||||||
|
}
|
||||||
|
|
||||||
@react.component
|
@react.component
|
||||||
let make = () => {
|
let make = () => {
|
||||||
<Menu/>
|
let (state, setState) = React.useState(buildState);
|
||||||
|
|
||||||
|
switch (state.client) {
|
||||||
|
| None => <Settings/>
|
||||||
|
| Some(_) => <div><Menu/><ShareText/></div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
44
src/Bindings/S3.res
Normal file
44
src/Bindings/S3.res
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
type client
|
||||||
|
|
||||||
|
type endpoint = {
|
||||||
|
protocol: string,
|
||||||
|
hostname: string,
|
||||||
|
path: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
type credentials = {
|
||||||
|
accessKeyId: string,
|
||||||
|
secretAccessKey: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
type config = {
|
||||||
|
endpoint: option<endpoint>,
|
||||||
|
credentials: option<credentials>,
|
||||||
|
tls: option<bool>,
|
||||||
|
forcePathStyle: option<bool>,
|
||||||
|
region: option<string>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@module("@aws-sdk/client-s3") @new external createClientWithConfig: config => client = "S3Client"
|
||||||
|
|
||||||
|
module ListBuckets = {
|
||||||
|
@module("@aws-sdk/client-s3") @new external new: {..} => AwsSdkV3.S3.ListBuckets.t = "ListBucketsCommand"
|
||||||
|
let make = () => Js.Obj.empty() -> new
|
||||||
|
@send external send: (client, AwsSdkV3.S3.ListBuckets.t) => Js.Promise.t<AwsSdkV3.S3.ListBuckets.response> = "send"
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
let s3 = createClientWithConfig({})
|
||||||
|
|
||||||
|
ListBuckets.send(s3, ListBuckets.make())
|
||||||
|
-> Promise.then(result => {
|
||||||
|
Js.log((result.owner -> Belt.Option.getExn).id)
|
||||||
|
Promise.resolve()
|
||||||
|
})
|
||||||
|
-> Promise.catch(error => {
|
||||||
|
Js.log(error)
|
||||||
|
Promise.resolve()
|
||||||
|
})
|
||||||
|
-> ignore
|
||||||
|
*/
|
|
@ -1,12 +1,13 @@
|
||||||
@react.component
|
@react.component
|
||||||
let make = () => {
|
let make = () => {
|
||||||
<section className="menu">
|
<section className="menu">
|
||||||
<div className="menu-title">{ React.string("Publier sur mon espace web")}</div>
|
<div className="menu-title">{ React.string("Fanzine")}</div>
|
||||||
<a className="menu-elem" href="#plain-text">{ React.string("Texte brut")}</a>
|
<a className="menu-elem" href="#plain-text">{ React.string("Texte brut")}</a>
|
||||||
<div className="menu-elem">{ React.string("Galerie photo")}</div>
|
<div className="menu-elem">{ React.string("Galerie photo")}</div>
|
||||||
<div className="menu-elem">{ React.string("Fichiers")}</div>
|
<div className="menu-elem">{ React.string("Fichiers")}</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div className="menu-elem">{ React.string("Site statique")}</div>
|
<div className="menu-elem">{ React.string("Site statique")}</div>
|
||||||
|
<div className="menu-elem">{ React.string("Explorateur de fichier")}</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div className="menu-elem">{ React.string(`Paramètres`)}</div>
|
<div className="menu-elem">{ React.string(`Paramètres`)}</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
116
src/Settings.res
Normal file
116
src/Settings.res
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
@react.component
|
||||||
|
let make = () => {
|
||||||
|
|
||||||
|
module Form = {
|
||||||
|
type proto = HTTP | HTTPS;
|
||||||
|
type urlStyle = VHOST | PATH;
|
||||||
|
|
||||||
|
type t = {
|
||||||
|
protocol: option<proto>,
|
||||||
|
endpoint: option<string>,
|
||||||
|
url: option<urlStyle>,
|
||||||
|
region: option<string>,
|
||||||
|
keyId: option<string>,
|
||||||
|
secretKey: option<string>,
|
||||||
|
}
|
||||||
|
|
||||||
|
type e =
|
||||||
|
| UndefinedParameter
|
||||||
|
|
||||||
|
|
||||||
|
let make = () => { protocol: None, endpoint: None, url: None, region: None, keyId: None, secretKey: None }
|
||||||
|
|
||||||
|
let _build_unsafe = f => {
|
||||||
|
open Belt.Option
|
||||||
|
|
||||||
|
{
|
||||||
|
S3.endpoint: Some({
|
||||||
|
protocol: switch(f.protocol -> getExn) { | HTTP => "http" | _ => "https" },
|
||||||
|
hostname: f.endpoint -> getExn,
|
||||||
|
path: "/"
|
||||||
|
}),
|
||||||
|
credentials: Some({
|
||||||
|
accessKeyId: f.keyId -> getExn,
|
||||||
|
secretAccessKey: f.secretKey -> getExn,
|
||||||
|
}),
|
||||||
|
tls: Some((f.protocol -> getExn) == HTTPS),
|
||||||
|
forcePathStyle: Some((f.url -> getExn) == PATH),
|
||||||
|
region: Some(f.region -> getExn),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let build: t => Belt.Result.t<S3.config, e> = f => {
|
||||||
|
switch(f -> _build_unsafe) {
|
||||||
|
| config => Ok(config)
|
||||||
|
| exception Not_found => Error(UndefinedParameter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (form, updateForm) = React.useState(_ => Form.make());
|
||||||
|
|
||||||
|
let login = _ => {
|
||||||
|
Js.log(form -> Form.build)
|
||||||
|
}
|
||||||
|
|
||||||
|
let setProtocol = evt => {
|
||||||
|
let value = switch (ReactEvent.Form.target(evt)["value"]) {
|
||||||
|
| "http" => Form.HTTP
|
||||||
|
| _ => Form.HTTPS
|
||||||
|
}
|
||||||
|
updateForm(form => {...form, protocol: Some(value)})
|
||||||
|
}
|
||||||
|
|
||||||
|
let setEndpoint = evt => {
|
||||||
|
let value = (ReactEvent.Form.target(evt)["value"])
|
||||||
|
updateForm(form => {...form, endpoint: Some(value) })
|
||||||
|
}
|
||||||
|
|
||||||
|
let setRegion = _ => ()
|
||||||
|
|
||||||
|
let setUrlStyle = evt => {
|
||||||
|
let value = switch (ReactEvent.Form.target(evt)["value"]) {
|
||||||
|
| "path" => Form.PATH
|
||||||
|
| _ => Form.VHOST
|
||||||
|
}
|
||||||
|
updateForm(form => {...form, url: Some(value)})
|
||||||
|
}
|
||||||
|
|
||||||
|
let setKeyId = _ => ()
|
||||||
|
let setSecretKey = _ => ()
|
||||||
|
|
||||||
|
<section className="menu">
|
||||||
|
<div className="menu-title">{ "Configuration"->React.string }</div>
|
||||||
|
<form>
|
||||||
|
<fieldset>
|
||||||
|
<legend> { `Réseau` -> React.string } </legend>
|
||||||
|
<div className="group">
|
||||||
|
<select name="protocol" onChange={setProtocol}>
|
||||||
|
<option value="https">{ "https://"->React.string }</option>
|
||||||
|
<option value="http">{ "http://"->React.string }</option>
|
||||||
|
</select>
|
||||||
|
<input name="endpoint" type_="text" placeholder="garage.example.tld" onChange={setEndpoint} />
|
||||||
|
</div>
|
||||||
|
<div className="group">
|
||||||
|
<input name="region" type_="text" placeholder="garage" onChange={setRegion} />
|
||||||
|
<select name="urlStyle" onChange={setUrlStyle} >
|
||||||
|
<option value="path">{ "chemin"->React.string }</option>
|
||||||
|
<option value="vhost">{ `hôte`->React.string }</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend> { "Authentification"->React.string }</legend>
|
||||||
|
<div className="group">
|
||||||
|
<input name="keyId" type_="text" placeholder="GKxxx" onChange={setKeyId}/>
|
||||||
|
</div>
|
||||||
|
<div className="group">
|
||||||
|
<input name="secretKey" type_="password" placeholder="************" onChange={setSecretKey} />
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<input className="primary" type_="button" onClick={login} />
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
@react.component
|
||||||
|
let make = () => {
|
||||||
|
|
||||||
|
<section id="plain-text">
|
||||||
|
<div className="menu-title"><a href="#home">{ React.string("<") }</a> { React.string("Texte brut") }</div>
|
||||||
|
<textarea id="plain-text-content" placeholder=`Collez votre texte à partager ici`></textarea>
|
||||||
|
<a className="primary" id="plain-text-send" href="#plain-text-done"> { React.string("Envoyer") }</a>
|
||||||
|
</section>
|
||||||
|
}
|
|
@ -22,12 +22,35 @@ a:hover {
|
||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.primary {
|
select, input[type=text], input[type=password] {
|
||||||
|
margin: 0.2rem;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary {
|
||||||
|
margin: 1rem 0rem 0.5rem 0rem;
|
||||||
padding: 0.2rem 0.5rem 0.4rem 0.5rem;
|
padding: 0.2rem 0.5rem 0.4rem 0.5rem;
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: var(--main-color);
|
background-color: var(--main-color);
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
background-color: #000;
|
||||||
|
color: #fff;
|
||||||
|
padding: 3px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
margin-top: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
|
@ -44,6 +67,21 @@ textarea {
|
||||||
/*box-shadow: 1px 1px 1px #999;*/
|
/*box-shadow: 1px 1px 1px #999;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* form group */
|
||||||
|
form .group {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .group input[type="text"], form .group input[type="password"] {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .group select {
|
||||||
|
flex-shrink: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Menu interface */
|
/* Menu interface */
|
||||||
.menu-elem, .menu-title {
|
.menu-elem, .menu-title {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -5,33 +5,6 @@
|
||||||
<link href="fanzine.css" rel="stylesheet">
|
<link href="fanzine.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<!-- <section id="home">
|
|
||||||
<div class="menu-title">Publier sur mon espace web</div>
|
|
||||||
<a class="menu-elem" href="#plain-text">Texte brut</a>
|
|
||||||
<div class="menu-elem">Galerie photo</div>
|
|
||||||
<div class="menu-elem">Fichiers</div>
|
|
||||||
<hr>
|
|
||||||
<div class="menu-elem">Site statique</div>
|
|
||||||
<hr>
|
|
||||||
<div class="menu-elem">Paramètres</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="plain-text">
|
|
||||||
<div class="menu-title"><a href="#home"><</a> Texte brut</div>
|
|
||||||
<textarea id="plain-text-content" placeholder="Collez votre texte à partager ici"></textarea>
|
|
||||||
<a class="primary" id="plain-text-send" href="#plain-text-done">Envoyer</a>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="plain-text-done">
|
|
||||||
<div id="output">Please wait...</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="configure">
|
|
||||||
<div class="menu-title">Paramètres</div>
|
|
||||||
|
|
||||||
</section>-->
|
|
||||||
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script src="fanzine.js"></script>
|
<script src="fanzine.js"></script>
|
||||||
|
|
|
@ -5,7 +5,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
entry: {
|
entry: {
|
||||||
main: "./lib/es6/src/main.bs.js",
|
main: "./lib/es6/src/Main.mjs",
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
modules: [path.resolve(__dirname, 'node_modules'), 'node_modules']
|
modules: [path.resolve(__dirname, 'node_modules'), 'node_modules']
|
||||||
|
|
Loading…
Reference in a new issue