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

[HOLD for payment 2024-12-11] [$250] Expense - On creating split expense via QAB, instead of next&save button split expense shown #52386

Closed
5 of 8 tasks
IuliiaHerets opened this issue Nov 12, 2024 · 33 comments
Assignees
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 External Added to denote the issue can be worked on by a contributor

Comments

@IuliiaHerets
Copy link

IuliiaHerets commented Nov 12, 2024

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Version Number: 9.0.60-0
Reproducible in staging?: Y
Reproducible in production?: Y
If this was caught on HybridApp, is this reproducible on New Expensify Standalone?: Y
Issue reported by: Applause Internal Team

Action Performed:

Pre:condition:

  1. Create a room
  2. Invite few members
  3. Create a split expense

Steps:

  1. Launch app
  2. Tap fab - split expense via QAB
  3. Enter an amount
  4. Note instead "next" button split expense button displayed
  5. Tap on split expense
  6. Tap amount
  7. Note instead "save" button split expense button displayed

Expected Result:

On creating split expense via QAB, instead of next&save button, split expense button must not be shown.

Actual Result:

On creating split expense via QAB, instead of next&save button, split expense button is shown.

Workaround:

Unknown

Platforms:

  • Android: Standalone
  • Android: HybridApp
  • Android: mWeb Chrome
  • iOS: Standalone
  • iOS: HybridApp
  • iOS: mWeb Safari
  • MacOS: Chrome / Safari
  • MacOS: Desktop

Screenshots/Videos

Bug6662094_1731384329996.Screenrecorder-2024-11-12-09-31-56-914.mp4

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~021858387880530531401
  • Upwork Job ID: 1858387880530531401
  • Last Price Increase: 2024-11-25
Issue OwnerCurrent Issue Owner: @adelekennedy
@IuliiaHerets IuliiaHerets added Daily KSv2 Bug Something is broken. Auto assigns a BugZero manager. labels Nov 12, 2024
Copy link

melvin-bot bot commented Nov 12, 2024

Triggered auto assignment to @adelekennedy (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.

@Krishna2323
Copy link
Contributor

Proposal


Please re-state the problem that we are trying to solve in this issue.

Expense - On creating split expense via QAB, instead of next&save button split expense shown

What is the root cause of that problem?

  • The manual group split can't be skipped but the button text is set to iou.splitExpense when iouType === CONST.IOU.TYPE.SPLIT is true.
    const buttonText: string = useMemo(() => {
    if (skipConfirmation) {
    if (iouType === CONST.IOU.TYPE.SPLIT) {
    return translate('iou.splitExpense');
    }
    if (iouType === CONST.IOU.TYPE.TRACK) {
    return translate('iou.trackExpense');
    }
    return translate('iou.submitExpense');
    }
    return isEditing ? translate('common.save') : translate('common.next');
    }, [skipConfirmation, iouType, isEditing, translate]);

What changes do you think we should make in order to solve the problem?


  • We should update this if block to (skipConfirmation && iouType !== CONST.IOU.TYPE.SPLIT) and remove this part.
  • For consistency we will also check IOURequestStepAmount, IOURequestStepScan & IOURequestStepDistance.

What alternative solutions did you explore? (Optional)

  • We can create shouldSkipConfirmation variable like we do in IOURequestStepDistance and use it instead of skipConfirmation directly.
  • We can also remove this check in IOURequestStepDistance .
    // For quick button actions, we'll skip the confirmation page unless the report is archived or this is a workspace
    // request and the workspace requires a category or a tag
    const shouldSkipConfirmation: boolean = useMemo(() => {
    if (!skipConfirmation || !report?.reportID) {
    return false;
    }
    return (
    iouType !== CONST.IOU.TYPE.SPLIT &&
    !ReportUtils.isArchivedRoom(report, reportNameValuePairs) &&
    !(ReportUtils.isPolicyExpenseChat(report) && ((policy?.requiresCategory ?? false) || (policy?.requiresTag ?? false)))
    );
    }, [report, skipConfirmation, policy, reportNameValuePairs, iouType]);

Result

@NJ-2020
Copy link
Contributor

NJ-2020 commented Nov 12, 2024

Edited by proposal-police: This proposal was edited at 2024-11-19 08:52:22 UTC.

Proposal

Please re-state the problem that we are trying to solve in this issue.

Expense - On creating split expense via QAB, instead of next&save button split expense shown

What is the root cause of that problem?

We will only return the save or next button if the skipConfirmation is false

const buttonText: string = useMemo(() => {
if (skipConfirmation) {
if (iouType === CONST.IOU.TYPE.SPLIT) {
return translate('iou.splitExpense');
}
if (iouType === CONST.IOU.TYPE.TRACK) {
return translate('iou.trackExpense');
}
return translate('iou.submitExpense');
}
return isEditing ? translate('common.save') : translate('common.next');

Since we split the expense from QAB, we pass skipConfirmation true here
case CONST.QUICK_ACTIONS.SPLIT_MANUAL:
case CONST.QUICK_ACTIONS.SPLIT_SCAN:
selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, quickActionReportID, undefined, true), true);
return;

App/src/libs/actions/IOU.ts

Lines 386 to 392 in cd3f30f

function clearMoneyRequest(transactionID: string, skipConfirmation = false) {
Onyx.set(`${ONYXKEYS.COLLECTION.SKIP_CONFIRMATION}${transactionID}`, skipConfirmation);
Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, null);
}
function startMoneyRequest(iouType: ValueOf<typeof CONST.IOU.TYPE>, reportID: string, requestType?: IOURequestType, skipConfirmation = false) {
clearMoneyRequest(CONST.IOU.OPTIMISTIC_TRANSACTION_ID, skipConfirmation);

What changes do you think we should make in order to solve the problem?

We should pass skipConfirmation false

case CONST.QUICK_ACTIONS.SPLIT_MANUAL:
case CONST.QUICK_ACTIONS.SPLIT_SCAN:
selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, quickActionReportID, undefined, true), true);
return;

