Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feat: QR code #90

Merged
merged 1 commit into from
Sep 25, 2024
Merged

Feat: QR code #90

merged 1 commit into from
Sep 25, 2024

Conversation

yassinedorbozgithub
Copy link
Collaborator

@yassinedorbozgithub yassinedorbozgithub commented Sep 25, 2024

Summary by CodeRabbit

  • New Features

    • Introduced new styled components: StyledDialogActions, StyledDialogContent, and StyledDialogTitle for enhanced dialog styling.
    • Added a QRCodeDialog component for displaying and copying QR codes and associated URLs.
    • Enhanced LinksTable component with QR code support and refined action handling.
  • Bug Fixes

    • Refactored AddLinkDialog to utilize shared styled components, improving maintainability.
  • Chores

    • Added a new method getQrCode to the API utility for generating QR codes for share links.

@yassinedorbozgithub yassinedorbozgithub self-assigned this Sep 25, 2024
@yassinedorbozgithub yassinedorbozgithub linked an issue Sep 25, 2024 that may be closed by this pull request
Copy link

coderabbitai bot commented Sep 25, 2024

Walkthrough

This pull request introduces several new styled components for dialog elements, including StyledDialogActions, StyledDialogContent, and StyledDialogTitle, enhancing the visual consistency of dialogs in the application. Additionally, it refactors the AddLinkDialog component to use these new styled components. A new QRCodeDialog component is added, which handles the display and copying of QR codes. Furthermore, the ApiSHLink class is updated with a method to fetch QR codes, and the LinksTable component is modified to integrate QR code functionality.

Changes

File Path Change Summary
src/app/components/StyledDialogActions.tsx New styled component StyledDialogActions created with specific padding and background gradient.
src/app/components/StyledDialogContent.tsx New styled component StyledDialogContent created with flex display and margin properties.
src/app/components/StyledDialogTitle.tsx New styled component StyledDialogTitle created with a background gradient for dialog titles.
src/app/shared-links/Components/AddLinkDialog.tsx Removed styled components from AddLinkDialog and imported them from the new shared components.
src/app/shared-links/Components/LinksTable.tsx Modified LinksTable to include QR code functionality, updated action columns, and refactored interface and function signatures.
src/app/shared-links/Components/QRCodeDialog.tsx New component QRCodeDialog created to display and manage QR codes, including buttons for copying the QR code and URL.
src/app/utils/api.class.ts Added asynchronous method getQrCode to fetch QR code images for specific links.

Possibly related PRs

  • Implemented qrcode and added hash to encryption #48: The main PR introduces a new styled component for dialog actions, while this PR implements QR code functionality, which may involve similar dialog components for displaying QR codes.
  • Disable links #84: This PR modifies the LinksTable component, which is relevant as the main PR also introduces a new styled component that could be used within the same context of displaying or managing links.

Suggested reviewers

  • BMartinos

Poem

🐇 In a world of links and codes,
A dialog blooms, where style explodes.
With QR magic, we now can share,
A simple click, and joy is there!
Hopping through changes, oh what a sight,
Our app's now brighter, a true delight! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Collaborator

@BMartinos BMartinos left a comment

Choose a reason for hiding this comment

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

Overall looks good to me 👍 🚀

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Outside diff range and nitpick comments (6)
src/app/components/StyledDialogContent.tsx (2)

3-8: LGTM: Component definition is correct. Consider using theme values.

The StyledDialogContent component is well-defined and follows best practices for styled components. The CSS properties create a flex container with vertical alignment, which is suitable for dialog content.

Consider using theme values for gap and margin instead of hard-coded pixel values. This can improve consistency across the application and make it easier to maintain a cohesive design system. For example:

export const StyledDialogContent = styled('div')(({ theme }) => ({
  gap: theme.spacing(2), // Assuming 1 spacing unit is 8px
  margin: theme.spacing(2),
  display: 'flex',
  flexDirection: 'column',
}));

This change would require the component to be wrapped in a ThemeProvider if it's not already.


1-8: Overall, the implementation looks good and aligns with the PR objective.

