Skip to content

Commit c7d6d9e

Browse files
authored
fix(connect-react): improve extension detection with polling and prop… (#1)
* fix(connect-react): improve extension detection with polling and proper cleanup Enhance the KleverProvider extension detection to handle cases where window.kleverWeb is injected with a delay by browser extensions. Changes: - Add exponential backoff polling (50ms to 1000ms, up to 10 attempts) - Implement proper cleanup with cancellation flag to prevent race conditions - Add detailed debug logging for extension detection attempts - Stop polling immediately when extension is detected - Handle StrictMode double-mounting correctly with cleanup function This ensures reliable extension detection in both fast and slow injection scenarios while maintaining proper React lifecycle management. Related: KLC-1882 * fix(ci): add pull-requests write permission to size-check workflow The workflow needs explicit permission to comment on pull requests. Without this, it fails with 'Resource not accessible by integration' error. * fix(ci): save bundle size report to file for PR comment The GITHUB_STEP_SUMMARY is not accessible between steps, so we now: - Write the report to both GITHUB_STEP_SUMMARY and a temporary file - Read from the file in the PR comment step - Add fallback message if report file is empty or missing * fix(connect-react): use ref for debug flag to satisfy React hooks rules The useEffect for extension detection was using config?.debug without including it in the dependency array, violating React's Rules of Hooks. Solution: Store debug flag in a ref that updates separately, allowing: - Access to latest debug value for logging - No re-runs of extension detection when debug changes - Proper React hooks compliance This prevents ESLint warnings and potential issues in strict mode.
1 parent 4fb4ab8 commit c7d6d9e

File tree

3 files changed

+102
-15
lines changed

3 files changed

+102
-15
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@klever/connect-react': patch
3+
---
4+
5+
Improve extension detection with polling and proper cleanup
6+
7+
- Add exponential backoff polling (50ms to 1000ms, up to 10 attempts) to handle delayed window.kleverWeb injection
8+
- Implement proper cleanup with cancellation flag to prevent race conditions
9+
- Add detailed debug logging for extension detection attempts
10+
- Stop polling immediately when extension is detected
11+
- Handle React StrictMode double-mounting correctly with cleanup function

.github/workflows/size-check.yml

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ on:
44
pull_request:
55
branches: [master, develop]
66

7+
permissions:
8+
contents: read
9+
pull-requests: write
10+
711
jobs:
812
size:
913
runs-on: ubuntu-latest
@@ -30,11 +34,12 @@ jobs:
3034
run: pnpm build
3135

3236
- name: Check bundle sizes
37+
id: size-check
3338
run: |
34-
echo "## Bundle Size Report" >> $GITHUB_STEP_SUMMARY
35-
echo "" >> $GITHUB_STEP_SUMMARY
36-
echo "| Package | Size | Gzipped |" >> $GITHUB_STEP_SUMMARY
37-
echo "|---------|------|---------|" >> $GITHUB_STEP_SUMMARY
39+
# Create report
40+
REPORT="## 📦 Bundle Size Report\n\n"
41+
REPORT+="| Package | Size | Gzipped |\n"
42+
REPORT+="|---------|------|---------|"
3843
3944
for pkg in packages/*/dist/index.js; do
4045
if [ -f "$pkg" ]; then
@@ -43,21 +48,37 @@ jobs:
4348
gzip_size=$(gzip -c "$pkg" | wc -c)
4449
size_kb=$((size / 1024))
4550
gzip_kb=$((gzip_size / 1024))
46-
echo "| $name | ${size_kb}KB | ${gzip_kb}KB |" >> $GITHUB_STEP_SUMMARY
51+
REPORT+="\n| $name | ${size_kb}KB | ${gzip_kb}KB |"
4752
fi
4853
done
4954
55+
# Save to step summary
56+
echo -e "$REPORT" >> $GITHUB_STEP_SUMMARY
57+
58+
# Save to output file for PR comment
59+
echo -e "$REPORT" > size-report.txt
60+
5061
- name: Comment PR
5162
uses: actions/github-script@v7
5263
if: github.event_name == 'pull_request'
5364
with:
5465
script: |
5566
const fs = require('fs');
56-
const summary = fs.readFileSync(process.env.GITHUB_STEP_SUMMARY, 'utf8');
67+
let body = '';
68+
69+
try {
70+
body = fs.readFileSync('size-report.txt', 'utf8');
71+
} catch (error) {
72+
body = '## 📦 Bundle Size Report\n\nNo bundle files found to analyze.';
73+
}
74+
75+
if (!body || body.trim() === '') {
76+
body = '## 📦 Bundle Size Report\n\nNo bundle files found to analyze.';
77+
}
5778
5879
github.rest.issues.createComment({
5980
issue_number: context.issue.number,
6081
owner: context.repo.owner,
6182
repo: context.repo.repo,
62-
body: summary
83+
body: body
6384
});

packages/connect-react/src/context.tsx

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -246,25 +246,80 @@ export function KleverProvider({ children, config }: KleverProviderProps): React
246246
[state.wallet, state.currentNetwork, state.address, createProviderWithNetwork],
247247
)
248248

249-
// Check for extension on mount
250-
React.useEffect((): void => {
251-
const checkExtension = async (): Promise<void> => {
252-
dispatch({ type: 'SET_SEARCHING_EXTENSION', searching: true })
249+
// Store debug flag in ref to avoid re-running effect when it changes
250+
const debugRef = React.useRef(config?.debug ?? false)
251+
React.useEffect(() => {
252+
debugRef.current = config?.debug ?? false
253+
}, [config?.debug])
254+
255+
// Check for extension on mount with polling
256+
// Note: Empty dependency array intentional - should only run once on mount
257+
// Uses debugRef.current to access latest debug value without triggering re-runs
258+
React.useEffect((): (() => void) => {
259+
let attempts = 0
260+
const MAX_ATTEMPTS = 10
261+
const INITIAL_DELAY = 50 // Start with 50ms
262+
const MAX_DELAY = 1000 // Cap at 1 second
263+
let timeoutId: NodeJS.Timeout | null = null
264+
let cancelled = false
265+
266+
const checkExtension = (): void => {
267+
if (cancelled) return
253268

254269
try {
255270
// Check if window.kleverWeb exists (Klever extension)
256271
const hasExtension =
257272
typeof window !== 'undefined' && 'kleverWeb' in window && window.kleverWeb !== undefined
258273

259-
dispatch({ type: 'SET_EXTENSION_INSTALLED', installed: hasExtension })
260-
} catch {
274+
if (debugRef.current) {
275+
console.log(
276+
`[KleverProvider] Extension check attempt ${attempts + 1}/${MAX_ATTEMPTS}:`,
277+
hasExtension,
278+
)
279+
}
280+
281+
if (hasExtension) {
282+
// Extension found - stop searching
283+
dispatch({ type: 'SET_EXTENSION_INSTALLED', installed: true })
284+
dispatch({ type: 'SET_SEARCHING_EXTENSION', searching: false })
285+
} else if (attempts < MAX_ATTEMPTS) {
286+
// Extension not found yet - schedule next check with exponential backoff
287+
attempts++
288+
const delay = Math.min(INITIAL_DELAY * Math.pow(1.5, attempts - 1), MAX_DELAY)
289+
290+
timeoutId = setTimeout(() => {
291+
checkExtension()
292+
}, delay)
293+
} else {
294+
// Max attempts reached - extension not found
295+
if (debugRef.current) {
296+
console.log('[KleverProvider] Extension not detected after max attempts')
297+
}
298+
dispatch({ type: 'SET_EXTENSION_INSTALLED', installed: false })
299+
dispatch({ type: 'SET_SEARCHING_EXTENSION', searching: false })
300+
}
301+
} catch (error) {
302+
if (debugRef.current) {
303+
console.error('[KleverProvider] Extension check error:', error)
304+
}
261305
dispatch({ type: 'SET_EXTENSION_INSTALLED', installed: false })
262-
} finally {
263306
dispatch({ type: 'SET_SEARCHING_EXTENSION', searching: false })
264307
}
265308
}
266309

267-
void checkExtension()
310+
// Start searching
311+
dispatch({ type: 'SET_SEARCHING_EXTENSION', searching: true })
312+
313+
// Start checking immediately
314+
checkExtension()
315+
316+
// Cleanup function to cancel pending timeouts
317+
return () => {
318+
cancelled = true
319+
if (timeoutId !== null) {
320+
clearTimeout(timeoutId)
321+
}
322+
}
268323
}, [])
269324

270325
// Auto-connect on mount if configured

0 commit comments

Comments
 (0)