We can also do the same approach for other request if needed

What alternative solutions did you explore? (Optional)

We can pass skipConfirmation false if it's manual/distance split

case CONST.QUICK_ACTIONS.SPLIT_MANUAL:
case CONST.QUICK_ACTIONS.SPLIT_SCAN:
selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, quickActionReportID, undefined, true), true);
return;

case CONST.QUICK_ACTIONS.SPLIT_MANUAL:
case CONST.QUICK_ACTIONS.SPLIT_DISTANCE:
    selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, quickActionReportID, undefined, false), true);
    return;
case CONST.QUICK_ACTIONS.SPLIT_SCAN:
    selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, quickActionReportID, undefined, true), true);
    return;

@bernhardoj
Copy link
Contributor

Proposal

Please re-state the problem that we are trying to solve in this issue.

When creating split expense from QAB, Split Expense is shown on the amount page Button instead of next/save.

What is the root cause of that problem?

When we select split bill from QAB, we pass skipConfirmation as true.

case CONST.QUICK_ACTIONS.SPLIT_MANUAL:
case CONST.QUICK_ACTIONS.SPLIT_SCAN:
selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, quickActionReportID, undefined, true), true);

If it's true, then we show Split Expense as the text.

const buttonText: string = useMemo(() => {
if (skipConfirmation) {
if (iouType === CONST.IOU.TYPE.SPLIT) {
return translate('iou.splitExpense');
}
if (iouType === CONST.IOU.TYPE.TRACK) {
return translate('iou.trackExpense');
}
return translate('iou.submitExpense');
}
return isEditing ? translate('common.save') : translate('common.next');
}, [skipConfirmation, iouType, isEditing, translate]);

However, pressing the button won't split the expense immediately and instead going to the confirm page. If we look at this comment, we only want to skip split if it's unconfigurable, and only scan is unconfigurable.