The StyledDialogContent component provides a structured layout for dialog content, which could be useful for displaying QR code-related information. The implementation is clean and follows best practices for styled components in React with Material-UI.

As this component might be used in multiple places related to the QR code feature, consider placing it in a shared components folder if it's not already there. This would promote reusability and maintain a clear architecture for your application.

src/app/components/StyledDialogTitle.tsx (1)

3-5: LGTM: Styled component is well-defined with a minor suggestion.

The StyledDialogTitle component is correctly defined and exported. The linear gradient background adds a subtle visual enhancement to the dialog title.

For consistency, consider using the same color notation for both colors in the gradient. You could either:

  1. Use HSL for both:
- backgroundImage: 'linear-gradient(to bottom, hsla(0, 0%, 90%, .05), #e6e6e6)',
+ backgroundImage: 'linear-gradient(to bottom, hsla(0, 0%, 90%, .05), hsl(0, 0%, 90%))',
  1. Or use HEX for both:
- backgroundImage: 'linear-gradient(to bottom, hsla(0, 0%, 90%, .05), #e6e6e6)',
+ backgroundImage: 'linear-gradient(to bottom, #e6e6e60d, #e6e6e6)',

This change would improve code consistency and readability.

src/app/shared-links/Components/AddLinkDialog.tsx (2)

Line range hint 19-21: Consider moving removeUndefinedValues to a utility file

The removeUndefinedValues function is a generic utility that could be useful in other parts of the application. Consider moving it to a separate utility file to promote reusability and keep this component file focused on its primary responsibility.


Line range hint 46-53: Improve error handling in onSubmitForm

The current error handling in onSubmitForm only logs the error to the console. Consider adding user-friendly error feedback, such as displaying an error message to the user or using a toast notification system. This would provide a better user experience when link creation fails.

Example improvement:

const onSubmitForm = async (data: TCreateSHLinkDto) => {
  try {
    const transformedData = removeUndefinedValues(data);
    await apiSharedLink.createLink(transformedData);
    callback?.();
    // Add success notification here
  } catch (error) {
    console.error('Failed to create link:', error);
    // Add user-friendly error notification here
  }
};
src/app/shared-links/Components/QRCodeDialog.tsx (1)

22-24: Consider removing unused type definition.

The TCreateSHLinkDto type is defined but not used within this file. If it's not needed for this component, consider removing it to keep the code clean and focused.

If you decide to keep it, consider moving it to a separate types file if it's used across multiple components.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 81439e1 and fc601c8.

📒 Files selected for processing (7)
  • src/app/components/StyledDialogActions.tsx (1 hunks)
  • src/app/components/StyledDialogContent.tsx (1 hunks)
  • src/app/components/StyledDialogTitle.tsx (1 hunks)
  • src/app/shared-links/Components/AddLinkDialog.tsx (1 hunks)
  • src/app/shared-links/Components/LinksTable.tsx (7 hunks)
  • src/app/shared-links/Components/QRCodeDialog.tsx (1 hunks)
  • src/app/utils/api.class.ts (1 hunks)
🔇 Additional comments not posted (10)
src/app/components/StyledDialogContent.tsx (1)

1-1: LGTM: Import statement is correct.

The import statement for the styled function from '@mui/material' is correct and follows best practices.

src/app/components/StyledDialogTitle.tsx (2)

1-1: LGTM: Import statement is correct and follows best practices.

The import statement correctly imports the necessary components from '@mui/material' and uses destructuring for cleaner code.


1-5: Overall, the implementation looks good. Could you provide more context about the QR code feature?

The StyledDialogTitle component is well-implemented and will provide a visually appealing gradient background for dialog titles. However, given that the PR title mentions a QR code feature, it would be helpful to understand:

  1. How this styled component fits into the larger QR code feature?
  2. Are there any specific accessibility considerations for this component in the context of QR code functionality?
  3. Will this component be used across multiple dialogs, or is it specific to QR code-related dialogs?

This additional context would help ensure that the component aligns well with the overall feature requirements and design.

src/app/components/StyledDialogActions.tsx (2)

1-1: LGTM: Imports are correct and concise.

