-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unify all interactions with Clipboard & stub all Clipboard methods during tests #2365
Changes from all commits
2ecd63a
81adb5f
6906d33
2c47331
5342288
2a59e05
fcd7f4f
8292b6e
ee17ef8
8711660
5127156
9042935
806d563
bd60c2e
6cd77fe
2bb6017
2d1c80e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{{yield (fn (perform this.copyTask)) this.hasRecentlyCopied}} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,54 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
import Component from '@glimmer/component'; | ||||||||||||||||||||||||||||||||||||||||||||||||
import { action } from '@ember/object'; | ||||||||||||||||||||||||||||||||||||||||||||||||
import { waitFor } from '@ember/test-waiters'; | ||||||||||||||||||||||||||||||||||||||||||||||||
import { tracked } from '@glimmer/tracking'; | ||||||||||||||||||||||||||||||||||||||||||||||||
import { task, timeout } from 'ember-concurrency'; | ||||||||||||||||||||||||||||||||||||||||||||||||
import config from 'codecrafters-frontend/config/environment'; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
export type OnCopiedCallback = () => void | Promise<void>; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
export interface CopyableAnythingSignature { | ||||||||||||||||||||||||||||||||||||||||||||||||
Args: { | ||||||||||||||||||||||||||||||||||||||||||||||||
value?: string | null; | ||||||||||||||||||||||||||||||||||||||||||||||||
isDisabled?: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||
onCopied?: OnCopiedCallback; | ||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||
Blocks: { | ||||||||||||||||||||||||||||||||||||||||||||||||
default: [() => void, boolean]; | ||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||
Element: null; | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
export default class CopyableAnythingComponent extends Component<CopyableAnythingSignature> { | ||||||||||||||||||||||||||||||||||||||||||||||||
@tracked hasRecentlyCopied: boolean = false; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
@action | ||||||||||||||||||||||||||||||||||||||||||||||||
@waitFor | ||||||||||||||||||||||||||||||||||||||||||||||||
async copy(): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||
if (this.args.isDisabled) { | ||||||||||||||||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
await navigator.clipboard.writeText(String(this.args.value)); | ||||||||||||||||||||||||||||||||||||||||||||||||
this.hasRecentlyCopied = true; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||
if (this.args.onCopied) { | ||||||||||||||||||||||||||||||||||||||||||||||||
await this.args.onCopied(); | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
} finally { | ||||||||||||||||||||||||||||||||||||||||||||||||
await timeout(config.x.copyConfirmationTimeout); | ||||||||||||||||||||||||||||||||||||||||||||||||
this.hasRecentlyCopied = false; | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+35
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Protect timeout from callback errors. If try {
if (this.args.onCopied) {
await this.args.onCopied();
}
+ } catch (error) {
+ console.error('onCopied callback failed:', error);
} finally {
- await timeout(config.x.copyConfirmationTimeout);
- this.hasRecentlyCopied = false;
+ try {
+ await timeout(config.x.copyConfirmationTimeout);
+ } finally {
+ this.hasRecentlyCopied = false;
+ }
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
copyTask = task({ keepLatest: true }, async (): Promise<void> => { | ||||||||||||||||||||||||||||||||||||||||||||||||
await this.copy(); | ||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
declare module '@glint/environment-ember-loose/registry' { | ||||||||||||||||||||||||||||||||||||||||||||||||
export default interface Registry { | ||||||||||||||||||||||||||||||||||||||||||||||||
CopyableAnything: typeof CopyableAnythingComponent; | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,43 +1,16 @@ | ||
import Component from '@glimmer/component'; | ||
import config from 'codecrafters-frontend/config/environment'; | ||
import { action } from '@ember/object'; | ||
import { later } from '@ember/runloop'; | ||
import { tracked } from '@glimmer/tracking'; | ||
|
||
interface Signature { | ||
Element: HTMLDivElement; | ||
|
||
Args: { | ||
backgroundColor?: 'gray' | 'white'; | ||
code: string; | ||
onCopyButtonClick?: () => void; | ||
onCopyButtonClick?: () => void | Promise<void>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Inconsistent callback signatures found across copyable components The Promise return type is not consistently implemented across copyable components:
The callback signature should be unified across all copyable components to maintain consistency, especially since this is part of a refactor to unify clipboard interactions. 🔗 Analysis chainLGTM: Method signature update allows for async callbacks. The change to allow Let's verify this change is consistently applied across other copyable components: The previous ast-grep pattern might have been too specific. Let's try a broader search to:
The previous search was too broad. Let's focus specifically on the copyable components to verify the consistency of the callback signatures. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Check if other copyable components have similar callback signatures
# Expect: Consistent Promise<void> | void signatures across components
ast-grep --pattern 'interface $_ {
$$$
onCopyButtonClick?: () => $ret;
$$$
}'
Length of output: 85 Script: #!/bin/bash
# Let's try a broader search for any interfaces or types with copy-related callbacks
# First, let's find all TypeScript files that might contain copyable components
fd -e ts -e tsx | xargs rg -l "copy|clipboard" --ignore-case
# Then let's search for method signatures related to copy operations
rg "(?:on[A-Z][a-zA-Z]*Copy|copy.*\?:.*=>.*)" -A 2 -B 2
Length of output: 2108 Script: #!/bin/bash
# Let's specifically look at the copyable components implementations
rg -A 5 -B 5 "onCopyButtonClick|CopyableAnything|CopyableCode" app/components/copyable-*.ts
# Also check the interfaces/types in these files
ast-grep --pattern 'interface $_ {
$$$
}' app/components/copyable-*.ts
Length of output: 8455 |
||
}; | ||
} | ||
|
||
export default class CopyableCodeComponent extends Component<Signature> { | ||
@tracked codeWasCopiedRecently: boolean = false; | ||
|
||
@action | ||
handleCopyButtonClick() { | ||
if (config.environment !== 'test') { | ||
navigator.clipboard.writeText(this.args.code); | ||
} | ||
|
||
this.codeWasCopiedRecently = true; | ||
|
||
later( | ||
this, | ||
() => { | ||
this.codeWasCopiedRecently = false; | ||
}, | ||
1000, | ||
); | ||
|
||
if (this.args.onCopyButtonClick) { | ||
this.args.onCopyButtonClick(); | ||
} | ||
} | ||
} | ||
export default class CopyableCodeComponent extends Component<Signature> {} | ||
|
||
declare module '@glint/environment-ember-loose/registry' { | ||
export default interface Registry { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for clipboard API.
The clipboard API can fail in various scenarios (permissions, secure context). Consider adding try-catch and user feedback.
📝 Committable suggestion