// Only skip confirmation when the split is not configurable, for now Smartscanned splits cannot be configured
if (iouType === CONST.IOU.TYPE.SPLIT && transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.SCAN) {
playSound(SOUNDS.DONE);

This means that we shouldn't skip confirmation for the manual amount.

What changes do you think we should make in order to solve the problem?

Since we only want to skip split scan expense, we can remove this code for split scan because it's inside the manual amount page.

if (shouldSkipConfirmation) {
// Only skip confirmation when the split is not configurable, for now Smartscanned splits cannot be configured
if (iouType === CONST.IOU.TYPE.SPLIT && transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.SCAN) {
playSound(SOUNDS.DONE);
IOU.splitBill({
participants,
currentUserLogin: currentUserPersonalDetails.login ?? '',
currentUserAccountID: currentUserPersonalDetails.accountID,
amount: backendAmount,
comment: '',
currency,
merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT,
tag: '',
category: '',
created: transaction?.created ?? '',
billable: false,
iouRequestType: CONST.IOU.REQUEST_TYPE.MANUAL,
existingSplitChatReportID: report?.reportID,
});
return;
}

And then update shouldSkipConfirmation to return false when it's split bill.

const shouldSkipConfirmation: boolean = useMemo(() => {
if (!skipConfirmation || !report?.reportID) {
return false;
}
return !(ReportUtils.isArchivedRoom(report, reportNameValuePairs) || ReportUtils.isPolicyExpenseChat(report));
}, [report, skipConfirmation, reportNameValuePairs]);

const shouldSkipConfirmation: boolean = useMemo(() => {
    if (isSplitBill || !skipConfirmation || !report?.reportID) {
        return false;
    }

    return !(ReportUtils.isArchivedRoom(report, reportNameValuePairs) || ReportUtils.isPolicyExpenseChat(report));
}, [report, skipConfirmation, reportNameValuePairs]);

@melvin-bot melvin-bot bot added the Overdue label Nov 15, 2024
@adelekennedy adelekennedy added the External Added to denote the issue can be worked on by a contributor label Nov 18, 2024
@melvin-bot melvin-bot bot changed the title Expense - On creating split expense via QAB, instead of next&save button split expense shown [$250] Expense - On creating split expense via QAB, instead of next&save button split expense shown Nov 18, 2024
Copy link

melvin-bot bot commented Nov 18, 2024

Job added to Upwork: https://www.upwork.com/jobs/~021858387880530531401

@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label Nov 18, 2024
@adelekennedy
Copy link

external

Copy link

melvin-bot bot commented Nov 18, 2024

Triggered auto assignment to Contributor-plus team member for initial proposal review - @mollfpr (External)

@melvin-bot melvin-bot bot removed the Overdue label Nov 18, 2024
@mollfpr
Copy link
Contributor

mollfpr commented Nov 19, 2024

Thank you guys for the proposals!


The manual group split can't be skipped but the button text is set to iou.splitExpense when iouType === CONST.IOU.TYPE.SPLIT is true.

@Krishna2323 The RCA should be clear in pointing out what's the start of the problem to the end. Here are some follow questions for your RCA.

  • Why the manual group can't be skipped?
  • Why the skipConfirmation value is true but the manual split can't be skipped?

For consistency we will also check IOURequestStepAmount, IOURequestStepScan & IOURequestStepDistance.

What check do we need to do?

We should update this if block to (skipConfirmation && iouType !== CONST.IOU.TYPE.SPLIT) and remove this part.

Also, the solution is not related to the main problem where the skipConfirmation has unintended value. So instead of adding an unnecessary check, we just need to have the skipConfirmation value false in the first place.


@NJ-2020 Your solution works only for the case when the QAB split expense is pressed. Is not working if we navigate to split manual from QAB split receipt/scan.


Since we only want to skip split scan expense, we can remove this code for split scan because it's inside the manual amount page.

@bernhardoj What's the initial reason for having the split scan on the manual amount page? I'm okay to remove it as long we have confirmed that the logic won't be used.

@mollfpr
Copy link
Contributor

mollfpr commented Nov 19, 2024

The alternative solution from @Krishna2323 and solution from @bernhardoj pretty much have the same approach to solving the issue, but they are ignoring the fact that skipConfirmation is true for the QAB split manual expense and it's wrong. If we decide that the QAB split manual expense can't skip the confirmation page, then we should set it that way in the first place to give clarity.

@NJ-2020
Copy link
Contributor

NJ-2020 commented Nov 19, 2024

Is not working if we navigate to split manual from QAB split receipt/scan.

@mollfpr What do you mean by that?, I think when we split manual from QAB receipt/scan, the amount menu is not displayed, because the receipt is not yet scanned

And also if I submit split scan expense, and then go to expense details > amount, the submit button showing the correct title which is Save

Alternatively we can do this

case CONST.QUICK_ACTIONS.SPLIT_MANUAL:
case CONST.QUICK_ACTIONS.SPLIT_SCAN:
selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, quickActionReportID, undefined, true), true);
return;

case CONST.QUICK_ACTIONS.SPLIT_MANUAL:
case CONST.QUICK_ACTIONS.SPLIT_DISTANCE:
    selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, quickActionReportID, undefined, false), true);
    return;
case CONST.QUICK_ACTIONS.SPLIT_SCAN:
    selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, quickActionReportID, undefined, true), true);
    return; // we will skip the confirmation page if it's split scan

