Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bring staging up to date with main #1565

Merged
merged 1 commit into from
Dec 9, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -138,7 +138,8 @@ class MainActivity : AppCompatActivity() {
<a href="https://prosopo.io">Link</a>
<input type="text" name="email" placeholder="Email" />
<input type="password" name="password" placeholder="Password" />
<div class="procaptcha" data-sitekey="5FWCbfR7pH9QiZqLgmm5Rw4QbFwyU5EaMqUV4G6xrvrTZDtC"></div>
<p>image</p>
<div class="procaptcha" data-sitekey="5FWCbfR7pH9QiZqLgmm5Rw4QbFwyU5EaMqUV4G6xrvrTZDtC" data-captcha-type="image"></div>
<br />
<input type="submit" value="Submit" />
</form>
2 changes: 1 addition & 1 deletion demos/cypress-shared/cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -84,7 +84,7 @@ function clickIAmHuman(): Cypress.Chainable<Captcha[]> {
}

function captchaImages(): Cypress.Chainable<JQuery<HTMLElement>> {
return getWidgetElement("p").then(($p) => {
return getWidgetElement(".prosopo-modalInner p").then(($p) => {
const $pWithText = $p.filter((index, el) => {
return Cypress.$(el).text().includes("all containing");
});
4 changes: 2 additions & 2 deletions demos/ios-webview/procaptcha.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
@@ -277,7 +277,7 @@
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 13.3;
@@ -315,7 +315,7 @@
"INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 13.3;
8 changes: 5 additions & 3 deletions demos/ios-webview/procaptcha/ContentView.swift
Original file line number Diff line number Diff line change
@@ -46,10 +46,11 @@ struct WebView: UIViewRepresentable {

let webView = WKWebView(frame: .zero, configuration: webViewConfiguration)
webView.navigationDelegate = context.coordinator
//webView.isInspectable = true
webView.isInspectable = true

//DispatchQueue.main.asyncAfter(deadline: .now() + 30) {
webView.loadHTMLString(htmlString, baseURL: nil)
// baseURL must be set for localstorage to work. If localstorage is not required, it can be set to nil. Localstorage errors appear like "SecurityError: The operation is insecure."
webView.loadHTMLString(htmlString, baseURL: URL(string: "https://prosopo.io"))
//}

return webView
@@ -99,7 +100,8 @@ struct ContentView: View {
<form action="" method="POST">
<input type="text" name="email" placeholder="Email" />
<input type="password" name="password" placeholder="Password" />
<div class="procaptcha" data-sitekey="5FWCbfR7pH9QiZqLgmm5Rw4QbFwyU5EaMqUV4G6xrvrTZDtC"></div>
<p>image</p>
<div class="procaptcha" data-sitekey="5FWCbfR7pH9QiZqLgmm5Rw4QbFwyU5EaMqUV4G6xrvrTZDtC" data-captcha-type="image"></div>
<br />
<input type="submit" value="Submit" />
</form>
33 changes: 21 additions & 12 deletions packages/procaptcha-react/src/components/CaptchaComponent.tsx
Original file line number Diff line number Diff line change
@@ -50,6 +50,8 @@ const CaptchaComponent = ({
() => (themeColor === "light" ? lightTheme : darkTheme),
[themeColor],
);
const fullSpacing = `${theme.spacing.unit}px`;
const halfSpacing = `${theme.spacing.half}px`;

return (
<Suspense fallback={<div>Loading...</div>}>
@@ -66,8 +68,10 @@ const CaptchaComponent = ({
border: "1px solid #dddddd",
boxShadow: "rgba(255, 255, 255, 0.2) 0px 0px 4px",
borderRadius: "4px",
padding: `${theme.spacing.unit}px`,
backgroundColor: theme.palette.background.default,
userSelect: "none",
touchAction: "none",
overscrollBehavior: "none",
}}
>
<div
@@ -89,6 +93,9 @@ const CaptchaComponent = ({
style={{
backgroundColor: theme.palette.primary.main,
width: "100%",
marginTop: fullSpacing,
marginLeft: fullSpacing,
marginRight: fullSpacing,
}}
>
<div
@@ -122,7 +129,13 @@ const CaptchaComponent = ({
</div>
</div>
</div>
<div {...addDataAttr({ dev: { cy: `captcha-${index}` } })}>
<div
{...addDataAttr({ dev: { cy: `captcha-${index}` } })}
style={{
paddingRight: halfSpacing,
paddingLeft: halfSpacing,
}}
>
{captcha && (
<CaptchaWidget
challenge={captcha}
@@ -132,29 +145,25 @@ const CaptchaComponent = ({
/>
)}
</div>
<div
{/* <div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "100%",
paddingTop: fullSpacing,
}}
{...addDataAttr({ dev: { cy: "dots-captcha" } })}
/>
<div
style={{
padding: `0 ${theme.spacing}px`,
display: "flex",
width: "100%",
}}
/>
/> */}
<div
style={{
padding: `0 ${theme.spacing}px`,
display: "flex",
alignItems: "center",
justifyContent: "space-between",
lineHeight: 1.75,
padding: fullSpacing,
paddingBottom: halfSpacing,
paddingTop: halfSpacing,
}}
>
<div
153 changes: 52 additions & 101 deletions packages/procaptcha-react/src/components/CaptchaWidget.tsx
Original file line number Diff line number Diff line change
@@ -46,153 +46,104 @@ export const CaptchaWidget = ({
[themeColor],
);

const isTouchDevice = "ontouchstart" in window;

// Assumes a 3x3 grid, could be made more generic
const fullSpacing = `${theme.spacing.unit}px`;
const halfSpacing = `${theme.spacing.half}px`;
// biome-ignore lint/suspicious/noExplicitAny: TODO fix
const paddingForImageColumns: { [key: number]: any } = {
0: {
paddingLeft: 0,
paddingRight: halfSpacing,
paddingTop: halfSpacing,
paddingBottom: halfSpacing,
},
1: {
paddingLeft: halfSpacing,
paddingRight: halfSpacing,
paddingTop: halfSpacing,
paddingBottom: halfSpacing,
},
2: {
paddingLeft: halfSpacing,
paddingRight: 0,
paddingTop: halfSpacing,
paddingBottom: halfSpacing,
},
};

// biome-ignore lint/suspicious/noExplicitAny: TODO fix
const paddingForImageRows: { [key: number]: any } = {
0: { paddingTop: fullSpacing },
2: { paddingBottom: fullSpacing },
};

return (
<div
style={{
paddingRight: 0.5,
paddingBottom: 0.5,
// expand to full height / width of parent
width: "100%",
height: "100%",
// display children in flex, spreading them evenly and wrapping when row length exceeded
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
justifyContent: "space-between",
paddingBottom: halfSpacing,
paddingRight: halfSpacing,
}}
>
{items.map((item, index) => {
const hash = getHash(item);
const imageStyle: Properties<string | number, string> = {
...paddingForImageColumns[index % 3],
...paddingForImageRows[Math.floor(index / 3)],
// enable the items in the grid to grow in width to use up excess space
flexGrow: 1,
// make the width of each item 1/3rd of the width overall, i.e. 3 columns
flexBasis: "33.3333%",
// include the padding / margin / border in the width
boxSizing: "border-box",
paddingLeft: halfSpacing,
paddingTop: halfSpacing,
};
return (
<div style={imageStyle} key={item.hash}>
<div
style={{
position: "relative",
cursor: "pointer",
height: "100%",
width: "100%",
border: 1,
padding: 0,
margin: 0,
borderStyle: "solid",
borderColor: theme.palette.grey[300],
}}
onClick={isTouchDevice ? undefined : () => onClick(hash)}
onTouchStart={isTouchDevice ? () => onClick(hash) : undefined}
onClick={() => onClick(hash)}
>
<div>
<img
style={{
width: "100%", // image should be full width / height of the item
backgroundColor: theme.palette.grey[300], // colour of the bands when letterboxing and image
opacity:
solution.includes(hash) && isTouchDevice ? "50%" : "100%", // iphone workaround
display: "block", // removes whitespace below imgs
objectFit: "contain", // contain the entire image in the img tag
aspectRatio: "1/1", // force AR to be 1, letterboxing images with different aspect ratios
height: "auto", // make the img tag responsive to its container
}}
src={item.data}
// biome-ignore lint/a11y/noRedundantAlt: has to contain image
alt={`Captcha image ${index + 1}`}
/>
</div>

<img
style={{
width: "100%", // image should be full width / height of the item
backgroundColor: theme.palette.grey[300], // colour of the bands when letterboxing and image
display: "block", // removes whitespace below imgs
objectFit: "contain", // contain the entire image in the img tag
aspectRatio: "1/1", // force AR to be 1, letterboxing images with different aspect ratios
height: "auto", // make the img tag responsive to its container
}}
src={item.data}
// biome-ignore lint/a11y/noRedundantAlt: has to contain image
alt={`Captcha image ${index + 1}`}
/>
<div
style={{
// relative to where the element _should_ be positioned
position: "relative",
// make the overlay the full height/width of an item
width: "100%",
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0,
height: "100%",
// shift it up 100% to overlay the item element
top: "-100%",
width: "100%",
// display overlays in center
display: "flex",
alignItems: "center",
justifyContent: "center",
// make bg half opacity, i.e. shadowing the item's img
backgroundColor: "rgba(0,0,0,0.5)",
visibility: solution.includes(hash) ? "visible" : "hidden",
// transition on opacity upon (de)selection
transition: "opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms",
opacity: 1,
}}
>
<div
<svg
style={{
// make the overlay absolute positioned compare to its container
position: "absolute",
// spread across 100% width/height of the item box
top: 0,
left: 0,
bottom: 0,
right: 0,
height: "100%",
width: "100%",
// display overlays in center
display: "flex",
alignItems: "center",
justifyContent: "center",
// make bg half opacity, i.e. shadowing the item's img
backgroundColor: "rgba(0,0,0,0.5)",
backgroundColor: "transparent",
// img must be displayed as block otherwise gets a bottom whitespace border
display: "block",
// how big the overlay icon is
width: "35%",
height: "35%",
transition: "fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms",
userSelect: "none",
fill: "currentcolor",
}}
focusable="false"
color="#fff"
aria-hidden="true"
viewBox="0 0 24 24"
data-testid="CheckIcon"
aria-label="Check icon"
>
<svg
style={{
backgroundColor: "transparent",
// img must be displayed as block otherwise gets a bottom whitespace border
display: "block",
// how big the overlay icon is
width: "35%",
height: "35%",
transition: "fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms",
userSelect: "none",
fill: "currentcolor",
}}
focusable="false"
color="#fff"
aria-hidden="true"
viewBox="0 0 24 24"
data-testid="CheckIcon"
aria-label="Check icon"
>
<path d="M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" />
</svg>
</div>
<path d="M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" />
</svg>
</div>
</div>
</div>