Merged
Show file tree
Hide file tree
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
11 changes: 11 additions & 0 deletions .changeset/expo-native-logo-max-height.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
---
'@clerk/expo': patch
---

Add a `logoMaxHeight` prop to `AuthView`, allowing Expo apps to control the maximum height of the managed logo on Android and iOS.

Usage:

```tsx
<AuthView logoMaxHeight={64} />
```
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,9 +5,12 @@ import android.util.Log
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import com.clerk.api.Clerk
import com.clerk.api.ui.ClerkDesign
import com.clerk.api.ui.ClerkTheme
import com.clerk.ui.auth.AuthMode
import com.clerk.ui.auth.AuthView
import expo.modules.kotlin.AppContext
Expand All@@ -25,6 +28,7 @@ private fun debugLog(tag: String, message: String) {

class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkComposeNativeViewHost(context, appContext) {
var isDismissible: Boolean = true
var logoMaxHeight: Float? = null
var mode: String? = null

private val onAuthEvent by EventDispatcher()
Expand DownExpand Up@@ -60,7 +64,7 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo

AuthView(
modifier = Modifier.fillMaxSize(),
clerkTheme = Clerk.customTheme,
clerkTheme = authTheme(),
mode = authMode(mode),
isDismissible = isDismissible,
onDismiss = ::sendDismissEvent,
Expand All@@ -70,6 +74,13 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo
)
}

private fun authTheme(): ClerkTheme? {
val maxHeight = logoMaxHeight ?: return Clerk.customTheme
val theme = Clerk.customTheme ?: ClerkTheme()
val design = theme.design ?: ClerkDesign()
return theme.copy(design = design.copy(logoMaxHeight = maxHeight.dp))
}

private fun sendEvent(type: String) {
onAuthEvent(mapOf("type" to type))
}
Expand DownExpand Up@@ -102,6 +113,10 @@ class ClerkAuthViewModule : Module() {
view.isDismissible = isDismissible
}

Prop("logoMaxHeight") { view: ClerkAuthNativeView, logoMaxHeight: Float? ->
view.logoMaxHeight = logoMaxHeight
}

OnViewDidUpdateProps { view: ClerkAuthNativeView ->
view.setupView()
}
Expand Down
12 changes: 12 additions & 0 deletions packages/expo/ios/ClerkAuthNativeView.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import UIKit
public class ClerkAuthNativeView: ClerkNativeViewHost {
private var currentMode: String = "signInOrUp"
private var currentDismissible: Bool = true
private var currentLogoMaxHeight: CGFloat?
private var didSendDismiss = false

let onAuthEvent = EventDispatcher()
Expand All@@ -22,6 +23,12 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
setNeedsHostedViewUpdate()
}

func setLogoMaxHeight(_ logoMaxHeight: CGFloat?) {
guard logoMaxHeight != currentLogoMaxHeight else { return }
currentLogoMaxHeight = logoMaxHeight
setNeedsHostedViewUpdate()
}

private func sendAuthEvent(type: ClerkNativeViewEvent) {
onAuthEvent(["type": type.rawValue])
}
Expand All@@ -45,6 +52,7 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
return ClerkNativeBridge.shared.makeAuthViewController(
mode: currentMode,
dismissible: currentDismissible,
logoMaxHeight: currentLogoMaxHeight,
onEvent: { [weak self] event, _ in
if event == .dismissed {
self?.sendDismissIfNeeded()
Expand All@@ -68,6 +76,10 @@ public class ClerkAuthViewModule: Module {
Prop("isDismissible") { (view: ClerkAuthNativeView, isDismissible: Bool?) in
view.setDismissible(isDismissible)
}

Prop("logoMaxHeight") { (view: ClerkAuthNativeView, logoMaxHeight: CGFloat?) in
view.setLogoMaxHeight(logoMaxHeight)
}
}
}
}
15 changes: 12 additions & 3 deletions packages/expo/ios/ClerkNativeBridge.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -229,6 +229,7 @@ final class ClerkNativeBridge {
func makeAuthViewController(
mode: String,
dismissible: Bool,
logoMaxHeight: CGFloat?,
onEvent: @escaping (ClerkNativeViewEvent, [String: Any]) -> Void
) -> UIViewController? {
guard Self.clerkConfigured else { return nil }
Expand All@@ -238,7 +239,8 @@ final class ClerkNativeBridge {
mode: Self.authMode(from: mode),
dismissible: dismissible,
lightTheme: lightTheme,
darkTheme: darkTheme
darkTheme: darkTheme,
logoMaxHeight: logoMaxHeight
),
onDismiss: dismissible ? { onEvent(.dismissed, [:]) } : nil
)
Expand DownExpand Up@@ -465,20 +467,27 @@ struct ClerkInlineAuthWrapperView: View {
let dismissible: Bool
let lightTheme: ClerkTheme?
let darkTheme: ClerkTheme?
let logoMaxHeight: CGFloat?

@Environment(\.colorScheme) private var colorScheme

private var themedAuthView: some View {
@ViewBuilder private var themedAuthView: some View {
let view = AuthView(mode: mode, isDismissible: dismissible)
.environment(Clerk.shared)
let theme = colorScheme == .dark ? (darkTheme ?? lightTheme) : lightTheme
return Group {
let themedView = Group {
if let theme {
view.environment(\.clerkTheme, theme)
} else {
view
}
}

if let logoMaxHeight {
themedView.clerkAppIcon(maxHeight: logoMaxHeight)
} else {
themedView
}
}

var body: some View {
Expand Down
8 changes: 7 additions & 1 deletion packages/expo/src/native/AuthView.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,12 @@ type AuthNativeEvent = NativeSyntheticEvent<Readonly<{ type: string }>>;
*
* @see {@link https://clerk.com/docs/components/authentication/sign-in} Clerk Sign-In Documentation
*/
export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss }: AuthViewProps): ReactElement {
export function AuthView({
mode = 'signInOrUp',
isDismissible = true,
logoMaxHeight,
onDismiss,
}: AuthViewProps): ReactElement {
Comment on lines +40 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
# Inspect the relevant file and nearby references
git ls-files 'packages/expo/src/native/AuthView.tsx''packages/expo/**'| sed -n '1,120p'echoecho"== AuthView.tsx =="
cat -n packages/expo/src/native/AuthView.tsx | sed -n '1,220p'echoecho"== Search for logoMaxHeight =="
rg -n "logoMaxHeight" packages/expo -S
echoecho"== Search for AuthViewProps =="
rg -n "type AuthViewProps|interface AuthViewProps|AuthViewProps" packages/expo -S

Repository: clerk/javascript

Length of output: 11533


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== AuthView props =="
cat -n packages/expo/src/native/AuthView.types.ts | sed -n '1,180p'echoecho"== iOS Auth view =="
cat -n packages/expo/ios/ClerkAuthNativeView.swift | sed -n '1,180p'echoecho"== Android Auth view =="
cat -n packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt | sed -n '1,220p'echoecho"== Native spec =="
cat -n packages/expo/src/specs/NativeClerkAuthView.ts | sed -n '1,120p'echo
cat -n packages/expo/src/specs/NativeClerkAuthView.android.ts | sed -n '1,120p'

Repository: clerk/javascript

Length of output: 11312


Validate logoMaxHeight before forwarding it to native.

It’s passed through unchanged, and both native implementations use it directly. Reject or coerce non-finite or negative values before crossing the bridge, and add coverage for invalid inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/src/native/AuthView.tsx` around lines 40 - 45, Validate
logoMaxHeight in AuthView before passing it to either native implementation:
reject or coerce values that are non-finite or negative, while preserving valid
values. Add coverage for non-finite and negative inputs, confirming invalid
values never cross the native bridge.

Source: Coding guidelines

const handleAuthEvent = useCallback(
(event: AuthNativeEvent) => {
if (event.nativeEvent.type === 'dismissed') {
Expand All@@ -64,6 +69,7 @@ export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss
style={{ flex: 1 }}
mode={mode}
isDismissible={isDismissible}
logoMaxHeight={logoMaxHeight}
onAuthEvent={handleAuthEvent}
/>
);
Expand Down
7 changes: 7 additions & 0 deletions packages/expo/src/native/AuthView.types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,13 @@ export interface AuthViewProps {
*/
isDismissible?: boolean;

/**
* Maximum height of the Clerk application logo, in density-independent pixels.
*
* @default 44
*/
logoMaxHeight?: number;

/**
* Called when the native authentication view requests dismissal.
*
Expand Down
12 changes: 11 additions & 1 deletion packages/expo/src/native/__tests__/AuthView.test.tsx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test, vi } from 'vitest';
import { beforeEach, describe, expect, test, vi } from 'vitest';

import { AuthView } from '../AuthView';

Expand DownExpand Up@@ -30,6 +30,16 @@ vi.mock('react-native', () => {
});

describe('AuthView', () => {
beforeEach(() => {
mocks.NativeClerkAuthView.mockClear();
});

test('passes logoMaxHeight to the native auth view', () => {
render(<AuthView logoMaxHeight={64} />);

expect(mocks.NativeClerkAuthView.mock.calls[0]?.[0]).toMatchObject({ logoMaxHeight: 64 });
});

test('calls onDismiss when the native auth view emits dismissed', () => {
const onDismiss = vi.fn();

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.android.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
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
11 changes: 11 additions & 0 deletions .changeset/expo-native-logo-max-height.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
---
'@clerk/expo': patch
---

Add a `logoMaxHeight` prop to `AuthView`, allowing Expo apps to control the maximum height of the managed logo on Android and iOS.

Usage:

```tsx
<AuthView logoMaxHeight={64} />
```
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,9 +5,12 @@ import android.util.Log
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import com.clerk.api.Clerk
import com.clerk.api.ui.ClerkDesign
import com.clerk.api.ui.ClerkTheme
import com.clerk.ui.auth.AuthMode
import com.clerk.ui.auth.AuthView
import expo.modules.kotlin.AppContext
Expand All@@ -25,6 +28,7 @@ private fun debugLog(tag: String, message: String) {

class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkComposeNativeViewHost(context, appContext) {
var isDismissible: Boolean = true
var logoMaxHeight: Float? = null
var mode: String? = null

private val onAuthEvent by EventDispatcher()
Expand DownExpand Up@@ -60,7 +64,7 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo

AuthView(
modifier = Modifier.fillMaxSize(),
clerkTheme = Clerk.customTheme,
clerkTheme = authTheme(),
mode = authMode(mode),
isDismissible = isDismissible,
onDismiss = ::sendDismissEvent,
Expand All@@ -70,6 +74,13 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo
)
}

private fun authTheme(): ClerkTheme? {
val maxHeight = logoMaxHeight ?: return Clerk.customTheme
val theme = Clerk.customTheme ?: ClerkTheme()
val design = theme.design ?: ClerkDesign()
return theme.copy(design = design.copy(logoMaxHeight = maxHeight.dp))
}

private fun sendEvent(type: String) {
onAuthEvent(mapOf("type" to type))
}
Expand DownExpand Up@@ -102,6 +113,10 @@ class ClerkAuthViewModule : Module() {
view.isDismissible = isDismissible
}

Prop("logoMaxHeight") { view: ClerkAuthNativeView, logoMaxHeight: Float? ->
view.logoMaxHeight = logoMaxHeight
}

OnViewDidUpdateProps { view: ClerkAuthNativeView ->
view.setupView()
}
Expand Down
12 changes: 12 additions & 0 deletions packages/expo/ios/ClerkAuthNativeView.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import UIKit
public class ClerkAuthNativeView: ClerkNativeViewHost {
private var currentMode: String = "signInOrUp"
private var currentDismissible: Bool = true
private var currentLogoMaxHeight: CGFloat?
private var didSendDismiss = false

let onAuthEvent = EventDispatcher()
Expand All@@ -22,6 +23,12 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
setNeedsHostedViewUpdate()
}

func setLogoMaxHeight(_ logoMaxHeight: CGFloat?) {
guard logoMaxHeight != currentLogoMaxHeight else { return }
currentLogoMaxHeight = logoMaxHeight
setNeedsHostedViewUpdate()
}

private func sendAuthEvent(type: ClerkNativeViewEvent) {
onAuthEvent(["type": type.rawValue])
}
Expand All@@ -45,6 +52,7 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
return ClerkNativeBridge.shared.makeAuthViewController(
mode: currentMode,
dismissible: currentDismissible,
logoMaxHeight: currentLogoMaxHeight,
onEvent: { [weak self] event, _ in
if event == .dismissed {
self?.sendDismissIfNeeded()
Expand All@@ -68,6 +76,10 @@ public class ClerkAuthViewModule: Module {
Prop("isDismissible") { (view: ClerkAuthNativeView, isDismissible: Bool?) in
view.setDismissible(isDismissible)
}

Prop("logoMaxHeight") { (view: ClerkAuthNativeView, logoMaxHeight: CGFloat?) in
view.setLogoMaxHeight(logoMaxHeight)
}
}
}
}
15 changes: 12 additions & 3 deletions packages/expo/ios/ClerkNativeBridge.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -229,6 +229,7 @@ final class ClerkNativeBridge {
func makeAuthViewController(
mode: String,
dismissible: Bool,
logoMaxHeight: CGFloat?,
onEvent: @escaping (ClerkNativeViewEvent, [String: Any]) -> Void
) -> UIViewController? {
guard Self.clerkConfigured else { return nil }
Expand All@@ -238,7 +239,8 @@ final class ClerkNativeBridge {
mode: Self.authMode(from: mode),
dismissible: dismissible,
lightTheme: lightTheme,
darkTheme: darkTheme
darkTheme: darkTheme,
logoMaxHeight: logoMaxHeight
),
onDismiss: dismissible ? { onEvent(.dismissed, [:]) } : nil
)
Expand DownExpand Up@@ -465,20 +467,27 @@ struct ClerkInlineAuthWrapperView: View {
let dismissible: Bool
let lightTheme: ClerkTheme?
let darkTheme: ClerkTheme?
let logoMaxHeight: CGFloat?

@Environment(\.colorScheme) private var colorScheme

private var themedAuthView: some View {
@ViewBuilder private var themedAuthView: some View {
let view = AuthView(mode: mode, isDismissible: dismissible)
.environment(Clerk.shared)
let theme = colorScheme == .dark ? (darkTheme ?? lightTheme) : lightTheme
return Group {
let themedView = Group {
if let theme {
view.environment(\.clerkTheme, theme)
} else {
view
}
}

if let logoMaxHeight {
themedView.clerkAppIcon(maxHeight: logoMaxHeight)
} else {
themedView
}
}

var body: some View {
Expand Down
8 changes: 7 additions & 1 deletion packages/expo/src/native/AuthView.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,12 @@ type AuthNativeEvent = NativeSyntheticEvent<Readonly<{ type: string }>>;
*
* @see {@link https://clerk.com/docs/components/authentication/sign-in} Clerk Sign-In Documentation
*/
export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss }: AuthViewProps): ReactElement {
export function AuthView({
mode = 'signInOrUp',
isDismissible = true,
logoMaxHeight,
onDismiss,
}: AuthViewProps): ReactElement {
Comment on lines +40 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
# Inspect the relevant file and nearby references
git ls-files 'packages/expo/src/native/AuthView.tsx''packages/expo/**'| sed -n '1,120p'echoecho"== AuthView.tsx =="
cat -n packages/expo/src/native/AuthView.tsx | sed -n '1,220p'echoecho"== Search for logoMaxHeight =="
rg -n "logoMaxHeight" packages/expo -S
echoecho"== Search for AuthViewProps =="
rg -n "type AuthViewProps|interface AuthViewProps|AuthViewProps" packages/expo -S

Repository: clerk/javascript

Length of output: 11533


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== AuthView props =="
cat -n packages/expo/src/native/AuthView.types.ts | sed -n '1,180p'echoecho"== iOS Auth view =="
cat -n packages/expo/ios/ClerkAuthNativeView.swift | sed -n '1,180p'echoecho"== Android Auth view =="
cat -n packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt | sed -n '1,220p'echoecho"== Native spec =="
cat -n packages/expo/src/specs/NativeClerkAuthView.ts | sed -n '1,120p'echo
cat -n packages/expo/src/specs/NativeClerkAuthView.android.ts | sed -n '1,120p'

Repository: clerk/javascript

Length of output: 11312


Validate logoMaxHeight before forwarding it to native.

It’s passed through unchanged, and both native implementations use it directly. Reject or coerce non-finite or negative values before crossing the bridge, and add coverage for invalid inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/src/native/AuthView.tsx` around lines 40 - 45, Validate
logoMaxHeight in AuthView before passing it to either native implementation:
reject or coerce values that are non-finite or negative, while preserving valid
values. Add coverage for non-finite and negative inputs, confirming invalid
values never cross the native bridge.

Source: Coding guidelines

const handleAuthEvent = useCallback(
(event: AuthNativeEvent) => {
if (event.nativeEvent.type === 'dismissed') {
Expand All@@ -64,6 +69,7 @@ export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss
style={{ flex: 1 }}
mode={mode}
isDismissible={isDismissible}
logoMaxHeight={logoMaxHeight}
onAuthEvent={handleAuthEvent}
/>
);
Expand Down
7 changes: 7 additions & 0 deletions packages/expo/src/native/AuthView.types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,13 @@ export interface AuthViewProps {
*/
isDismissible?: boolean;

/**
* Maximum height of the Clerk application logo, in density-independent pixels.
*
* @default 44
*/
logoMaxHeight?: number;

/**
* Called when the native authentication view requests dismissal.
*
Expand Down
12 changes: 11 additions & 1 deletion packages/expo/src/native/__tests__/AuthView.test.tsx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test, vi } from 'vitest';
import { beforeEach, describe, expect, test, vi } from 'vitest';

import { AuthView } from '../AuthView';

Expand DownExpand Up@@ -30,6 +30,16 @@ vi.mock('react-native', () => {
});

describe('AuthView', () => {
beforeEach(() => {
mocks.NativeClerkAuthView.mockClear();
});

test('passes logoMaxHeight to the native auth view', () => {
render(<AuthView logoMaxHeight={64} />);

expect(mocks.NativeClerkAuthView.mock.calls[0]?.[0]).toMatchObject({ logoMaxHeight: 64 });
});

test('calls onDismiss when the native auth view emits dismissed', () => {
const onDismiss = vi.fn();

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.android.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
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
11 changes: 11 additions & 0 deletions .changeset/expo-native-logo-max-height.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
---
'@clerk/expo': patch
---

Add a `logoMaxHeight` prop to `AuthView`, allowing Expo apps to control the maximum height of the managed logo on Android and iOS.

Usage:

```tsx
<AuthView logoMaxHeight={64} />
```
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,9 +5,12 @@ import android.util.Log
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import com.clerk.api.Clerk
import com.clerk.api.ui.ClerkDesign
import com.clerk.api.ui.ClerkTheme
import com.clerk.ui.auth.AuthMode
import com.clerk.ui.auth.AuthView
import expo.modules.kotlin.AppContext
Expand All@@ -25,6 +28,7 @@ private fun debugLog(tag: String, message: String) {

class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkComposeNativeViewHost(context, appContext) {
var isDismissible: Boolean = true
var logoMaxHeight: Float? = null
var mode: String? = null

private val onAuthEvent by EventDispatcher()
Expand DownExpand Up@@ -60,7 +64,7 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo

AuthView(
modifier = Modifier.fillMaxSize(),
clerkTheme = Clerk.customTheme,
clerkTheme = authTheme(),
mode = authMode(mode),
isDismissible = isDismissible,
onDismiss = ::sendDismissEvent,
Expand All@@ -70,6 +74,13 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo
)
}

private fun authTheme(): ClerkTheme? {
val maxHeight = logoMaxHeight ?: return Clerk.customTheme
val theme = Clerk.customTheme ?: ClerkTheme()
val design = theme.design ?: ClerkDesign()
return theme.copy(design = design.copy(logoMaxHeight = maxHeight.dp))
}

private fun sendEvent(type: String) {
onAuthEvent(mapOf("type" to type))
}
Expand DownExpand Up@@ -102,6 +113,10 @@ class ClerkAuthViewModule : Module() {
view.isDismissible = isDismissible
}

Prop("logoMaxHeight") { view: ClerkAuthNativeView, logoMaxHeight: Float? ->
view.logoMaxHeight = logoMaxHeight
}

OnViewDidUpdateProps { view: ClerkAuthNativeView ->
view.setupView()
}
Expand Down
12 changes: 12 additions & 0 deletions packages/expo/ios/ClerkAuthNativeView.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import UIKit
public class ClerkAuthNativeView: ClerkNativeViewHost {
private var currentMode: String = "signInOrUp"
private var currentDismissible: Bool = true
private var currentLogoMaxHeight: CGFloat?
private var didSendDismiss = false

let onAuthEvent = EventDispatcher()
Expand All@@ -22,6 +23,12 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
setNeedsHostedViewUpdate()
}

func setLogoMaxHeight(_ logoMaxHeight: CGFloat?) {
guard logoMaxHeight != currentLogoMaxHeight else { return }
currentLogoMaxHeight = logoMaxHeight
setNeedsHostedViewUpdate()
}

private func sendAuthEvent(type: ClerkNativeViewEvent) {
onAuthEvent(["type": type.rawValue])
}
Expand All@@ -45,6 +52,7 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
return ClerkNativeBridge.shared.makeAuthViewController(
mode: currentMode,
dismissible: currentDismissible,
logoMaxHeight: currentLogoMaxHeight,
onEvent: { [weak self] event, _ in
if event == .dismissed {
self?.sendDismissIfNeeded()
Expand All@@ -68,6 +76,10 @@ public class ClerkAuthViewModule: Module {
Prop("isDismissible") { (view: ClerkAuthNativeView, isDismissible: Bool?) in
view.setDismissible(isDismissible)
}

Prop("logoMaxHeight") { (view: ClerkAuthNativeView, logoMaxHeight: CGFloat?) in
view.setLogoMaxHeight(logoMaxHeight)
}
}
}
}
15 changes: 12 additions & 3 deletions packages/expo/ios/ClerkNativeBridge.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -229,6 +229,7 @@ final class ClerkNativeBridge {
func makeAuthViewController(
mode: String,
dismissible: Bool,
logoMaxHeight: CGFloat?,
onEvent: @escaping (ClerkNativeViewEvent, [String: Any]) -> Void
) -> UIViewController? {
guard Self.clerkConfigured else { return nil }
Expand All@@ -238,7 +239,8 @@ final class ClerkNativeBridge {
mode: Self.authMode(from: mode),
dismissible: dismissible,
lightTheme: lightTheme,
darkTheme: darkTheme
darkTheme: darkTheme,
logoMaxHeight: logoMaxHeight
),
onDismiss: dismissible ? { onEvent(.dismissed, [:]) } : nil
)
Expand DownExpand Up@@ -465,20 +467,27 @@ struct ClerkInlineAuthWrapperView: View {
let dismissible: Bool
let lightTheme: ClerkTheme?
let darkTheme: ClerkTheme?
let logoMaxHeight: CGFloat?

@Environment(\.colorScheme) private var colorScheme

private var themedAuthView: some View {
@ViewBuilder private var themedAuthView: some View {
let view = AuthView(mode: mode, isDismissible: dismissible)
.environment(Clerk.shared)
let theme = colorScheme == .dark ? (darkTheme ?? lightTheme) : lightTheme
return Group {
let themedView = Group {
if let theme {
view.environment(\.clerkTheme, theme)
} else {
view
}
}

if let logoMaxHeight {
themedView.clerkAppIcon(maxHeight: logoMaxHeight)
} else {
themedView
}
}

var body: some View {
Expand Down
8 changes: 7 additions & 1 deletion packages/expo/src/native/AuthView.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,12 @@ type AuthNativeEvent = NativeSyntheticEvent<Readonly<{ type: string }>>;
*
* @see {@link https://clerk.com/docs/components/authentication/sign-in} Clerk Sign-In Documentation
*/
export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss }: AuthViewProps): ReactElement {
export function AuthView({
mode = 'signInOrUp',
isDismissible = true,
logoMaxHeight,
onDismiss,
}: AuthViewProps): ReactElement {
Comment on lines +40 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
# Inspect the relevant file and nearby references
git ls-files 'packages/expo/src/native/AuthView.tsx''packages/expo/**'| sed -n '1,120p'echoecho"== AuthView.tsx =="
cat -n packages/expo/src/native/AuthView.tsx | sed -n '1,220p'echoecho"== Search for logoMaxHeight =="
rg -n "logoMaxHeight" packages/expo -S
echoecho"== Search for AuthViewProps =="
rg -n "type AuthViewProps|interface AuthViewProps|AuthViewProps" packages/expo -S

Repository: clerk/javascript

Length of output: 11533


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== AuthView props =="
cat -n packages/expo/src/native/AuthView.types.ts | sed -n '1,180p'echoecho"== iOS Auth view =="
cat -n packages/expo/ios/ClerkAuthNativeView.swift | sed -n '1,180p'echoecho"== Android Auth view =="
cat -n packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt | sed -n '1,220p'echoecho"== Native spec =="
cat -n packages/expo/src/specs/NativeClerkAuthView.ts | sed -n '1,120p'echo
cat -n packages/expo/src/specs/NativeClerkAuthView.android.ts | sed -n '1,120p'

Repository: clerk/javascript

Length of output: 11312


Validate logoMaxHeight before forwarding it to native.

It’s passed through unchanged, and both native implementations use it directly. Reject or coerce non-finite or negative values before crossing the bridge, and add coverage for invalid inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/src/native/AuthView.tsx` around lines 40 - 45, Validate
logoMaxHeight in AuthView before passing it to either native implementation:
reject or coerce values that are non-finite or negative, while preserving valid
values. Add coverage for non-finite and negative inputs, confirming invalid
values never cross the native bridge.

Source: Coding guidelines

const handleAuthEvent = useCallback(
(event: AuthNativeEvent) => {
if (event.nativeEvent.type === 'dismissed') {
Expand All@@ -64,6 +69,7 @@ export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss
style={{ flex: 1 }}
mode={mode}
isDismissible={isDismissible}
logoMaxHeight={logoMaxHeight}
onAuthEvent={handleAuthEvent}
/>
);
Expand Down
7 changes: 7 additions & 0 deletions packages/expo/src/native/AuthView.types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,13 @@ export interface AuthViewProps {
*/
isDismissible?: boolean;

/**
* Maximum height of the Clerk application logo, in density-independent pixels.
*
* @default 44
*/
logoMaxHeight?: number;

/**
* Called when the native authentication view requests dismissal.
*
Expand Down
12 changes: 11 additions & 1 deletion packages/expo/src/native/__tests__/AuthView.test.tsx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test, vi } from 'vitest';
import { beforeEach, describe, expect, test, vi } from 'vitest';

import { AuthView } from '../AuthView';

Expand DownExpand Up@@ -30,6 +30,16 @@ vi.mock('react-native', () => {
});

describe('AuthView', () => {
beforeEach(() => {
mocks.NativeClerkAuthView.mockClear();
});

test('passes logoMaxHeight to the native auth view', () => {
render(<AuthView logoMaxHeight={64} />);

expect(mocks.NativeClerkAuthView.mock.calls[0]?.[0]).toMatchObject({ logoMaxHeight: 64 });
});

test('calls onDismiss when the native auth view emits dismissed', () => {
const onDismiss = vi.fn();

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.android.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
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
11 changes: 11 additions & 0 deletions .changeset/expo-native-logo-max-height.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
---
'@clerk/expo': patch
---

Add a `logoMaxHeight` prop to `AuthView`, allowing Expo apps to control the maximum height of the managed logo on Android and iOS.

Usage:

```tsx
<AuthView logoMaxHeight={64} />
```
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,9 +5,12 @@ import android.util.Log
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import com.clerk.api.Clerk
import com.clerk.api.ui.ClerkDesign
import com.clerk.api.ui.ClerkTheme
import com.clerk.ui.auth.AuthMode
import com.clerk.ui.auth.AuthView
import expo.modules.kotlin.AppContext
Expand All@@ -25,6 +28,7 @@ private fun debugLog(tag: String, message: String) {

class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkComposeNativeViewHost(context, appContext) {
var isDismissible: Boolean = true
var logoMaxHeight: Float? = null
var mode: String? = null

private val onAuthEvent by EventDispatcher()
Expand DownExpand Up@@ -60,7 +64,7 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo

AuthView(
modifier = Modifier.fillMaxSize(),
clerkTheme = Clerk.customTheme,
clerkTheme = authTheme(),
mode = authMode(mode),
isDismissible = isDismissible,
onDismiss = ::sendDismissEvent,
Expand All@@ -70,6 +74,13 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo
)
}

private fun authTheme(): ClerkTheme? {
val maxHeight = logoMaxHeight ?: return Clerk.customTheme
val theme = Clerk.customTheme ?: ClerkTheme()
val design = theme.design ?: ClerkDesign()
return theme.copy(design = design.copy(logoMaxHeight = maxHeight.dp))
}

private fun sendEvent(type: String) {
onAuthEvent(mapOf("type" to type))
}
Expand DownExpand Up@@ -102,6 +113,10 @@ class ClerkAuthViewModule : Module() {
view.isDismissible = isDismissible
}

Prop("logoMaxHeight") { view: ClerkAuthNativeView, logoMaxHeight: Float? ->
view.logoMaxHeight = logoMaxHeight
}

OnViewDidUpdateProps { view: ClerkAuthNativeView ->
view.setupView()
}
Expand Down
12 changes: 12 additions & 0 deletions packages/expo/ios/ClerkAuthNativeView.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import UIKit
public class ClerkAuthNativeView: ClerkNativeViewHost {
private var currentMode: String = "signInOrUp"
private var currentDismissible: Bool = true
private var currentLogoMaxHeight: CGFloat?
private var didSendDismiss = false

let onAuthEvent = EventDispatcher()
Expand All@@ -22,6 +23,12 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
setNeedsHostedViewUpdate()
}

func setLogoMaxHeight(_ logoMaxHeight: CGFloat?) {
guard logoMaxHeight != currentLogoMaxHeight else { return }
currentLogoMaxHeight = logoMaxHeight
setNeedsHostedViewUpdate()
}

private func sendAuthEvent(type: ClerkNativeViewEvent) {
onAuthEvent(["type": type.rawValue])
}
Expand All@@ -45,6 +52,7 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
return ClerkNativeBridge.shared.makeAuthViewController(
mode: currentMode,
dismissible: currentDismissible,
logoMaxHeight: currentLogoMaxHeight,
onEvent: { [weak self] event, _ in
if event == .dismissed {
self?.sendDismissIfNeeded()
Expand All@@ -68,6 +76,10 @@ public class ClerkAuthViewModule: Module {
Prop("isDismissible") { (view: ClerkAuthNativeView, isDismissible: Bool?) in
view.setDismissible(isDismissible)
}

Prop("logoMaxHeight") { (view: ClerkAuthNativeView, logoMaxHeight: CGFloat?) in
view.setLogoMaxHeight(logoMaxHeight)
}
}
}
}
15 changes: 12 additions & 3 deletions packages/expo/ios/ClerkNativeBridge.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -229,6 +229,7 @@ final class ClerkNativeBridge {
func makeAuthViewController(
mode: String,
dismissible: Bool,
logoMaxHeight: CGFloat?,
onEvent: @escaping (ClerkNativeViewEvent, [String: Any]) -> Void
) -> UIViewController? {
guard Self.clerkConfigured else { return nil }
Expand All@@ -238,7 +239,8 @@ final class ClerkNativeBridge {
mode: Self.authMode(from: mode),
dismissible: dismissible,
lightTheme: lightTheme,
darkTheme: darkTheme
darkTheme: darkTheme,
logoMaxHeight: logoMaxHeight
),
onDismiss: dismissible ? { onEvent(.dismissed, [:]) } : nil
)
Expand DownExpand Up@@ -465,20 +467,27 @@ struct ClerkInlineAuthWrapperView: View {
let dismissible: Bool
let lightTheme: ClerkTheme?
let darkTheme: ClerkTheme?
let logoMaxHeight: CGFloat?

@Environment(\.colorScheme) private var colorScheme

private var themedAuthView: some View {
@ViewBuilder private var themedAuthView: some View {
let view = AuthView(mode: mode, isDismissible: dismissible)
.environment(Clerk.shared)
let theme = colorScheme == .dark ? (darkTheme ?? lightTheme) : lightTheme
return Group {
let themedView = Group {
if let theme {
view.environment(\.clerkTheme, theme)
} else {
view
}
}

if let logoMaxHeight {
themedView.clerkAppIcon(maxHeight: logoMaxHeight)
} else {
themedView
}
}

var body: some View {
Expand Down
8 changes: 7 additions & 1 deletion packages/expo/src/native/AuthView.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,12 @@ type AuthNativeEvent = NativeSyntheticEvent<Readonly<{ type: string }>>;
*
* @see {@link https://clerk.com/docs/components/authentication/sign-in} Clerk Sign-In Documentation
*/
export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss }: AuthViewProps): ReactElement {
export function AuthView({
mode = 'signInOrUp',
isDismissible = true,
logoMaxHeight,
onDismiss,
}: AuthViewProps): ReactElement {
Comment on lines +40 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
# Inspect the relevant file and nearby references
git ls-files 'packages/expo/src/native/AuthView.tsx''packages/expo/**'| sed -n '1,120p'echoecho"== AuthView.tsx =="
cat -n packages/expo/src/native/AuthView.tsx | sed -n '1,220p'echoecho"== Search for logoMaxHeight =="
rg -n "logoMaxHeight" packages/expo -S
echoecho"== Search for AuthViewProps =="
rg -n "type AuthViewProps|interface AuthViewProps|AuthViewProps" packages/expo -S

Repository: clerk/javascript

Length of output: 11533


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== AuthView props =="
cat -n packages/expo/src/native/AuthView.types.ts | sed -n '1,180p'echoecho"== iOS Auth view =="
cat -n packages/expo/ios/ClerkAuthNativeView.swift | sed -n '1,180p'echoecho"== Android Auth view =="
cat -n packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt | sed -n '1,220p'echoecho"== Native spec =="
cat -n packages/expo/src/specs/NativeClerkAuthView.ts | sed -n '1,120p'echo
cat -n packages/expo/src/specs/NativeClerkAuthView.android.ts | sed -n '1,120p'

Repository: clerk/javascript

Length of output: 11312


Validate logoMaxHeight before forwarding it to native.

It’s passed through unchanged, and both native implementations use it directly. Reject or coerce non-finite or negative values before crossing the bridge, and add coverage for invalid inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/src/native/AuthView.tsx` around lines 40 - 45, Validate
logoMaxHeight in AuthView before passing it to either native implementation:
reject or coerce values that are non-finite or negative, while preserving valid
values. Add coverage for non-finite and negative inputs, confirming invalid
values never cross the native bridge.

Source: Coding guidelines

const handleAuthEvent = useCallback(
(event: AuthNativeEvent) => {
if (event.nativeEvent.type === 'dismissed') {
Expand All@@ -64,6 +69,7 @@ export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss
style={{ flex: 1 }}
mode={mode}
isDismissible={isDismissible}
logoMaxHeight={logoMaxHeight}
onAuthEvent={handleAuthEvent}
/>
);
Expand Down
7 changes: 7 additions & 0 deletions packages/expo/src/native/AuthView.types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,13 @@ export interface AuthViewProps {
*/
isDismissible?: boolean;

/**
* Maximum height of the Clerk application logo, in density-independent pixels.
*
* @default 44
*/
logoMaxHeight?: number;

/**
* Called when the native authentication view requests dismissal.
*
Expand Down
12 changes: 11 additions & 1 deletion packages/expo/src/native/__tests__/AuthView.test.tsx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test, vi } from 'vitest';
import { beforeEach, describe, expect, test, vi } from 'vitest';

import { AuthView } from '../AuthView';

Expand DownExpand Up@@ -30,6 +30,16 @@ vi.mock('react-native', () => {
});

describe('AuthView', () => {
beforeEach(() => {
mocks.NativeClerkAuthView.mockClear();
});

test('passes logoMaxHeight to the native auth view', () => {
render(<AuthView logoMaxHeight={64} />);

expect(mocks.NativeClerkAuthView.mock.calls[0]?.[0]).toMatchObject({ logoMaxHeight: 64 });
});

test('calls onDismiss when the native auth view emits dismissed', () => {
const onDismiss = vi.fn();

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.android.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
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
11 changes: 11 additions & 0 deletions .changeset/expo-native-logo-max-height.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
---
'@clerk/expo': patch
---

Add a `logoMaxHeight` prop to `AuthView`, allowing Expo apps to control the maximum height of the managed logo on Android and iOS.

Usage:

```tsx
<AuthView logoMaxHeight={64} />
```
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,9 +5,12 @@ import android.util.Log
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import com.clerk.api.Clerk
import com.clerk.api.ui.ClerkDesign
import com.clerk.api.ui.ClerkTheme
import com.clerk.ui.auth.AuthMode
import com.clerk.ui.auth.AuthView
import expo.modules.kotlin.AppContext
Expand All@@ -25,6 +28,7 @@ private fun debugLog(tag: String, message: String) {

class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkComposeNativeViewHost(context, appContext) {
var isDismissible: Boolean = true
var logoMaxHeight: Float? = null
var mode: String? = null

private val onAuthEvent by EventDispatcher()
Expand DownExpand Up@@ -60,7 +64,7 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo

AuthView(
modifier = Modifier.fillMaxSize(),
clerkTheme = Clerk.customTheme,
clerkTheme = authTheme(),
mode = authMode(mode),
isDismissible = isDismissible,
onDismiss = ::sendDismissEvent,
Expand All@@ -70,6 +74,13 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo
)
}

private fun authTheme(): ClerkTheme? {
val maxHeight = logoMaxHeight ?: return Clerk.customTheme
val theme = Clerk.customTheme ?: ClerkTheme()
val design = theme.design ?: ClerkDesign()
return theme.copy(design = design.copy(logoMaxHeight = maxHeight.dp))
}

private fun sendEvent(type: String) {
onAuthEvent(mapOf("type" to type))
}
Expand DownExpand Up@@ -102,6 +113,10 @@ class ClerkAuthViewModule : Module() {
view.isDismissible = isDismissible
}

Prop("logoMaxHeight") { view: ClerkAuthNativeView, logoMaxHeight: Float? ->
view.logoMaxHeight = logoMaxHeight
}

OnViewDidUpdateProps { view: ClerkAuthNativeView ->
view.setupView()
}
Expand Down
12 changes: 12 additions & 0 deletions packages/expo/ios/ClerkAuthNativeView.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import UIKit
public class ClerkAuthNativeView: ClerkNativeViewHost {
private var currentMode: String = "signInOrUp"
private var currentDismissible: Bool = true
private var currentLogoMaxHeight: CGFloat?
private var didSendDismiss = false

let onAuthEvent = EventDispatcher()
Expand All@@ -22,6 +23,12 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
setNeedsHostedViewUpdate()
}

func setLogoMaxHeight(_ logoMaxHeight: CGFloat?) {
guard logoMaxHeight != currentLogoMaxHeight else { return }
currentLogoMaxHeight = logoMaxHeight
setNeedsHostedViewUpdate()
}

private func sendAuthEvent(type: ClerkNativeViewEvent) {
onAuthEvent(["type": type.rawValue])
}
Expand All@@ -45,6 +52,7 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
return ClerkNativeBridge.shared.makeAuthViewController(
mode: currentMode,
dismissible: currentDismissible,
logoMaxHeight: currentLogoMaxHeight,
onEvent: { [weak self] event, _ in
if event == .dismissed {
self?.sendDismissIfNeeded()
Expand All@@ -68,6 +76,10 @@ public class ClerkAuthViewModule: Module {
Prop("isDismissible") { (view: ClerkAuthNativeView, isDismissible: Bool?) in
view.setDismissible(isDismissible)
}

Prop("logoMaxHeight") { (view: ClerkAuthNativeView, logoMaxHeight: CGFloat?) in
view.setLogoMaxHeight(logoMaxHeight)
}
}
}
}
15 changes: 12 additions & 3 deletions packages/expo/ios/ClerkNativeBridge.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -229,6 +229,7 @@ final class ClerkNativeBridge {
func makeAuthViewController(
mode: String,
dismissible: Bool,
logoMaxHeight: CGFloat?,
onEvent: @escaping (ClerkNativeViewEvent, [String: Any]) -> Void
) -> UIViewController? {
guard Self.clerkConfigured else { return nil }
Expand All@@ -238,7 +239,8 @@ final class ClerkNativeBridge {
mode: Self.authMode(from: mode),
dismissible: dismissible,
lightTheme: lightTheme,
darkTheme: darkTheme
darkTheme: darkTheme,
logoMaxHeight: logoMaxHeight
),
onDismiss: dismissible ? { onEvent(.dismissed, [:]) } : nil
)
Expand DownExpand Up@@ -465,20 +467,27 @@ struct ClerkInlineAuthWrapperView: View {
let dismissible: Bool
let lightTheme: ClerkTheme?
let darkTheme: ClerkTheme?
let logoMaxHeight: CGFloat?

@Environment(\.colorScheme) private var colorScheme

private var themedAuthView: some View {
@ViewBuilder private var themedAuthView: some View {
let view = AuthView(mode: mode, isDismissible: dismissible)
.environment(Clerk.shared)
let theme = colorScheme == .dark ? (darkTheme ?? lightTheme) : lightTheme
return Group {
let themedView = Group {
if let theme {
view.environment(\.clerkTheme, theme)
} else {
view
}
}

if let logoMaxHeight {
themedView.clerkAppIcon(maxHeight: logoMaxHeight)
} else {
themedView
}
}

var body: some View {
Expand Down
8 changes: 7 additions & 1 deletion packages/expo/src/native/AuthView.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,12 @@ type AuthNativeEvent = NativeSyntheticEvent<Readonly<{ type: string }>>;
*
* @see {@link https://clerk.com/docs/components/authentication/sign-in} Clerk Sign-In Documentation
*/
export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss }: AuthViewProps): ReactElement {
export function AuthView({
mode = 'signInOrUp',
isDismissible = true,
logoMaxHeight,
onDismiss,
}: AuthViewProps): ReactElement {
Comment on lines +40 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
# Inspect the relevant file and nearby references
git ls-files 'packages/expo/src/native/AuthView.tsx''packages/expo/**'| sed -n '1,120p'echoecho"== AuthView.tsx =="
cat -n packages/expo/src/native/AuthView.tsx | sed -n '1,220p'echoecho"== Search for logoMaxHeight =="
rg -n "logoMaxHeight" packages/expo -S
echoecho"== Search for AuthViewProps =="
rg -n "type AuthViewProps|interface AuthViewProps|AuthViewProps" packages/expo -S

Repository: clerk/javascript

Length of output: 11533


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== AuthView props =="
cat -n packages/expo/src/native/AuthView.types.ts | sed -n '1,180p'echoecho"== iOS Auth view =="
cat -n packages/expo/ios/ClerkAuthNativeView.swift | sed -n '1,180p'echoecho"== Android Auth view =="
cat -n packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt | sed -n '1,220p'echoecho"== Native spec =="
cat -n packages/expo/src/specs/NativeClerkAuthView.ts | sed -n '1,120p'echo
cat -n packages/expo/src/specs/NativeClerkAuthView.android.ts | sed -n '1,120p'

Repository: clerk/javascript

Length of output: 11312


Validate logoMaxHeight before forwarding it to native.

It’s passed through unchanged, and both native implementations use it directly. Reject or coerce non-finite or negative values before crossing the bridge, and add coverage for invalid inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/src/native/AuthView.tsx` around lines 40 - 45, Validate
logoMaxHeight in AuthView before passing it to either native implementation:
reject or coerce values that are non-finite or negative, while preserving valid
values. Add coverage for non-finite and negative inputs, confirming invalid
values never cross the native bridge.

Source: Coding guidelines

const handleAuthEvent = useCallback(
(event: AuthNativeEvent) => {
if (event.nativeEvent.type === 'dismissed') {
Expand All@@ -64,6 +69,7 @@ export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss
style={{ flex: 1 }}
mode={mode}
isDismissible={isDismissible}
logoMaxHeight={logoMaxHeight}
onAuthEvent={handleAuthEvent}
/>
);
Expand Down
7 changes: 7 additions & 0 deletions packages/expo/src/native/AuthView.types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,13 @@ export interface AuthViewProps {
*/
isDismissible?: boolean;

/**
* Maximum height of the Clerk application logo, in density-independent pixels.
*
* @default 44
*/
logoMaxHeight?: number;

/**
* Called when the native authentication view requests dismissal.
*
Expand Down
12 changes: 11 additions & 1 deletion packages/expo/src/native/__tests__/AuthView.test.tsx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test, vi } from 'vitest';
import { beforeEach, describe, expect, test, vi } from 'vitest';

import { AuthView } from '../AuthView';

Expand DownExpand Up@@ -30,6 +30,16 @@ vi.mock('react-native', () => {
});

describe('AuthView', () => {
beforeEach(() => {
mocks.NativeClerkAuthView.mockClear();
});

test('passes logoMaxHeight to the native auth view', () => {
render(<AuthView logoMaxHeight={64} />);

expect(mocks.NativeClerkAuthView.mock.calls[0]?.[0]).toMatchObject({ logoMaxHeight: 64 });
});

test('calls onDismiss when the native auth view emits dismissed', () => {
const onDismiss = vi.fn();

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.android.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
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
11 changes: 11 additions & 0 deletions .changeset/expo-native-logo-max-height.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
---
'@clerk/expo': patch
---

Add a `logoMaxHeight` prop to `AuthView`, allowing Expo apps to control the maximum height of the managed logo on Android and iOS.

Usage:

```tsx
<AuthView logoMaxHeight={64} />
```
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,9 +5,12 @@ import android.util.Log
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import com.clerk.api.Clerk
import com.clerk.api.ui.ClerkDesign
import com.clerk.api.ui.ClerkTheme
import com.clerk.ui.auth.AuthMode
import com.clerk.ui.auth.AuthView
import expo.modules.kotlin.AppContext
Expand All@@ -25,6 +28,7 @@ private fun debugLog(tag: String, message: String) {

class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkComposeNativeViewHost(context, appContext) {
var isDismissible: Boolean = true
var logoMaxHeight: Float? = null
var mode: String? = null

private val onAuthEvent by EventDispatcher()
Expand DownExpand Up@@ -60,7 +64,7 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo

AuthView(
modifier = Modifier.fillMaxSize(),
clerkTheme = Clerk.customTheme,
clerkTheme = authTheme(),
mode = authMode(mode),
isDismissible = isDismissible,
onDismiss = ::sendDismissEvent,
Expand All@@ -70,6 +74,13 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo
)
}

private fun authTheme(): ClerkTheme? {
val maxHeight = logoMaxHeight ?: return Clerk.customTheme
val theme = Clerk.customTheme ?: ClerkTheme()
val design = theme.design ?: ClerkDesign()
return theme.copy(design = design.copy(logoMaxHeight = maxHeight.dp))
}

private fun sendEvent(type: String) {
onAuthEvent(mapOf("type" to type))
}
Expand DownExpand Up@@ -102,6 +113,10 @@ class ClerkAuthViewModule : Module() {
view.isDismissible = isDismissible
}

Prop("logoMaxHeight") { view: ClerkAuthNativeView, logoMaxHeight: Float? ->
view.logoMaxHeight = logoMaxHeight
}

OnViewDidUpdateProps { view: ClerkAuthNativeView ->
view.setupView()
}
Expand Down
12 changes: 12 additions & 0 deletions packages/expo/ios/ClerkAuthNativeView.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import UIKit
public class ClerkAuthNativeView: ClerkNativeViewHost {
private var currentMode: String = "signInOrUp"
private var currentDismissible: Bool = true
private var currentLogoMaxHeight: CGFloat?
private var didSendDismiss = false

let onAuthEvent = EventDispatcher()
Expand All@@ -22,6 +23,12 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
setNeedsHostedViewUpdate()
}

func setLogoMaxHeight(_ logoMaxHeight: CGFloat?) {
guard logoMaxHeight != currentLogoMaxHeight else { return }
currentLogoMaxHeight = logoMaxHeight
setNeedsHostedViewUpdate()
}

private func sendAuthEvent(type: ClerkNativeViewEvent) {
onAuthEvent(["type": type.rawValue])
}
Expand All@@ -45,6 +52,7 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
return ClerkNativeBridge.shared.makeAuthViewController(
mode: currentMode,
dismissible: currentDismissible,
logoMaxHeight: currentLogoMaxHeight,
onEvent: { [weak self] event, _ in
if event == .dismissed {
self?.sendDismissIfNeeded()
Expand All@@ -68,6 +76,10 @@ public class ClerkAuthViewModule: Module {
Prop("isDismissible") { (view: ClerkAuthNativeView, isDismissible: Bool?) in
view.setDismissible(isDismissible)
}

Prop("logoMaxHeight") { (view: ClerkAuthNativeView, logoMaxHeight: CGFloat?) in
view.setLogoMaxHeight(logoMaxHeight)
}
}
}
}
15 changes: 12 additions & 3 deletions packages/expo/ios/ClerkNativeBridge.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -229,6 +229,7 @@ final class ClerkNativeBridge {
func makeAuthViewController(
mode: String,
dismissible: Bool,
logoMaxHeight: CGFloat?,
onEvent: @escaping (ClerkNativeViewEvent, [String: Any]) -> Void
) -> UIViewController? {
guard Self.clerkConfigured else { return nil }
Expand All@@ -238,7 +239,8 @@ final class ClerkNativeBridge {
mode: Self.authMode(from: mode),
dismissible: dismissible,
lightTheme: lightTheme,
darkTheme: darkTheme
darkTheme: darkTheme,
logoMaxHeight: logoMaxHeight
),
onDismiss: dismissible ? { onEvent(.dismissed, [:]) } : nil
)
Expand DownExpand Up@@ -465,20 +467,27 @@ struct ClerkInlineAuthWrapperView: View {
let dismissible: Bool
let lightTheme: ClerkTheme?
let darkTheme: ClerkTheme?
let logoMaxHeight: CGFloat?

@Environment(\.colorScheme) private var colorScheme

private var themedAuthView: some View {
@ViewBuilder private var themedAuthView: some View {
let view = AuthView(mode: mode, isDismissible: dismissible)
.environment(Clerk.shared)
let theme = colorScheme == .dark ? (darkTheme ?? lightTheme) : lightTheme
return Group {
let themedView = Group {
if let theme {
view.environment(\.clerkTheme, theme)
} else {
view
}
}

if let logoMaxHeight {
themedView.clerkAppIcon(maxHeight: logoMaxHeight)
} else {
themedView
}
}

var body: some View {
Expand Down
8 changes: 7 additions & 1 deletion packages/expo/src/native/AuthView.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,12 @@ type AuthNativeEvent = NativeSyntheticEvent<Readonly<{ type: string }>>;
*
* @see {@link https://clerk.com/docs/components/authentication/sign-in} Clerk Sign-In Documentation
*/
export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss }: AuthViewProps): ReactElement {
export function AuthView({
mode = 'signInOrUp',
isDismissible = true,
logoMaxHeight,
onDismiss,
}: AuthViewProps): ReactElement {
Comment on lines +40 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
# Inspect the relevant file and nearby references
git ls-files 'packages/expo/src/native/AuthView.tsx''packages/expo/**'| sed -n '1,120p'echoecho"== AuthView.tsx =="
cat -n packages/expo/src/native/AuthView.tsx | sed -n '1,220p'echoecho"== Search for logoMaxHeight =="
rg -n "logoMaxHeight" packages/expo -S
echoecho"== Search for AuthViewProps =="
rg -n "type AuthViewProps|interface AuthViewProps|AuthViewProps" packages/expo -S

Repository: clerk/javascript

Length of output: 11533


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== AuthView props =="
cat -n packages/expo/src/native/AuthView.types.ts | sed -n '1,180p'echoecho"== iOS Auth view =="
cat -n packages/expo/ios/ClerkAuthNativeView.swift | sed -n '1,180p'echoecho"== Android Auth view =="
cat -n packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt | sed -n '1,220p'echoecho"== Native spec =="
cat -n packages/expo/src/specs/NativeClerkAuthView.ts | sed -n '1,120p'echo
cat -n packages/expo/src/specs/NativeClerkAuthView.android.ts | sed -n '1,120p'

Repository: clerk/javascript

Length of output: 11312


Validate logoMaxHeight before forwarding it to native.

It’s passed through unchanged, and both native implementations use it directly. Reject or coerce non-finite or negative values before crossing the bridge, and add coverage for invalid inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/src/native/AuthView.tsx` around lines 40 - 45, Validate
logoMaxHeight in AuthView before passing it to either native implementation:
reject or coerce values that are non-finite or negative, while preserving valid
values. Add coverage for non-finite and negative inputs, confirming invalid
values never cross the native bridge.

Source: Coding guidelines

const handleAuthEvent = useCallback(
(event: AuthNativeEvent) => {
if (event.nativeEvent.type === 'dismissed') {
Expand All@@ -64,6 +69,7 @@ export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss
style={{ flex: 1 }}
mode={mode}
isDismissible={isDismissible}
logoMaxHeight={logoMaxHeight}
onAuthEvent={handleAuthEvent}
/>
);
Expand Down
7 changes: 7 additions & 0 deletions packages/expo/src/native/AuthView.types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,13 @@ export interface AuthViewProps {
*/
isDismissible?: boolean;

/**
* Maximum height of the Clerk application logo, in density-independent pixels.
*
* @default 44
*/
logoMaxHeight?: number;

/**
* Called when the native authentication view requests dismissal.
*
Expand Down
12 changes: 11 additions & 1 deletion packages/expo/src/native/__tests__/AuthView.test.tsx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test, vi } from 'vitest';
import { beforeEach, describe, expect, test, vi } from 'vitest';

import { AuthView } from '../AuthView';

Expand DownExpand Up@@ -30,6 +30,16 @@ vi.mock('react-native', () => {
});

describe('AuthView', () => {
beforeEach(() => {
mocks.NativeClerkAuthView.mockClear();
});

test('passes logoMaxHeight to the native auth view', () => {
render(<AuthView logoMaxHeight={64} />);

expect(mocks.NativeClerkAuthView.mock.calls[0]?.[0]).toMatchObject({ logoMaxHeight: 64 });
});

test('calls onDismiss when the native auth view emits dismissed', () => {
const onDismiss = vi.fn();

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.android.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
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
11 changes: 11 additions & 0 deletions .changeset/expo-native-logo-max-height.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
---
'@clerk/expo': patch
---

Add a `logoMaxHeight` prop to `AuthView`, allowing Expo apps to control the maximum height of the managed logo on Android and iOS.

Usage:

```tsx
<AuthView logoMaxHeight={64} />
```
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,9 +5,12 @@ import android.util.Log
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import com.clerk.api.Clerk
import com.clerk.api.ui.ClerkDesign
import com.clerk.api.ui.ClerkTheme
import com.clerk.ui.auth.AuthMode
import com.clerk.ui.auth.AuthView
import expo.modules.kotlin.AppContext
Expand All@@ -25,6 +28,7 @@ private fun debugLog(tag: String, message: String) {

class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkComposeNativeViewHost(context, appContext) {
var isDismissible: Boolean = true
var logoMaxHeight: Float? = null
var mode: String? = null

private val onAuthEvent by EventDispatcher()
Expand DownExpand Up@@ -60,7 +64,7 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo

AuthView(
modifier = Modifier.fillMaxSize(),
clerkTheme = Clerk.customTheme,
clerkTheme = authTheme(),
mode = authMode(mode),
isDismissible = isDismissible,
onDismiss = ::sendDismissEvent,
Expand All@@ -70,6 +74,13 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo
)
}

private fun authTheme(): ClerkTheme? {
val maxHeight = logoMaxHeight ?: return Clerk.customTheme
val theme = Clerk.customTheme ?: ClerkTheme()
val design = theme.design ?: ClerkDesign()
return theme.copy(design = design.copy(logoMaxHeight = maxHeight.dp))
}

private fun sendEvent(type: String) {
onAuthEvent(mapOf("type" to type))
}
Expand DownExpand Up@@ -102,6 +113,10 @@ class ClerkAuthViewModule : Module() {
view.isDismissible = isDismissible
}

Prop("logoMaxHeight") { view: ClerkAuthNativeView, logoMaxHeight: Float? ->
view.logoMaxHeight = logoMaxHeight
}

OnViewDidUpdateProps { view: ClerkAuthNativeView ->
view.setupView()
}
Expand Down
12 changes: 12 additions & 0 deletions packages/expo/ios/ClerkAuthNativeView.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import UIKit
public class ClerkAuthNativeView: ClerkNativeViewHost {
private var currentMode: String = "signInOrUp"
private var currentDismissible: Bool = true
private var currentLogoMaxHeight: CGFloat?
private var didSendDismiss = false

let onAuthEvent = EventDispatcher()
Expand All@@ -22,6 +23,12 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
setNeedsHostedViewUpdate()
}

func setLogoMaxHeight(_ logoMaxHeight: CGFloat?) {
guard logoMaxHeight != currentLogoMaxHeight else { return }
currentLogoMaxHeight = logoMaxHeight
setNeedsHostedViewUpdate()
}

private func sendAuthEvent(type: ClerkNativeViewEvent) {
onAuthEvent(["type": type.rawValue])
}
Expand All@@ -45,6 +52,7 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
return ClerkNativeBridge.shared.makeAuthViewController(
mode: currentMode,
dismissible: currentDismissible,
logoMaxHeight: currentLogoMaxHeight,
onEvent: { [weak self] event, _ in
if event == .dismissed {
self?.sendDismissIfNeeded()
Expand All@@ -68,6 +76,10 @@ public class ClerkAuthViewModule: Module {
Prop("isDismissible") { (view: ClerkAuthNativeView, isDismissible: Bool?) in
view.setDismissible(isDismissible)
}

Prop("logoMaxHeight") { (view: ClerkAuthNativeView, logoMaxHeight: CGFloat?) in
view.setLogoMaxHeight(logoMaxHeight)
}
}
}
}
15 changes: 12 additions & 3 deletions packages/expo/ios/ClerkNativeBridge.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -229,6 +229,7 @@ final class ClerkNativeBridge {
func makeAuthViewController(
mode: String,
dismissible: Bool,
logoMaxHeight: CGFloat?,
onEvent: @escaping (ClerkNativeViewEvent, [String: Any]) -> Void
) -> UIViewController? {
guard Self.clerkConfigured else { return nil }
Expand All@@ -238,7 +239,8 @@ final class ClerkNativeBridge {
mode: Self.authMode(from: mode),
dismissible: dismissible,
lightTheme: lightTheme,
darkTheme: darkTheme
darkTheme: darkTheme,
logoMaxHeight: logoMaxHeight
),
onDismiss: dismissible ? { onEvent(.dismissed, [:]) } : nil
)
Expand DownExpand Up@@ -465,20 +467,27 @@ struct ClerkInlineAuthWrapperView: View {
let dismissible: Bool
let lightTheme: ClerkTheme?
let darkTheme: ClerkTheme?
let logoMaxHeight: CGFloat?

@Environment(\.colorScheme) private var colorScheme

private var themedAuthView: some View {
@ViewBuilder private var themedAuthView: some View {
let view = AuthView(mode: mode, isDismissible: dismissible)
.environment(Clerk.shared)
let theme = colorScheme == .dark ? (darkTheme ?? lightTheme) : lightTheme
return Group {
let themedView = Group {
if let theme {
view.environment(\.clerkTheme, theme)
} else {
view
}
}

if let logoMaxHeight {
themedView.clerkAppIcon(maxHeight: logoMaxHeight)
} else {
themedView
}
}

var body: some View {
Expand Down
8 changes: 7 additions & 1 deletion packages/expo/src/native/AuthView.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,12 @@ type AuthNativeEvent = NativeSyntheticEvent<Readonly<{ type: string }>>;
*
* @see {@link https://clerk.com/docs/components/authentication/sign-in} Clerk Sign-In Documentation
*/
export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss }: AuthViewProps): ReactElement {
export function AuthView({
mode = 'signInOrUp',
isDismissible = true,
logoMaxHeight,
onDismiss,
}: AuthViewProps): ReactElement {
Comment on lines +40 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
# Inspect the relevant file and nearby references
git ls-files 'packages/expo/src/native/AuthView.tsx''packages/expo/**'| sed -n '1,120p'echoecho"== AuthView.tsx =="
cat -n packages/expo/src/native/AuthView.tsx | sed -n '1,220p'echoecho"== Search for logoMaxHeight =="
rg -n "logoMaxHeight" packages/expo -S
echoecho"== Search for AuthViewProps =="
rg -n "type AuthViewProps|interface AuthViewProps|AuthViewProps" packages/expo -S

Repository: clerk/javascript

Length of output: 11533


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== AuthView props =="
cat -n packages/expo/src/native/AuthView.types.ts | sed -n '1,180p'echoecho"== iOS Auth view =="
cat -n packages/expo/ios/ClerkAuthNativeView.swift | sed -n '1,180p'echoecho"== Android Auth view =="
cat -n packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt | sed -n '1,220p'echoecho"== Native spec =="
cat -n packages/expo/src/specs/NativeClerkAuthView.ts | sed -n '1,120p'echo
cat -n packages/expo/src/specs/NativeClerkAuthView.android.ts | sed -n '1,120p'

Repository: clerk/javascript

Length of output: 11312


Validate logoMaxHeight before forwarding it to native.

It’s passed through unchanged, and both native implementations use it directly. Reject or coerce non-finite or negative values before crossing the bridge, and add coverage for invalid inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/src/native/AuthView.tsx` around lines 40 - 45, Validate
logoMaxHeight in AuthView before passing it to either native implementation:
reject or coerce values that are non-finite or negative, while preserving valid
values. Add coverage for non-finite and negative inputs, confirming invalid
values never cross the native bridge.

Source: Coding guidelines

const handleAuthEvent = useCallback(
(event: AuthNativeEvent) => {
if (event.nativeEvent.type === 'dismissed') {
Expand All@@ -64,6 +69,7 @@ export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss
style={{ flex: 1 }}
mode={mode}
isDismissible={isDismissible}
logoMaxHeight={logoMaxHeight}
onAuthEvent={handleAuthEvent}
/>
);
Expand Down
7 changes: 7 additions & 0 deletions packages/expo/src/native/AuthView.types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,13 @@ export interface AuthViewProps {
*/
isDismissible?: boolean;

/**
* Maximum height of the Clerk application logo, in density-independent pixels.
*
* @default 44
*/
logoMaxHeight?: number;

/**
* Called when the native authentication view requests dismissal.
*
Expand Down
12 changes: 11 additions & 1 deletion packages/expo/src/native/__tests__/AuthView.test.tsx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test, vi } from 'vitest';
import { beforeEach, describe, expect, test, vi } from 'vitest';

import { AuthView } from '../AuthView';

Expand DownExpand Up@@ -30,6 +30,16 @@ vi.mock('react-native', () => {
});

describe('AuthView', () => {
beforeEach(() => {
mocks.NativeClerkAuthView.mockClear();
});

test('passes logoMaxHeight to the native auth view', () => {
render(<AuthView logoMaxHeight={64} />);

expect(mocks.NativeClerkAuthView.mock.calls[0]?.[0]).toMatchObject({ logoMaxHeight: 64 });
});

test('calls onDismiss when the native auth view emits dismissed', () => {
const onDismiss = vi.fn();

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.android.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
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
11 changes: 11 additions & 0 deletions .changeset/expo-native-logo-max-height.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
---
'@clerk/expo': patch
---

Add a `logoMaxHeight` prop to `AuthView`, allowing Expo apps to control the maximum height of the managed logo on Android and iOS.

Usage:

```tsx
<AuthView logoMaxHeight={64} />
```
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,9 +5,12 @@ import android.util.Log
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import com.clerk.api.Clerk
import com.clerk.api.ui.ClerkDesign
import com.clerk.api.ui.ClerkTheme
import com.clerk.ui.auth.AuthMode
import com.clerk.ui.auth.AuthView
import expo.modules.kotlin.AppContext
Expand All@@ -25,6 +28,7 @@ private fun debugLog(tag: String, message: String) {

class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkComposeNativeViewHost(context, appContext) {
var isDismissible: Boolean = true
var logoMaxHeight: Float? = null
var mode: String? = null

private val onAuthEvent by EventDispatcher()
Expand DownExpand Up@@ -60,7 +64,7 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo

AuthView(
modifier = Modifier.fillMaxSize(),
clerkTheme = Clerk.customTheme,
clerkTheme = authTheme(),
mode = authMode(mode),
isDismissible = isDismissible,
onDismiss = ::sendDismissEvent,
Expand All@@ -70,6 +74,13 @@ class ClerkAuthNativeView(context: Context, appContext: AppContext) : ClerkCompo
)
}

private fun authTheme(): ClerkTheme? {
val maxHeight = logoMaxHeight ?: return Clerk.customTheme
val theme = Clerk.customTheme ?: ClerkTheme()
val design = theme.design ?: ClerkDesign()
return theme.copy(design = design.copy(logoMaxHeight = maxHeight.dp))
}

private fun sendEvent(type: String) {
onAuthEvent(mapOf("type" to type))
}
Expand DownExpand Up@@ -102,6 +113,10 @@ class ClerkAuthViewModule : Module() {
view.isDismissible = isDismissible
}

Prop("logoMaxHeight") { view: ClerkAuthNativeView, logoMaxHeight: Float? ->
view.logoMaxHeight = logoMaxHeight
}

OnViewDidUpdateProps { view: ClerkAuthNativeView ->
view.setupView()
}
Expand Down
12 changes: 12 additions & 0 deletions packages/expo/ios/ClerkAuthNativeView.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import UIKit
public class ClerkAuthNativeView: ClerkNativeViewHost {
private var currentMode: String = "signInOrUp"
private var currentDismissible: Bool = true
private var currentLogoMaxHeight: CGFloat?
private var didSendDismiss = false

let onAuthEvent = EventDispatcher()
Expand All@@ -22,6 +23,12 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
setNeedsHostedViewUpdate()
}

func setLogoMaxHeight(_ logoMaxHeight: CGFloat?) {
guard logoMaxHeight != currentLogoMaxHeight else { return }
currentLogoMaxHeight = logoMaxHeight
setNeedsHostedViewUpdate()
}

private func sendAuthEvent(type: ClerkNativeViewEvent) {
onAuthEvent(["type": type.rawValue])
}
Expand All@@ -45,6 +52,7 @@ public class ClerkAuthNativeView: ClerkNativeViewHost {
return ClerkNativeBridge.shared.makeAuthViewController(
mode: currentMode,
dismissible: currentDismissible,
logoMaxHeight: currentLogoMaxHeight,
onEvent: { [weak self] event, _ in
if event == .dismissed {
self?.sendDismissIfNeeded()
Expand All@@ -68,6 +76,10 @@ public class ClerkAuthViewModule: Module {
Prop("isDismissible") { (view: ClerkAuthNativeView, isDismissible: Bool?) in
view.setDismissible(isDismissible)
}

Prop("logoMaxHeight") { (view: ClerkAuthNativeView, logoMaxHeight: CGFloat?) in
view.setLogoMaxHeight(logoMaxHeight)
}
}
}
}
15 changes: 12 additions & 3 deletions packages/expo/ios/ClerkNativeBridge.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -229,6 +229,7 @@ final class ClerkNativeBridge {
func makeAuthViewController(
mode: String,
dismissible: Bool,
logoMaxHeight: CGFloat?,
onEvent: @escaping (ClerkNativeViewEvent, [String: Any]) -> Void
) -> UIViewController? {
guard Self.clerkConfigured else { return nil }
Expand All@@ -238,7 +239,8 @@ final class ClerkNativeBridge {
mode: Self.authMode(from: mode),
dismissible: dismissible,
lightTheme: lightTheme,
darkTheme: darkTheme
darkTheme: darkTheme,
logoMaxHeight: logoMaxHeight
),
onDismiss: dismissible ? { onEvent(.dismissed, [:]) } : nil
)
Expand DownExpand Up@@ -465,20 +467,27 @@ struct ClerkInlineAuthWrapperView: View {
let dismissible: Bool
let lightTheme: ClerkTheme?
let darkTheme: ClerkTheme?
let logoMaxHeight: CGFloat?

@Environment(\.colorScheme) private var colorScheme

private var themedAuthView: some View {
@ViewBuilder private var themedAuthView: some View {
let view = AuthView(mode: mode, isDismissible: dismissible)
.environment(Clerk.shared)
let theme = colorScheme == .dark ? (darkTheme ?? lightTheme) : lightTheme
return Group {
let themedView = Group {
if let theme {
view.environment(\.clerkTheme, theme)
} else {
view
}
}

if let logoMaxHeight {
themedView.clerkAppIcon(maxHeight: logoMaxHeight)
} else {
themedView
}
}

var body: some View {
Expand Down
8 changes: 7 additions & 1 deletion packages/expo/src/native/AuthView.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,12 @@ type AuthNativeEvent = NativeSyntheticEvent<Readonly<{ type: string }>>;
*
* @see {@link https://clerk.com/docs/components/authentication/sign-in} Clerk Sign-In Documentation
*/
export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss }: AuthViewProps): ReactElement {
export function AuthView({
mode = 'signInOrUp',
isDismissible = true,
logoMaxHeight,
onDismiss,
}: AuthViewProps): ReactElement {
Comment on lines +40 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
# Inspect the relevant file and nearby references
git ls-files 'packages/expo/src/native/AuthView.tsx''packages/expo/**'| sed -n '1,120p'echoecho"== AuthView.tsx =="
cat -n packages/expo/src/native/AuthView.tsx | sed -n '1,220p'echoecho"== Search for logoMaxHeight =="
rg -n "logoMaxHeight" packages/expo -S
echoecho"== Search for AuthViewProps =="
rg -n "type AuthViewProps|interface AuthViewProps|AuthViewProps" packages/expo -S

Repository: clerk/javascript

Length of output: 11533


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo"== AuthView props =="
cat -n packages/expo/src/native/AuthView.types.ts | sed -n '1,180p'echoecho"== iOS Auth view =="
cat -n packages/expo/ios/ClerkAuthNativeView.swift | sed -n '1,180p'echoecho"== Android Auth view =="
cat -n packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthViewModule.kt | sed -n '1,220p'echoecho"== Native spec =="
cat -n packages/expo/src/specs/NativeClerkAuthView.ts | sed -n '1,120p'echo
cat -n packages/expo/src/specs/NativeClerkAuthView.android.ts | sed -n '1,120p'

Repository: clerk/javascript

Length of output: 11312


Validate logoMaxHeight before forwarding it to native.

It’s passed through unchanged, and both native implementations use it directly. Reject or coerce non-finite or negative values before crossing the bridge, and add coverage for invalid inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/src/native/AuthView.tsx` around lines 40 - 45, Validate
logoMaxHeight in AuthView before passing it to either native implementation:
reject or coerce values that are non-finite or negative, while preserving valid
values. Add coverage for non-finite and negative inputs, confirming invalid
values never cross the native bridge.

Source: Coding guidelines

const handleAuthEvent = useCallback(
(event: AuthNativeEvent) => {
if (event.nativeEvent.type === 'dismissed') {
Expand All@@ -64,6 +69,7 @@ export function AuthView({ mode = 'signInOrUp', isDismissible = true, onDismiss
style={{ flex: 1 }}
mode={mode}
isDismissible={isDismissible}
logoMaxHeight={logoMaxHeight}
onAuthEvent={handleAuthEvent}
/>
);
Expand Down
7 changes: 7 additions & 0 deletions packages/expo/src/native/AuthView.types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,13 @@ export interface AuthViewProps {
*/
isDismissible?: boolean;

/**
* Maximum height of the Clerk application logo, in density-independent pixels.
*
* @default 44
*/
logoMaxHeight?: number;

/**
* Called when the native authentication view requests dismissal.
*
Expand Down
12 changes: 11 additions & 1 deletion packages/expo/src/native/__tests__/AuthView.test.tsx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { render } from '@testing-library/react';
import React from 'react';
import { describe, expect, test, vi } from 'vitest';
import { beforeEach, describe, expect, test, vi } from 'vitest';

import { AuthView } from '../AuthView';

Expand DownExpand Up@@ -30,6 +30,16 @@ vi.mock('react-native', () => {
});

describe('AuthView', () => {
beforeEach(() => {
mocks.NativeClerkAuthView.mockClear();
});

test('passes logoMaxHeight to the native auth view', () => {
render(<AuthView logoMaxHeight={64} />);

expect(mocks.NativeClerkAuthView.mock.calls[0]?.[0]).toMatchObject({ logoMaxHeight: 64 });
});

test('calls onDismiss when the native auth view emits dismissed', () => {
const onDismiss = vi.fn();

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.android.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
1 change: 1 addition & 0 deletions packages/expo/src/specs/NativeClerkAuthView.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ type AuthEvent = Readonly<{ type: string }>;
interface NativeProps extends ViewProps {
mode?: string;
isDismissible?: boolean;
logoMaxHeight?: number;
onAuthEvent?: (event: NativeSyntheticEvent<AuthEvent>) => void;
}

Expand Down
Loading