Proposal Updated

  • Add alternative solution

@mollfpr
Copy link
Contributor

mollfpr commented Nov 19, 2024

@NJ-2020 Let me know the result with your solution of this step case:

  1. Go to group chat
  2. Create a scan split expense
  3. Click on the QAB split receipt
  4. Press on the manual tab

Could you confirm if the text is shown next & save?

@mollfpr
Copy link
Contributor

mollfpr commented Nov 19, 2024

@NJ-2020 Here's there result I've got when trying your solution. The text still shows split expense.

Screen.Recording.2024-11-19.at.16.09.35.mp4

The reason why this happens is because when selecting QAB split receipt where it has the skipConfirmation true and it doesn't change when tab on the manual or distance expense.

@bernhardoj
Copy link
Contributor

but they are ignoring the fact that skipConfirmation is true for the QAB split manual expense and it's wrong.

I mentioned it here that we pass skipConfirmation as true and it's not wrong. All QAB should pass it as true,
image

Including this distance QAB.

case CONST.QUICK_ACTIONS.SPLIT_DISTANCE:
selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, quickActionReportID, undefined, false), true);
return;

The skipConfirmation basically tells whether this action is a QAB or not.

shouldSkipConfirmation will be the one that decide whether it's skippable or not.

const shouldSkipConfirmation: boolean = useMemo(() => {
if (!skipConfirmation || !report?.reportID) {
return false;
}
return !(ReportUtils.isArchivedRoom(report, reportNameValuePairs) || ReportUtils.isPolicyExpenseChat(report));
}, [report, skipConfirmation, reportNameValuePairs]);

@mollfpr
Copy link
Contributor

mollfpr commented Nov 19, 2024

@bernhardoj Yes you did mention it in the RCA, but in your solution, you leave it as is.

The skipConfirmation basically tells whether this action is a QAB or not.

Could you elaborate this? Is other button quick actions without skipConfirmation true is not a QAB?

@bernhardoj
Copy link
Contributor

I'm saying every QAB should pass the skipConfirmation. Currently, only split expense QAB that passes it as false.

case CONST.QUICK_ACTIONS.SPLIT_DISTANCE:
selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, quickActionReportID, undefined, false), true);
return;

Otherwise, this will happen which you mentioned in your comment.

The reason why this happens is because when selecting QAB split receipt where it has the skipConfirmation true and it doesn't change when tab on the manual or distance expense.

but in your solution, you leave it as is.

The skipConfirmation basically tells whether this action is a QAB or not.

shouldSkipConfirmation will be the one that decide whether it's skippable or not.

Copy link

melvin-bot bot commented Nov 25, 2024

📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸

Copy link

melvin-bot bot commented Nov 26, 2024

@mollfpr @adelekennedy this issue was created 2 weeks ago. Are we close to approving a proposal? If not, what's blocking us from getting this issue assigned? Don't hesitate to create a thread in #expensify-open-source to align faster in real time. Thanks!

@mollfpr
Copy link
Contributor

mollfpr commented Nov 26, 2024

I think we can move forward now. The proposal from @bernhardoj has more explanation on the RCA and I think it has the correct RCA. So the proposal looks good to me!

🎀 👀 🎀 C+ reviewed!

@melvin-bot melvin-bot bot removed the Overdue label Nov 26, 2024
@melvin-bot melvin-bot bot added Reviewing Has a PR in review Weekly KSv2 and removed Daily KSv2 labels Nov 27, 2024
@bernhardoj
Copy link
Contributor

PR is ready

cc: @mollfpr

@melvin-bot melvin-bot bot added Weekly KSv2 Awaiting Payment Auto-added when associated PR is deployed to production and removed Weekly KSv2 labels Dec 4, 2024
@melvin-bot melvin-bot bot changed the title [$250] Expense - On creating split expense via QAB, instead of next&save button split expense shown [HOLD for payment 2024-12-11] [$250] Expense - On creating split expense via QAB, instead of next&save button split expense shown Dec 4, 2024
@melvin-bot melvin-bot bot removed the Reviewing Has a PR in review label Dec 4, 2024
Copy link

melvin-bot bot commented Dec 4, 2024

Reviewing label has been removed, please complete the "BugZero Checklist".

Copy link

melvin-bot bot commented Dec 4, 2024

The solution for this issue has been 🚀 deployed to production 🚀 in version 9.0.70-9 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

If no regressions arise, payment will be issued on 2024-12-11. 🎊