The necessary imports from '@mui/material' are present, and there are no unused imports.


1-8: Overall assessment: Good addition with room for minor improvements

The StyledDialogActions component is a good addition that extends the Material-UI DialogActions with custom styling. The implementation is generally correct, but could benefit from the suggested improvements to enhance consistency, maintainability, and alignment with Material-UI best practices.

Consider implementing the proposed changes to make the component more robust and easier to maintain in the long run.

src/app/shared-links/Components/AddLinkDialog.tsx (2)

14-16: LGTM: Improved code organization with shared styled components

The addition of these import statements for styled components from a shared directory is a positive change. It promotes code reusability and consistency across the application. Using absolute imports is also a good practice.


Line range hint 72-106: LGTM: Correct usage of imported styled components

The imported styled components (StyledDialogTitle, StyledDialogContent, and StyledDialogActions) are correctly used in the component. This usage is consistent with the previous implementation and improves the overall structure of the code.

src/app/shared-links/Components/QRCodeDialog.tsx (2)

38-46: LGTM! Consider using null for initial qrCodeBlob state.

The component definition and state initialization look good. As a minor suggestion, consider using null instead of undefined as the initial state for qrCodeBlob. This can make it clearer that the blob is intentionally empty initially, rather than potentially undefined.

const [qrCodeBlob, setQrCodeBlob] = useState<Blob | null>(null);

1-184: Overall, well-implemented QR code dialog component with minor improvements suggested.

The QRCodeDialog component is well-structured and implements the required functionality effectively. It uses Material-UI components appropriately for styling and layout, and the QR code fetching and display logic is sound. The copy functionality for both the QR code and URL is implemented correctly.

Some areas for improvement have been suggested in previous comments:

  1. Error handling for the API call
  2. Using FileReader for base64 conversion
  3. Fixing a typo in button text
  4. Improving the copy status reset logic

Addressing these points will enhance the robustness and user experience of the component. Great job overall!

src/app/shared-links/Components/LinksTable.tsx (1)

125-125: Verify the assignment of managementToken in qrCodeData

The managementToken is set to an empty string when setting qrCodeData. If managementToken is required for the QR code functionality or used within QRCodeDialog, ensure that it is correctly retrieved and assigned. If it's unnecessary, consider removing it from qrCodeData.

Comment on lines +3 to +8
export const StyledDialogActions = styled(DialogActions)(() => ({
paddingTop: '15px',
paddingRight: '25px',
paddingBottom: '15px',
backgroundImage: 'linear-gradient(to top, hsla(0, 0%, 90%, .05), #e6e6e6)',
}));
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider the following improvements for consistency and maintainability:

  1. The padding is inconsistent. Consider adding left padding for symmetry:
   paddingTop: '15px',
   paddingRight: '25px',
   paddingBottom: '15px',
+  paddingLeft: '25px',
  1. The background gradient mixes HSL and hex color formats. For consistency, consider using a single format:
-  backgroundImage: 'linear-gradient(to top, hsla(0, 0%, 90%, .05), #e6e6e6)',
+  backgroundImage: 'linear-gradient(to top, hsla(0, 0%, 90%, 0.05), hsla(0, 0%, 90%, 1))',
  1. To improve maintainability, consider using theme variables for colors and sizes:
-export const StyledDialogActions = styled(DialogActions)(() => ({
+export const StyledDialogActions = styled(DialogActions)(({ theme }) => ({
-  paddingTop: '15px',
-  paddingRight: '25px',
-  paddingBottom: '15px',
+  padding: `${theme.spacing(2)} ${theme.spacing(3)}`,
-  backgroundImage: 'linear-gradient(to top, hsla(0, 0%, 90%, .05), #e6e6e6)',
+  backgroundImage: `linear-gradient(to top, ${theme.palette.grey[200]}0D, ${theme.palette.grey[200]})`,
}));

These changes will help maintain consistency with the Material-UI theming system and improve the overall maintainability of the component.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const StyledDialogActions = styled(DialogActions)(() => ({
paddingTop: '15px',
paddingRight: '25px',
paddingBottom: '15px',
backgroundImage: 'linear-gradient(to top, hsla(0, 0%, 90%, .05), #e6e6e6)',
}));
export const StyledDialogActions = styled(DialogActions)(({ theme }) => ({
padding: `${theme.spacing(2)} ${theme.spacing(3)}`,
backgroundImage: `linear-gradient(to top, ${theme.palette.grey[200]}0D, ${theme.palette.grey[200]})`,
}));

Comment on lines +56 to +64
async getQrCode(linkId: string, data: object) {
return await this.create({
url: `/${EEndpoint.shareLinks}/${linkId}/qrcode`,
data,
params: {
responseType: 'arraybuffer',
},
});
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance type safety and documentation for the getQrCode method.

The implementation of the getQrCode method looks good overall. However, there are a few suggestions to improve its robustness and maintainability:

  1. Add a TypeScript interface for the data parameter to ensure type safety. This will help prevent runtime errors caused by passing incorrect data.

  2. Consider adding a brief JSDoc comment to explain the method's purpose, parameters, and return type. This will improve code documentation and make it easier for other developers to understand and use the method.

  3. While error handling is likely managed by the base class, consider if there are any QR code-specific errors that might need custom handling.

Here's a suggested implementation with these improvements:

/**
 * Generates a QR code for a specific share link.
 * @param linkId - The ID of the share link.
 * @param data - The data to be encoded in the QR code.
 * @returns A Promise that resolves to an ArrayBuffer containing the QR code image data.
 */
async getQrCode(linkId: string, data: QRCodeData): Promise<ArrayBuffer> {
  try {
    const response = await this.create({
      url: `/${EEndpoint.shareLinks}/${linkId}/qrcode`,
      data,
      params: {
        responseType: 'arraybuffer',
      },
    });
    return response.data;
  } catch (error) {
    // Handle QR code-specific errors if necessary
    console.error('Error generating QR code:', error);
    throw error;
  }
}

// Define an interface for the QR code data
interface QRCodeData {
  // Add properties as needed
  size?: number;
  format?: string;
  // ... other QR code options
}

This implementation adds type safety, documentation, and basic error handling while maintaining the original functionality.

Comment on lines +48 to +61
useEffect(() => {
if (open && data?.id) {
apiSharedLink
.getQrCode(data.id, {
managementToken: data?.managementToken,
})
.then(async (response) => {
setQrCodeUrl(
`data:image/png;base64,${Buffer.from(response.data, 'binary').toString('base64')}`,
);
setQrCodeBlob(new Blob([response.data], { type: 'image/png' }));
});
}
}, [data?.id, data?.managementToken, open]);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and consider using FileReader for base64 conversion.

The QR code fetching logic looks good, but there are two areas for improvement:

  1. Error Handling: Add error handling for the API call to manage potential network issues or server errors.

  2. Base64 Conversion: In a browser environment, using Buffer might not be necessary. Consider using FileReader for base64 conversion.

Here's a suggested implementation:

useEffect(() => {
  if (open && data?.id) {
    apiSharedLink
      .getQrCode(data.id, {
        managementToken: data?.managementToken,
      })
      .then(async (response) => {
        const reader = new FileReader();
        reader.onloadend = () => {
          const base64data = reader.result;
          setQrCodeUrl(base64data as string);
        };
        reader.readAsDataURL(new Blob([response.data], { type: 'image/png' }));
        setQrCodeBlob(new Blob([response.data], { type: 'image/png' }));
      })
      .catch((error) => {
        console.error('Error fetching QR code:', error);
        // Handle error (e.g., show error message to user)
      });
  }
}, [data?.id, data?.managementToken, open]);

This approach handles potential errors and uses FileReader for base64 conversion, which is more browser-friendly.

) : (
<>
<FileCopy sx={{ fontSize: '20px', pr: '3px' }} />
Coping QR Code
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in button text.

There's a typo in the button text when copying. "Coping" should be "Copying".

Apply this diff to fix the typo:

-                    Coping QR Code
+                    Copying QR Code
-                    Coping link
+                    Copying link

Also applies to: 169-169

Comment on lines +100 to +115
onClick={async () => {
if (qrCodeBlob) {
setQrCopyStatus('loading');

await navigator.clipboard.write([
new ClipboardItem({
[qrCodeBlob.type]: qrCodeBlob,
}),
]);

setTimeout(() => {
setQrCopyStatus('copied');
setLinkCopyStatus('copy');
}, 500);
}
}}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve copy status reset logic.

The current implementation uses setTimeout to reset the copy status, which might lead to race conditions if the user clicks the button multiple times quickly. Consider using a more robust approach with useCallback and useEffect.

Here's a suggested implementation:

const resetCopyStatus = useCallback((setter: React.Dispatch<React.SetStateAction<TCopyStatus>>) => {
  setter('copy');
}, []);

useEffect(() => {
  if (QrCopyStatus === 'copied') {
    const timer = setTimeout(() => resetCopyStatus(setQrCopyStatus), 2000);
    return () => clearTimeout(timer);
  }
}, [QrCopyStatus, resetCopyStatus]);

useEffect(() => {
  if (linkCopyStatus === 'copied') {
    const timer = setTimeout(() => resetCopyStatus(setLinkCopyStatus), 2000);
    return () => clearTimeout(timer);
  }
}, [linkCopyStatus, resetCopyStatus]);

// In the onClick handlers, remove the setTimeout calls and just set the status to 'copied'

This approach ensures that the status is reset after a fixed duration, handles component unmounting, and avoids potential race conditions.

Also applies to: 143-154

<LinkOffIcon />,
handleDeactivate,
'Deactivate',
(row) => row.active,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct the isDisabled condition for the Deactivate button

The isDisabled function for the Deactivate action is currently (row) => row.active, which disables the button when the link is active. This is the opposite of the desired behavior. You likely want to disable the Deactivate button when the link is inactive.

Update the isDisabled function to reflect the correct logic:

-createActionColumn(
  ...
  'Deactivate',
-  (row) => row.active,
+  (row) => !row.active,
),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
(row) => row.active,
(row) => !row.active,

Comment on lines +170 to +178
{qrCodeDialogOpen && (
<QRCodeDialog
open={qrCodeDialogOpen}
data={qrCodeData}
onClose={() => {
setQrCodeDialogOpen(false);
}}
/>
)}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure qrCodeData is defined before rendering QRCodeDialog

If qrCodeData is undefined, passing it to QRCodeDialog may cause runtime errors. To prevent this, ensure that qrCodeData is defined before rendering the dialog.

Modify the conditional rendering to include a check for qrCodeData:

-{qrCodeDialogOpen && (
+{qrCodeDialogOpen && qrCodeData && (
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{qrCodeDialogOpen && (
<QRCodeDialog
open={qrCodeDialogOpen}
data={qrCodeData}
onClose={() => {
setQrCodeDialogOpen(false);
}}
/>
)}
{qrCodeDialogOpen && qrCodeData && (
<QRCodeDialog
open={qrCodeDialogOpen}
data={qrCodeData}
onClose={() => {
setQrCodeDialogOpen(false);
}}
/>
)}

</TableCell>
);
})}
<TableCell width={200}>
{actionColumns.map((actionColumn) => (
<Tooltip key={uuid()} title={actionColumn.tooltipTitle}>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid using dynamic keys in lists; use stable keys instead

Using uuid() as the key prop in the actionColumns.map rendering generates a new unique key on every render. This can lead to performance issues and unexpected behavior because React relies on stable keys to track elements between renders.

Consider using a stable identifier for the key, such as the index or a unique property of actionColumn that remains constant.

Possible fix:

-{actionColumns.map((actionColumn) => (
-  <Tooltip key={uuid()} title={actionColumn.tooltipTitle}>
+{actionColumns.map((actionColumn, index) => (
+  <Tooltip key={index} title={actionColumn.tooltipTitle}>

Committable suggestion was skipped due to low confidence.

@yassinedorbozgithub yassinedorbozgithub merged commit 022e9bb into main Sep 25, 2024
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

UI Show QR code
2 participants