diff --git a/config/dependencies.yaml b/config/dependencies.yaml index 04ee761ae09..d450026d6be 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -132,6 +132,7 @@ react: luxon: "^3.5.0" nuqs: "^1.17.6" react-markdown: "^9.0.1" + tlds: "^1.255.0" use-local-storage-state: "19.1.0" use-session-storage-state: "^19.0.0" peer: diff --git a/config/exceptions.yaml b/config/exceptions.yaml index 0e6738f33da..a81fbd25e02 100644 --- a/config/exceptions.yaml +++ b/config/exceptions.yaml @@ -109,6 +109,7 @@ packageJson: # Hooks "./hooks/useAccount": "./hooks/useAccount/index.mjs" "./hooks/useBackend": "./hooks/useBackend/index.mjs" + "./hooks/useControl": "./hooks/useControl/index.mjs" "./hooks/useSelection": "./hooks/useSelection/index.mjs" # Lib "./lib/RestClient": "./lib/RestClient/index.mjs" diff --git a/package-lock.json b/package-lock.json index e5b5ba477b1..415e54cd436 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61565,6 +61565,7 @@ } }, "packages/i18n": { + "name": "@freesewing/i18n", "version": "3.3.0-rc.1", "license": "MIT", "devDependencies": {}, @@ -62042,6 +62043,9 @@ "name": "@freesewing/utils", "version": "3.3.0-rc.1", "license": "MIT", + "dependencies": { + "tlds": "^1.255.0" + }, "devDependencies": {}, "engines": { "node": ">= 20" @@ -62051,6 +62055,14 @@ "url": "https://freesewing.org/patrons/join" } }, + "packages/utils/node_modules/tlds": { + "version": "1.255.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.255.0.tgz", + "integrity": "sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw==", + "bin": { + "tlds": "bin.js" + } + }, "plugins/core-plugins": { "name": "@freesewing/core-plugins", "version": "3.3.0-rc.1", diff --git a/packages/config/src/control.mjs b/packages/config/src/control.mjs index 2dc45ca7b77..1898719b7d9 100644 --- a/packages/config/src/control.mjs +++ b/packages/config/src/control.mjs @@ -26,7 +26,6 @@ const account = { bookmarks: 2, sets: 1, patterns: 1, - apikeys: 4, }, info: { username: 2, @@ -145,3 +144,26 @@ export const control = { views: editor.views, }, } + +export const controlDesc = { + 1: { + title: `Keep it as simple as possible`, + desc: `Hides all but the most crucial features.`, + }, + 2: { + title: `Keep it simple, but not too simple`, + desc: `Hides the majority of features.`, + }, + 3: { + title: `Balance simplicity with power`, + desc: `Reveals the majority of features, but not all.`, + }, + 4: { + title: `Give me all powers, but keep me safe`, + desc: `Reveals all features, keeps handrails and safety checks`, + }, + 5: { + title: `Get out of my way`, + desc: `Reveals all features, removes handrails and safety checks`, + }, +} diff --git a/packages/config/src/index.mjs b/packages/config/src/index.mjs index cb7925d0d99..271db7e1f39 100644 --- a/packages/config/src/index.mjs +++ b/packages/config/src/index.mjs @@ -1,5 +1,5 @@ import { cloudflare } from './cloudflare.mjs' -import { control } from './control.mjs' +import { control, controlDesc } from './control.mjs' import { logoPath } from './logo.mjs' import { measurements, degreeMeasurements, isDegreeMeasurement } from './measurements.mjs' import { sewingTechniques } from './sewing.mjs' @@ -14,6 +14,7 @@ export { apikeyLevels, cloudflare, control, + controlDesc, logoPath, measurements, degreeMeasurements, diff --git a/packages/react/components/Account/Avatar.mjs b/packages/react/components/Account/Avatar.mjs new file mode 100644 index 00000000000..c590a167d49 --- /dev/null +++ b/packages/react/components/Account/Avatar.mjs @@ -0,0 +1,103 @@ +// Dependencies +import { welcomeSteps } from './shared.mjs' +import { cloudflareImageUrl } from '@freesewing/utils' + +// Context +import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus' + +// Hooks +import React, { useState, useContext } from 'react' +import { useAccount } from '@freesewing/react/hooks/useAccount' +import { useBackend } from '@freesewing/react/hooks/useBackend' + +// Components +import { Link as WebLink } from '@freesewing/react/components/Link' +import { SaveIcon } from '@freesewing/react/components/Icon' +import { PassiveImageInput } from '@freesewing/react/components/Input' + +/* + * Component for the account/bio page + * + * @params {object} props - All React props + * @params {bool} props.welcome - Set to true to use this component on the welcome page + * @params {function} props.Link - A framework specific Link component for client-side routing + */ +export const Avatar = ({ welcome = false, Link = false }) => { + if (!Link) Link = WebLink + + // Hooks + const { account, setAccount } = useAccount() + const backend = useBackend() + + // State + const [img, setImg] = useState('') + + // Context + const { setLoadingStatus } = useContext(LoadingStatusContext) + + // Save handler + const save = async () => { + setLoadingStatus([true, 'Uploading image']) + const [status, body] = await backend.updateAccount({ img }) + if (status === 200 && body.result === 'success') { + setAccount(body.account) + setLoadingStatus([true, 'Avatar saved', true, true]) + } else setLoadingStatus([true, 'Failed to save avatar image. Please report this', true, false]) + } + + // Next page in welcome flow + const nextHref = '/docs/about/guide' + + return ( +
+ +
+ > + )} ++ +
+ + {welcome ? ( + <> +