mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
added tap_share
settings
This commit is contained in:
parent
02db8c6c13
commit
972bd9802b
9
packages/app/constants/settings/tap_share/context.js
Normal file
9
packages/app/constants/settings/tap_share/context.js
Normal file
@ -0,0 +1,9 @@
|
||||
import React from "react"
|
||||
|
||||
const RegisterNewTagStepsDefaultContext = {
|
||||
next: () => { },
|
||||
prev: () => { },
|
||||
submit: () => { },
|
||||
}
|
||||
|
||||
export default React.createContext(RegisterNewTagStepsDefaultContext)
|
11
packages/app/constants/settings/tap_share/errors.js
Normal file
11
packages/app/constants/settings/tap_share/errors.js
Normal file
@ -0,0 +1,11 @@
|
||||
export default {
|
||||
NFC_NOT_SUPPORTED: "Your device doesn't support NFC.",
|
||||
NFC_NOT_ENABLED: "NFC is not enabled.",
|
||||
NFC_NOT_READABLE: "NFC is not readable.",
|
||||
NFC_NOT_WRITABLE: "NFC is not writable.",
|
||||
NFC_READ_ERROR: "Cannot read NFC tag. Please try again.",
|
||||
NFC_WRITE_ERROR: "Cannot write NFC tag. Please try again.",
|
||||
NFC_NOT_OWNER: "This tag is not owned by you.",
|
||||
NFC_NOT_REGISTERED: "This tag is not registered.",
|
||||
NFC_NOT_MATCH: "This tag is not match with the registered tag.",
|
||||
}
|
301
packages/app/constants/settings/tap_share/index.jsx
Normal file
301
packages/app/constants/settings/tap_share/index.jsx
Normal file
@ -0,0 +1,301 @@
|
||||
import React from "react"
|
||||
import * as antd from "antd"
|
||||
import { Input } from "antd-mobile"
|
||||
import classnames from "classnames"
|
||||
import NFCModel from "comty.js/models/nfc"
|
||||
import { Icons } from "components/Icons"
|
||||
|
||||
import AnimationPlayer from "components/AnimationPlayer"
|
||||
|
||||
import StepsContext from "./context"
|
||||
import NFC_ERRORS from "./errors"
|
||||
|
||||
import "./index.less"
|
||||
|
||||
import CheckRegister from "./steps/check_register"
|
||||
import DataEditor from "./steps/data_editor"
|
||||
import TagWritter from "./steps/tag_writter"
|
||||
import Success from "./steps/success"
|
||||
|
||||
const RegisterNewTagSteps = [
|
||||
CheckRegister,
|
||||
DataEditor,
|
||||
TagWritter,
|
||||
Success,
|
||||
]
|
||||
|
||||
const RegisterNewTag = (props) => {
|
||||
const [step, setStep] = React.useState(0)
|
||||
const [stepsValues, setStepsValues] = React.useState({
|
||||
...props.tagData ?? {}
|
||||
})
|
||||
|
||||
const nextStep = () => {
|
||||
setStep((step) => step + 1)
|
||||
}
|
||||
|
||||
const prevStep = () => {
|
||||
setStep((step) => step - 1)
|
||||
}
|
||||
|
||||
const finish = () => {
|
||||
if (typeof props.onFinish === "function") {
|
||||
props.onFinish()
|
||||
}
|
||||
|
||||
if (typeof props.close === "function") {
|
||||
props.close()
|
||||
}
|
||||
}
|
||||
|
||||
// create a react context for the steps
|
||||
const StepsContextValue = {
|
||||
next: nextStep,
|
||||
prev: prevStep,
|
||||
values: stepsValues,
|
||||
setValue: (key, value) => {
|
||||
setStepsValues((stepsValues) => {
|
||||
return {
|
||||
...stepsValues,
|
||||
[key]: value
|
||||
}
|
||||
})
|
||||
},
|
||||
onFinish: finish,
|
||||
nfcReader: app.cores.nfc.instance(),
|
||||
close: props.close
|
||||
}
|
||||
|
||||
if (props.tagData) {
|
||||
return <div className="tap-share-register">
|
||||
<div className="tap-share-register-content">
|
||||
<StepsContext.Provider value={StepsContextValue}>
|
||||
<DataEditor
|
||||
onFinish={finish}
|
||||
/>
|
||||
</StepsContext.Provider>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
if (app.cores.nfc.incompatible) {
|
||||
return <antd.Result
|
||||
status="error"
|
||||
title="Error"
|
||||
subTitle="Your device doesn't support NFC."
|
||||
/>
|
||||
}
|
||||
|
||||
return <div
|
||||
className={classnames(
|
||||
"tap-share-register",
|
||||
{
|
||||
["compact"]: step > 0
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className="tap-share-register-header">
|
||||
<antd.Button
|
||||
type="link"
|
||||
onClick={prevStep}
|
||||
disabled={step === 0}
|
||||
icon={<Icons.MdChevronLeft />}
|
||||
className={classnames(
|
||||
"tap-share-register-header-back",
|
||||
{
|
||||
["hidden"]: step === 0
|
||||
}
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="tap-share-register-header-icon">
|
||||
<Icons.MdNfc />
|
||||
</div>
|
||||
|
||||
<h1>
|
||||
Register new tag
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="tap-share-register-content">
|
||||
<StepsContext.Provider value={StepsContextValue}>
|
||||
{
|
||||
React.createElement(RegisterNewTagSteps[step])
|
||||
}
|
||||
</StepsContext.Provider>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
const TagItem = (props) => {
|
||||
return <div
|
||||
key={props.tag.serialNumber}
|
||||
id={props.tag.serialNumber}
|
||||
className="tap-share-own_tags-item"
|
||||
>
|
||||
<div className="tap-share-own_tags-item-icon">
|
||||
<Icons.MdNfc />
|
||||
</div>
|
||||
|
||||
<div className="tap-share-own_tags-item-title">
|
||||
<h4>
|
||||
{props.tag.alias}
|
||||
</h4>
|
||||
|
||||
<span>
|
||||
{props.tag.serial}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="tap-share-own_tags-item-actions">
|
||||
<antd.Button
|
||||
icon={<Icons.MdEdit />}
|
||||
onClick={props.onEdit}
|
||||
/>
|
||||
<antd.Button
|
||||
icon={<Icons.MdDelete />}
|
||||
danger
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
class OwnTags extends React.Component {
|
||||
state = {
|
||||
loading: true,
|
||||
error: null,
|
||||
data: null,
|
||||
}
|
||||
|
||||
loadData = async () => {
|
||||
this.setState({
|
||||
loading: true,
|
||||
})
|
||||
|
||||
const result = await NFCModel.getOwnTags()
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
this.setState({
|
||||
error: err.message,
|
||||
loading: false,
|
||||
data: null
|
||||
})
|
||||
return false
|
||||
})
|
||||
|
||||
if (!result) {
|
||||
return false
|
||||
}
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
data: result,
|
||||
error: null
|
||||
})
|
||||
}
|
||||
|
||||
handleTagRead = async (error, tag) => {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
return false
|
||||
}
|
||||
|
||||
const ownedTag = this.state.data.find((ownedTag) => {
|
||||
return ownedTag.serial === tag.serialNumber
|
||||
})
|
||||
|
||||
console.log(ownedTag)
|
||||
|
||||
if (!ownedTag) {
|
||||
app.message.error("This tag is not registered or you don't have permission to edit it.")
|
||||
return false
|
||||
}
|
||||
|
||||
return OpenTagEditor({
|
||||
tag: ownedTag
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount = async () => {
|
||||
await this.loadData()
|
||||
|
||||
app.cores.nfc.subscribe(this.handleTagRead)
|
||||
}
|
||||
|
||||
componentWillUnmount = () => {
|
||||
app.cores.nfc.unsubscribe(this.handleTagRead)
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.loading) {
|
||||
return <div className="tap-share-own_tags">
|
||||
<antd.Skeleton />
|
||||
</div>
|
||||
}
|
||||
|
||||
return <div className="tap-share-own_tags">
|
||||
{
|
||||
this.state.data.length === 0 && <antd.Empty
|
||||
description="You don't have any tags yet."
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
this.state.data.length > 0 && this.state.data.map((tag) => {
|
||||
return <TagItem
|
||||
key={tag.serialNumber}
|
||||
tag={tag}
|
||||
onEdit={() => {
|
||||
OpenTagEditor({
|
||||
tag
|
||||
})
|
||||
}}
|
||||
/>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
const OpenTagEditor = ({ tag, onFinish = () => app.navigation.softReload() } = {}) => {
|
||||
app.DrawerController.open("tag_register", RegisterNewTag, {
|
||||
componentProps: {
|
||||
tagData: tag,
|
||||
onFinish: onFinish,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const TapShareRender = () => {
|
||||
return <div className="tap-share-render">
|
||||
<div className="tap-share-field">
|
||||
<div className="tap-share-field_header">
|
||||
<h1>
|
||||
<Icons.MdSpoke /> Registered Tags
|
||||
</h1>
|
||||
</div>
|
||||
<span className="tip">
|
||||
<Icons.MdInfo /> You can quickly edit your tags by tapping them.
|
||||
</span>
|
||||
|
||||
<OwnTags />
|
||||
</div>
|
||||
|
||||
<antd.Button
|
||||
type="primary"
|
||||
icon={<Icons.Plus />}
|
||||
onClick={() => OpenTagEditor()}
|
||||
>
|
||||
Add new
|
||||
</antd.Button>
|
||||
</div>
|
||||
}
|
||||
|
||||
export default {
|
||||
id: "tap_share",
|
||||
icon: "MdNfc",
|
||||
label: "Tap Share",
|
||||
group: "app",
|
||||
render: TapShareRender
|
||||
}
|
281
packages/app/constants/settings/tap_share/index.less
Normal file
281
packages/app/constants/settings/tap_share/index.less
Normal file
@ -0,0 +1,281 @@
|
||||
.tap-share-render {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
gap: 20px;
|
||||
|
||||
.tap-share-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
background-color: var(--background-color-accent);
|
||||
padding: 20px;
|
||||
|
||||
border-radius: 12px;
|
||||
|
||||
gap: 10px;
|
||||
|
||||
span {
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.tap-share-field_header {
|
||||
display: flex;
|
||||
|
||||
flex-direction: row;
|
||||
|
||||
align-items: center;
|
||||
|
||||
h1,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tap-share-own_tags {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
align-items: center;
|
||||
|
||||
gap: 10px;
|
||||
|
||||
.tap-share-own_tags-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
|
||||
background-color: var(--background-color-primary);
|
||||
|
||||
border-radius: 8px;
|
||||
|
||||
.tap-share-own_tags-item-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.tap-share-own_tags-item-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
width: 100%;
|
||||
|
||||
gap: 10px;
|
||||
|
||||
color: var(--text-color);
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
span {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 0.7rem;
|
||||
opacity: 0.7px;
|
||||
}
|
||||
}
|
||||
|
||||
.tap-share-own_tags-item-actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
align-items: center;
|
||||
|
||||
gap: 10px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.tap-share-register {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
height: 100%;
|
||||
|
||||
gap: 20px;
|
||||
|
||||
&.compact {
|
||||
.tap-share-register-header {
|
||||
padding: 10px;
|
||||
flex-direction: row;
|
||||
|
||||
justify-content: flex-start;
|
||||
|
||||
.tap-share-register-header-icon {
|
||||
font-size: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tap-share-register-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
padding: 20px;
|
||||
|
||||
gap: 10px;
|
||||
|
||||
background-color: var(--background-color-accent);
|
||||
|
||||
border-radius: 12px;
|
||||
|
||||
transition: all 150ms ease-in-out;
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tap-share-register-header-back {
|
||||
font-size: 5rem;
|
||||
color: var(--colorPrimary);
|
||||
|
||||
&.hidden {
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tap-share-register-header-icon {
|
||||
font-size: 5rem;
|
||||
color: var(--colorPrimary);
|
||||
|
||||
svg {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tap-share-register-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
height: 100%;
|
||||
|
||||
.tap-share-register_step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
padding: 0 10px;
|
||||
|
||||
transition: all 150ms ease-in-out;
|
||||
|
||||
h1 {
|
||||
transition: all 150ms ease-in-out;
|
||||
}
|
||||
|
||||
&.centered {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ant-form {
|
||||
width: 100%;
|
||||
|
||||
.ant-form-item {
|
||||
height: fit-content;
|
||||
padding: 0;
|
||||
|
||||
.ant-form_with_selector {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
align-items: center;
|
||||
|
||||
gap: 10px;
|
||||
|
||||
.ant-select {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.ant-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-form-item-label {
|
||||
height: fit-content;
|
||||
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
label {
|
||||
height: fit-content;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tap-share-register_step_actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
align-items: center;
|
||||
|
||||
gap: 20px;
|
||||
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.animation-player {
|
||||
width: 50%;
|
||||
|
||||
&.loading {
|
||||
.phone-loop {
|
||||
color: #17b2ff;
|
||||
}
|
||||
}
|
||||
|
||||
.phone-loop {
|
||||
color: var(--colorPrimary);
|
||||
|
||||
path {
|
||||
fill: currentColor;
|
||||
stroke: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
import React from "react"
|
||||
import * as antd from "antd"
|
||||
import NFCModel from "comty.js/models/nfc"
|
||||
|
||||
import AnimationPlayer from "components/AnimationPlayer"
|
||||
|
||||
import NFC_ERRORS from "../../errors"
|
||||
import StepsContext from "../../context"
|
||||
|
||||
export default (props) => {
|
||||
const [error, setError] = React.useState(null)
|
||||
const [loading, setLoading] = React.useState(false)
|
||||
const context = React.useContext(StepsContext)
|
||||
|
||||
const readTagRegister = async (error, data) => {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
|
||||
setError(NFC_ERRORS.NFC_READ_ERROR)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return false
|
||||
}
|
||||
|
||||
setError(null)
|
||||
setLoading(true)
|
||||
|
||||
console.log(data)
|
||||
|
||||
const registerResult = await NFCModel.getTagBySerial(data.serialNumber).catch((err) => {
|
||||
if (err.response.status === 404) {
|
||||
return false
|
||||
}
|
||||
|
||||
return {
|
||||
error: err,
|
||||
is_owner: false
|
||||
}
|
||||
})
|
||||
|
||||
console.log(registerResult)
|
||||
|
||||
setLoading(false)
|
||||
|
||||
if (!registerResult) {
|
||||
// this means that the tag is not registered, step to the next step
|
||||
context.setValue("serial", data.serialNumber)
|
||||
|
||||
unregisterScan()
|
||||
|
||||
return context.next()
|
||||
} else {
|
||||
if (registerResult.error) {
|
||||
return setError("Cannot check if the tag is registered. Please try again.")
|
||||
}
|
||||
|
||||
if (!registerResult.is_owner) {
|
||||
// this means that the tag is registered but not owned by the user
|
||||
return setError(NFC_ERRORS.NFC_NOT_OWNER)
|
||||
}
|
||||
|
||||
context.setValue("serial", data.serialNumber)
|
||||
context.setValue("alias", registerResult.alias)
|
||||
context.setValue("behavior", registerResult.behavior)
|
||||
|
||||
unregisterScan()
|
||||
|
||||
return context.next()
|
||||
}
|
||||
}
|
||||
|
||||
const unregisterScan = () => {
|
||||
app.cores.nfc.unsubscribe(readTagRegister)
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
app.cores.nfc.subscribe(readTagRegister)
|
||||
|
||||
return () => {
|
||||
unregisterScan()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <div className="tap-share-register_step centered">
|
||||
<AnimationPlayer
|
||||
src="https://storage.ragestudio.net/comty-static-assets/animations/nfc_tap.json"
|
||||
loop={true}
|
||||
className={[
|
||||
{
|
||||
["loading"]: loading,
|
||||
["error"]: error
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
{
|
||||
error && <antd.Alert
|
||||
type="error"
|
||||
message={error}
|
||||
/>
|
||||
}
|
||||
|
||||
<h1 style={{
|
||||
opacity: loading ? 0 : 1
|
||||
}}>
|
||||
Tap your tag to your phone
|
||||
</h1>
|
||||
|
||||
{
|
||||
loading && <antd.Spin />
|
||||
}
|
||||
</div>
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
import React from "react"
|
||||
import * as antd from "antd"
|
||||
import { Input } from "antd-mobile"
|
||||
import NFCModel from "comty.js/models/nfc"
|
||||
import { Icons } from "components/Icons"
|
||||
|
||||
import StepsContext from "../../context"
|
||||
|
||||
export default (props) => {
|
||||
const context = React.useContext(StepsContext)
|
||||
|
||||
if (!context.values.serial) {
|
||||
app.message.error("Serial not available.")
|
||||
|
||||
return <>
|
||||
Serial not available, please try again.
|
||||
</>
|
||||
}
|
||||
|
||||
const handleOnFinish = async (values) => {
|
||||
context.setValue("alias", values.alias)
|
||||
context.setValue("behavior", values.behavior)
|
||||
|
||||
const result = await NFCModel.registerTag(context.values.serial, {
|
||||
alias: values.alias,
|
||||
behavior: values.behavior
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
|
||||
app.message.error("Cannot register your tag. Please try again.")
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (!result) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!result.endpoint_url) {
|
||||
app.message.error("Cannot register your tag. Please try again.")
|
||||
return false
|
||||
}
|
||||
|
||||
if (props.onFinish) {
|
||||
app.message.success("All changes have been saved.")
|
||||
return props.onFinish(result)
|
||||
}
|
||||
|
||||
app.message.success("Your tag has been registered successfully.")
|
||||
|
||||
context.setValue("endpoint_url", result.endpoint_url)
|
||||
|
||||
return context.next()
|
||||
}
|
||||
|
||||
return <div className="tap-share-register_step">
|
||||
<h2>
|
||||
Tag Data
|
||||
</h2>
|
||||
|
||||
<antd.Form
|
||||
name="register_tag"
|
||||
onFinish={handleOnFinish}
|
||||
initialValues={{
|
||||
serial: context.values.serial,
|
||||
alias: context.values.alias,
|
||||
behavior: context.values.behavior,
|
||||
}}
|
||||
>
|
||||
<antd.Form.Item
|
||||
name="serial"
|
||||
label={<>
|
||||
<Icons.MdTag />
|
||||
Serial
|
||||
</>}
|
||||
>
|
||||
<Input
|
||||
disabled
|
||||
/>
|
||||
</antd.Form.Item>
|
||||
<antd.Form.Item
|
||||
name="alias"
|
||||
label={<>
|
||||
<Icons.Tag />
|
||||
Alias
|
||||
</>}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "Please input an alias."
|
||||
}
|
||||
]}
|
||||
>
|
||||
<antd.Input
|
||||
placeholder="Short name for your tag"
|
||||
/>
|
||||
</antd.Form.Item>
|
||||
|
||||
<antd.Form.Item
|
||||
label={<>
|
||||
<Icons.MdWebhook />
|
||||
Behavior
|
||||
</>}
|
||||
>
|
||||
<span className="description">
|
||||
What will happen when someone taps your tag?
|
||||
</span>
|
||||
|
||||
<div className="ant-form_with_selector">
|
||||
<antd.Form.Item
|
||||
name={["behavior", "type"]}
|
||||
noStyle
|
||||
size="large"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "Please select your tag behavior."
|
||||
}
|
||||
]}
|
||||
>
|
||||
<antd.Select
|
||||
placeholder="Options"
|
||||
size="large"
|
||||
>
|
||||
<antd.Select.Option
|
||||
value="url"
|
||||
>
|
||||
Custom URL
|
||||
</antd.Select.Option>
|
||||
|
||||
<antd.Select.Option
|
||||
value="profile"
|
||||
>
|
||||
Profile
|
||||
</antd.Select.Option>
|
||||
|
||||
<antd.Select.Option
|
||||
value="post"
|
||||
>
|
||||
Post
|
||||
</antd.Select.Option>
|
||||
</antd.Select>
|
||||
</antd.Form.Item>
|
||||
|
||||
<antd.Form.Item
|
||||
name={["behavior", "value"]}
|
||||
noStyle
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "Please select your behavior value."
|
||||
}
|
||||
]}
|
||||
>
|
||||
<antd.Input
|
||||
placeholder="value"
|
||||
size="large"
|
||||
autoCapitalize="off"
|
||||
autoCorrect="off"
|
||||
spellCheck="false"
|
||||
/>
|
||||
</antd.Form.Item>
|
||||
</div>
|
||||
</antd.Form.Item>
|
||||
|
||||
<antd.Form.Item
|
||||
colon={false}
|
||||
>
|
||||
<antd.Button
|
||||
block
|
||||
type="primary"
|
||||
size="large"
|
||||
htmlType="submit"
|
||||
>
|
||||
Save
|
||||
</antd.Button>
|
||||
</antd.Form.Item>
|
||||
</antd.Form>
|
||||
</div>
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import React from "react"
|
||||
|
||||
import AnimationPlayer from "components/AnimationPlayer"
|
||||
|
||||
import StepsContext from "../../context"
|
||||
|
||||
export default (props) => {
|
||||
const context = React.useContext(StepsContext)
|
||||
|
||||
React.useEffect(() => {
|
||||
setTimeout(() => {
|
||||
if (typeof context.onFinish === "function") {
|
||||
context.onFinish()
|
||||
}
|
||||
}, 2000)
|
||||
}, [])
|
||||
|
||||
return <div className="tap-share-register_step centered">
|
||||
<AnimationPlayer
|
||||
src="https://assets10.lottiefiles.com/packages/lf20_dyy9le6w.json"
|
||||
/>
|
||||
<h1>
|
||||
Your tag is ready to use!
|
||||
</h1>
|
||||
</div>
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
import React from "react"
|
||||
import * as antd from "antd"
|
||||
|
||||
import AnimationPlayer from "components/AnimationPlayer"
|
||||
|
||||
import NFC_ERRORS from "../../errors"
|
||||
import StepsContext from "../../context"
|
||||
|
||||
export default (props) => {
|
||||
const context = React.useContext(StepsContext)
|
||||
|
||||
const [loading, setLoading] = React.useState(false)
|
||||
const [error, setError] = React.useState(null)
|
||||
|
||||
const abortController = React.useRef(new AbortController())
|
||||
|
||||
const handleWritter = async (error, tag) => {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
|
||||
setError(NFC_ERRORS.NFC_READ_ERROR)
|
||||
return false
|
||||
}
|
||||
|
||||
const nfcInstance = app.cores.nfc.instance()
|
||||
|
||||
if (!nfcInstance) {
|
||||
setError(NFC_ERRORS.NFC_NOT_AVAILABLE)
|
||||
return false
|
||||
}
|
||||
|
||||
setError(null)
|
||||
setLoading(true)
|
||||
|
||||
if (tag.serialNumber !== context.values.serial) {
|
||||
setError(NFC_ERRORS.NFC_NOT_MATCH)
|
||||
|
||||
setLoading(false)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
nfcInstance.write({
|
||||
records: [{
|
||||
recordType: "url",
|
||||
data: context.values.endpoint_url
|
||||
}]
|
||||
}, {
|
||||
signal: abortController.current.signal
|
||||
}).then(() => {
|
||||
app.message.success("Tag written successfully.")
|
||||
setLoading(false)
|
||||
|
||||
return context.next()
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
|
||||
setError(NFC_ERRORS.NFC_WRITE_ERROR)
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
app.cores.nfc.subscribe(handleWritter)
|
||||
|
||||
return () => {
|
||||
app.cores.nfc.unsubscribe(handleWritter)
|
||||
abortController.current.abort("finished")
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <div className="tap-share-register_step centered">
|
||||
<h1>
|
||||
Your tag is ready to write!
|
||||
</h1>
|
||||
|
||||
<p>
|
||||
Tap your tag to your phone to write the data.
|
||||
<br />
|
||||
This is only necessary the first time you use your tag.
|
||||
</p>
|
||||
|
||||
|
||||
<h2 style={{
|
||||
opacity: loading ? 1 : 0,
|
||||
color: loading ? "red" : "inherit"
|
||||
}}>
|
||||
Please do not pick up the tag
|
||||
</h2>
|
||||
|
||||
<AnimationPlayer
|
||||
src="https://storage.ragestudio.net/comty-static-assets/animations/nfc_tap.json"
|
||||
className={[
|
||||
{
|
||||
["loading"]: loading,
|
||||
["error"]: error
|
||||
}
|
||||
]}
|
||||
loop
|
||||
/>
|
||||
|
||||
{
|
||||
error && <antd.Alert
|
||||
type="error"
|
||||
message={error}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user