For reference, here are some details about the assignees on this issue:

  • @mollfpr requires payment through NewDot Manual Requests
  • @bernhardoj requires payment through NewDot Manual Requests

Copy link

melvin-bot bot commented Dec 4, 2024

@mollfpr @adelekennedy @mollfpr The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]

@melvin-bot melvin-bot bot added Daily KSv2 and removed Weekly KSv2 labels Dec 10, 2024
Copy link

melvin-bot bot commented Dec 11, 2024

Payment Summary

Upwork Job

BugZero Checklist (@adelekennedy)

  • I have verified the correct assignees and roles are listed above and updated the neccesary manual offers
  • I have verified that there are no duplicate or incorrect contracts on Upwork for this job (https://www.upwork.com/ab/applicants/1858387880530531401/hired)
  • I have paid out the Upwork contracts or cancelled the ones that are incorrect
  • I have verified the payment summary above is correct

@melvin-bot melvin-bot bot added the Overdue label Dec 12, 2024
@adelekennedy
Copy link

BugZero Checklist:

  • [Contributor] Classify the bug:
Bug classification

Source of bug:

  • 1a. Result of the original design (eg. a case wasn't considered)
  • 1b. Mistake during implementation
  • 1c. Backend bug
  • 1z. Other:

Where bug was reported:

  • 2a. Reported on production (eg. bug slipped through the normal regression and PR testing process on staging)
  • 2b. Reported on staging (eg. found during regression or PR testing)
  • 2d. Reported on a PR
  • 2z. Other:

Who reported the bug:

  • 3a. Expensify user
  • 3b. Expensify employee
  • 3c. Contributor
  • 3d. QA
  • 3z. Other:
  • [Contributor] The offending PR has been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.

    Link to comment:

  • [Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.

    Link to discussion:

  • [Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.

Regression Test Proposal Template
  • [BugZero Assignee] Create a GH issue for creating/updating the regression test once above steps have been agreed upon.

    Link to issue:

Regression Test Proposal

Precondition:

Test:

Do we agree 👍 or 👎

@melvin-bot melvin-bot bot removed the Overdue label Dec 12, 2024
@adelekennedy
Copy link

@mollfpr for the steps above!

@adelekennedy
Copy link

adelekennedy commented Dec 12, 2024

Contributor: @bernhardoj owed $250.00 via NewDot
Contributor+: @mollfpr owed $250.00 via NewDot

@bernhardoj
Copy link
Contributor

Requested in ND.

@JmillsExpensify
Copy link

$250 approved for @bernhardoj

@melvin-bot melvin-bot bot added the Overdue label Dec 16, 2024
Copy link

melvin-bot bot commented Dec 16, 2024

@mollfpr, @stitesExpensify, @bernhardoj, @adelekennedy Whoops! This issue is 2 days overdue. Let's get this updated quick!

@mollfpr
Copy link
Contributor

mollfpr commented Dec 16, 2024

BugZero Checklist:

  • [Contributor] Classify the bug:
Bug classification

Source of bug:

  • 1a. Result of the original design (eg. a case wasn't considered)
  • 1b. Mistake during implementation
  • 1c. Backend bug
  • 1z. Other:

Where bug was reported:

  • 2a. Reported on production (eg. bug slipped through the normal regression and PR testing process on staging)
  • 2b. Reported on staging (eg. found during regression or PR testing)
  • 2d. Reported on a PR
  • 2z. Other:

Who reported the bug:

  • 3a. Expensify user
  • 3b. Expensify employee
  • 3c. Contributor
  • 3d. QA
  • 3z. Other:
  • [Contributor] The offending PR has been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.

    Link to comment: https://github.com/Expensify/App/pull/40386/files#r1886532266

  • [Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.

    Link to discussion: Not a critical

  • [Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.

  • [BugZero Assignee] Create a GH issue for creating/updating the regression test once above steps have been agreed upon.

    Link to issue:

Regression Test Proposal

Precondition:

  • Create a split expense in a room or group chat

Test:

  1. Tap fab - split expense via QAB
  2. Enter an amount
  3. Verify the button text is "Next" and press it
  4. Press the amount field
  5. Verify the button text is "Save"

Do we agree 👍 or 👎

@adelekennedy
Copy link

@garrettmknight
Copy link
Contributor

$250 approved for @mollfpr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 External Added to denote the issue can be worked on by a contributor
Projects
None yet
Development

No branches or pull requests

9 participants