Updated smart form

This commit is contained in:
Andrew Gundersen 2022-04-04 08:02:16 -05:00
commit 78eb4050ae
4 changed files with 109 additions and 62 deletions

View file

@ -2,8 +2,9 @@ const exec = require('child_process').exec;
const sign = require("electron-osx-sign").signAsync;
const notarize = require("electron-notarize").notarize;
const app = "Crimata.app";
const dir = `${app}/Contents/Resources/app`;
const name = "Crimata";
const dir = `${name}.app/Contents/Resources/app`;
const registry = "https://gitlab.com/api/v4/projects/25637892/packages/generic/crimata/0.0.1/Crimata.zip";
const runShellCommand = (cmd) => (new Promise((resolve, reject) => {
exec(cmd, (error, stdout, stderr) => {
@ -11,8 +12,11 @@ const runShellCommand = (cmd) => (new Promise((resolve, reject) => {
});
}));
// /projects/:id/packages/generic/:package_name/:package_version/:file_name?status=:status
const signConfig = {
"app": app,
"app": name + ".app",
"hardened-runtime": true,
"gatekeeper-assess": false,
"signature-flags": "library",
@ -21,7 +25,7 @@ const signConfig = {
};
const notarizeConfig = {
appPath: app,
appPath: name + ".app",
appleId: "gundersena@crimata.com",
appBundleId: "com.crimata.CrimataMessenger",
appleIdPassword: process.env["AC_PASSWORD"]
@ -31,18 +35,24 @@ const notarizeConfig = {
try
{
console.log("Building...");
await runShellCommand(`rm -rf ${dir} && mkdir ${dir} && cp -r {package.json,src,node_modules} ${dir}
`);
// console.log("Building...");
// await runShellCommand(`rm -rf ${dir} && mkdir ${dir} && cp -r {package.json,src,node_modules} ${dir}
// `);
console.log("Cleaning...");
await runShellCommand(`xattr -cr ${app}`);
// console.log("Cleaning...");
// await runShellCommand(`xattr -cr ${name}.app`);
console.log("Signing...");
await sign(signConfig);
// console.log("Signing...");
// await sign(signConfig);
console.log("Notarizing...");
await notarize(notarizeConfig);
// console.log("Notarizing...");
// await notarize(notarizeConfig);
// console.log("Compressing...");
// await runShellCommand(`zip -vr ${name}.zip ${name}.app`);
console.log("Uploading to Gitlab...");
await runShellCommand(`curl --header "PRIVATE-TOKEN: ${process.env["API_TOKEN"]}" --upload-file ${name}.zip "${registry}"`);
}
catch (e)
{
@ -50,3 +60,10 @@ const notarizeConfig = {
}
})();
/**
* Copy file from local to remote:
*
* scp -r Electron.app gundersena@crimata-server:~/Desktop/crimata/website/assets
*/

View file

@ -0,0 +1,23 @@
export const isEmail = (value) => {
console.log(value);
if (!/(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/.test(value)) {
return "Not a valid email."
}
}
export const isPassword = (value) => {
if (!/^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{6,16}$/.test(value)) {
return "Password must have symbol, number, uppercase and be 6-16 length.";
}
}
/**
* Name is optional and therefore doesn't return error if no value.
*/
export const isName = (value) => {
if (value) {
if (!/^([a-zA-Z ]){5,30}$/.test(value)) {
return "Name must be at least 5 long.";
}
}
}

View file

@ -1,83 +1,86 @@
// Basic debounce implentation
const debounce = (fn, delay) => {
let timeoutId;
const wrapper = (...args) => {
if (timeoutId) {
clearTimeout(timeoutId);
}
return (...args) => {
if (timeoutId) clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn(...args);
}, delay);
}
return wrapper;
}
const SmartForm =
/**
* Dynamic form component with auto submission and validation.
*
* props
* .fields => List of field objects
* .channel => e.g. "/login"
* .modifier => Form description
*/
const SmartForm =
{
props: ["fields", "channel", "modifier"],
setup(props)
{
let logsEl;
// Error log below form
const log = Vue.ref(props.modifier);
const submit = debounce(async (data) => {
/**
* Valid and submit form data to channel.
*/
const submit = async () => {
let object = {};
data.forEach(function(value, key){
object[key] = value;
});
const err = await window.mainApi.invoke(props.channel, object)
if (err) {
logsEl.innerText = err;
logsEl.style.color = "red";
return;
// Validate each field
for (let field of props.fields) {
const error = field.valid(field.value);
if (error) return error;
}
logsEl.style.color = "green";
logsEl.innerText = props.modifier;
// Reduce fields into the form
const form = props.fields.reduce((v, n) => {
v[n.name] = n.value;
return v;
}, {});
console.log("Submitting =>", form);
return await window.mainApi.invoke(props.channel, form);
}
/**
* Wrapper for submit that is debounced and updates UI elements
*/
const preSub = debounce(async () => {
const error = await submit();
log.value = error ? error : props.modifier
}, 2000);
Vue.onMounted(() => {
const el = document.getElementById("smart-form");
logsEl = el.querySelector(".smart-form-logs");
logsEl.innerText = props.modifier;
el.addEventListener("input", () => {
submit(new FormData(el))
});
});
return { preSub, log };
},
template: `
<form id="smart-form" class="smart-form">
<form id="smart-form" class="smart-form" v-on:input="preSub">
<div v-for="field in fields" style="display:flex">
<input type="text" class="text-field"
<input type="text" class="text-field"
v-model="field.value"
:name="field.name"
:value="field.value"
:placeholder="field.placeholder"
/>
</div>
<div class="smart-form-logs"></div>
<div class="smart-form-logs">{{ log }}</div>
</form>`
}
export default SmartForm;

View file

@ -1,4 +1,5 @@
import SmartForm from "./form.js";
import { isEmail, isPassword, isName } from "./control/valid.js";
const Login =
{
@ -8,7 +9,7 @@ const Login =
setup()
{
return;
return { isEmail, isPassword, isName };
},
template: `
@ -19,16 +20,19 @@ const Login =
name: 'email',
value: null,
placeholder: 'Email',
valid: isEmail
},
{
name: 'password',
value: null,
placeholder: 'Password'
placeholder: 'Password',
valid: isPassword
},
{
name: 'name',
value: null,
placeholder: 'Name'
placeholder: 'Name',
valid: isName
}]"
:channel="'auth'"
:modifier="'Login or enter name to register.'"
@ -37,4 +41,4 @@ const Login =
</div>`
}
export default Login;
export default Login;