Skip to content

[$175] October 2026 product update - Add persistent bottom-right product marketing window #104426

Open

Description

@MelvinBot

Background

I'm helping create the persistent window for the October 2026 Product Update feature: https://github.com/Expensify/Expensify/issues/683753

Deliverables

Employee/Member Window Variant

Admin Window Variant

Image (same for both variants):

Image
Upwork Automation - Do Not Edit

Activity

  1. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Oct 19, 2026
  2. melvin-bot commented on Oct 19, 2026

    @melvin-bot

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

  3. changed the title [-]October 2026 product update - Add persistent bottom-right product marketing window[/-] [+][$175] October 2026 product update - Add persistent bottom-right product marketing window[/+] on Oct 19, 2026
  4. melvin-bot commented on Oct 19, 2026

    @melvin-bot
  5. camesenin commented on Oct 19, 2026

    @camesenin

    Proposal

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

    Add the persistent bottom-right product marketing window for the October 2026 Product Update ("Mileage on cruise control" / "Mileage handled automatically"), supporting two audience variants:

    1. Employee/Member Window Variant (Non-Admin Members):
      • Title: Mileage on cruise control
      • Subtext: Jump straight to your frequent trips and fix a trip’s stop location without starting over.
    2. Admin Window Variant (Admins):
      • Title: Mileage handled automatically
      • Subtext: Mileage rates that keep themselves current, plus total control over how trips get logged.
    3. Buttons & Link (Both variants): [Dismiss] and [Learn more] linking to https://help.expensify.com/articles/new-expensify/reports-and-expenses/Distance-Expenses.
    4. Visual Asset: Marketing promo image provided in the issue description.

    What is the root cause of that problem?

    This is a feature addition on the persistent product marketing window infrastructure (issue #95791 / PR #96141). Currently, ACTIVE_PRODUCT_MARKETING_ANNOUNCEMENT in src/libs/ProductMarketingWindowUtils.ts statically serves the September 2026 announcement (PRODUCT_UPDATE_SEPTEMBER_2026) and does not dynamically distinguish between Admin and Non-Admin (Member) audiences.

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

    1. Add Constant and Asset:

      • In src/CONST/index.ts:
        • Add PRODUCT_UPDATE_OCTOBER_2026: 'productUpdateOctober2026' to CONST.MARKETING_WINDOW_UPDATE_KEYS.
        • Add DISTANCE_EXPENSES_HELP_URL: 'https://help.expensify.com/articles/new-expensify/reports-and-expenses/Distance-Expenses'.
      • In assets/images/:
        • Add october2026-promo.png (using the provided visual asset).
    2. Add Localization Keys:

      • In src/languages/en.ts (and mirror across other language files):
        productMarketingWindow: {
            october2026: {
                member: {
                    heading: 'Mileage on cruise control',
                    body: 'Jump straight to your frequent trips and fix a trip’s stop location without starting over.',
                },
                admin: {
                    heading: 'Mileage handled automatically',
                    body: 'Mileage rates that keep themselves current, plus total control over how trips get logged.',
                },
            },
        },
    3. Update Announcement Logic with Role-Based Resolution in ProductMarketingWindowUtils.ts:

      • Define both variant announcements sharing updateKey: CONST.MARKETING_WINDOW_UPDATE_KEYS.PRODUCT_UPDATE_OCTOBER_2026, ctaUrl: CONST.DISTANCE_EXPENSES_HELP_URL, and image October2026PromoImage.
      • Export a helper function getActiveProductMarketingAnnouncement(isAdmin: boolean): ProductMarketingAnnouncement:
        function getActiveProductMarketingAnnouncement(isAdmin: boolean): ProductMarketingAnnouncement {
            return {
                updateKey: CONST.MARKETING_WINDOW_UPDATE_KEYS.PRODUCT_UPDATE_OCTOBER_2026,
                visual: {type: 'image', source: October2026PromoImage},
                heading: isAdmin ? 'productMarketingWindow.october2026.admin.heading' : 'productMarketingWindow.october2026.member.heading',
                body: isAdmin ? 'productMarketingWindow.october2026.admin.body' : 'productMarketingWindow.october2026.member.body',
                ctaLabel: 'common.learnMore',
                ctaUrl: CONST.DISTANCE_EXPENSES_HELP_URL,
            };
        }
    4. Integrate Role Check in ProductMarketingWindowManager.tsx:

      • Use useHasActiveAdminPolicies():
        const hasActiveAdminPolicies = useHasActiveAdminPolicies();
        const announcement = getActiveProductMarketingAnnouncement(hasActiveAdminPolicies);
      • Why useHasActiveAdminPolicies() over useIsPaidPolicyAdmin()?
        The audience specification requires that anyone who is an admin on at least one workspace receives the admin variant (and member variant only if not an admin on any workspace). useHasActiveAdminPolicies() evaluates active workspaces across both free and paid policies, perfectly matching the design doctrine.
      • Keep the dismissal key logic (lastDismissedMarketingWindow === updateKey) consistent so dismissing either variant persists under productUpdateOctober2026 across subsequent role changes.
    5. Update Unit Tests:

      • In tests/unit/ProductMarketingWindowUtilsTest.ts: Update UNKNOWN_UPDATE_KEY from 'productUpdateOctober2026' to 'productUpdateNovember2026' so the stale/future client detection test continues to test an unregistered future key.
      • In tests/unit/ProductMarketingWindowTest.tsx: Add test asserting admin copy when hasActiveAdminPolicies is true, and member copy when false.

    What alternative solutions did you explore? (Optional)

    • Using useIsPaidPolicyAdmin(): Excludes admins of free workspaces who should also receive the Admin variant.
    • Separate updateKeys for admin and member: Rejected because dismissing the October update should dismiss the monthly product update for the user permanently, preventing repeated popups if their workspace role changes.

    Contributor details
    Your Expensify account email: camesenin@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/~0170d6b02e9440e604

  6. melvin-bot commented on Oct 19, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  7. janiya2 commented on Oct 20, 2026

    @janiya2

    Contributor details
    Your Expensify account email: janithad2@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/janithad

    Proposal

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

    Add the persistent bottom-right product marketing window for the October 2026 Product Update ("Mileage on cruise control" / "Mileage handled automatically") supporting audience-targeted variants:

    1. Employee/Member Window Variant (Non-Admin Members):
      • Title: Mileage on cruise control
      • Subtext: Jump straight to your frequent trips and fix a trip’s stop location without starting over.
    2. Admin Window Variant (Admins):
      • Title: Mileage handled automatically
      • Subtext: Mileage rates that keep themselves current, plus total control over how trips get logged.
    3. Common Actions & CTA destination:
      • Buttons: [Dismiss] and [Learn more]
      • CTA destination: https://help.expensify.com/articles/new-expensify/reports-and-expenses/Distance-Expenses
    4. Visual Asset:
      • Marketing visual image provided in the issue description.

    What is the root cause of that problem?

    Currently, ACTIVE_PRODUCT_MARKETING_ANNOUNCEMENT in src/libs/ProductMarketingWindowUtils.ts statically serves the September 2026 announcement (PRODUCT_UPDATE_SEPTEMBER_2026) and only supports a single static heading/body without audience-role differentiation. Furthermore:

    • The October 2026 update key and help article URL are not yet defined in src/CONST/index.ts.
    • The new promotional image asset (october2026-promo.png) and translation strings need to be integrated.
    • Audience targeting (Admins vs Non-Admin Members where admin role prevails) needs to be resolved dynamically via policy state.

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

    1. Constants & Asset Integration

    • Asset: Save the provided promotional graphic to assets/images/october2026-promo.png.
    • src/CONST/index.ts:
      • Add PRODUCT_UPDATE_OCTOBER_2026: 'productUpdateOctober2026' to CONST.MARKETING_WINDOW_UPDATE_KEYS.
      • Add DISTANCE_EXPENSES_HELP_URL: 'https://help.expensify.com/articles/new-expensify/reports-and-expenses/Distance-Expenses'.

    2. Localized Strings (src/languages/en.ts and other language files)

    In src/languages/en.ts (and corresponding fallback entries in other locale files):

    productMarketingWindow: {
        october2026: {
            member: {
                heading: 'Mileage on cruise control',
                body: 'Jump straight to your frequent trips and fix a trip’s stop location without starting over.',
            },
            admin: {
                heading: 'Mileage handled automatically',
                body: 'Mileage rates that keep themselves current, plus total control over how trips get logged.',
            },
        },
    },

    3. Update Announcement Configuration in src/libs/ProductMarketingWindowUtils.ts

    • Define the October 2026 announcement with audience-targeted content while maintaining a single unified updateKey: CONST.MARKETING_WINDOW_UPDATE_KEYS.PRODUCT_UPDATE_OCTOBER_2026:
    import October2026PromoImage from '@assets/images/october2026-promo.png';
    
    type ProductMarketingAnnouncementContent = {
        heading: TranslationPaths;
        body: TranslationPaths;
    };
    
    const OCTOBER_2026_ANNOUNCEMENT_CONTENT = {
        admin: {
            heading: 'productMarketingWindow.october2026.admin.heading' as TranslationPaths,
            body: 'productMarketingWindow.october2026.admin.body' as TranslationPaths,
        },
        member: {
            heading: 'productMarketingWindow.october2026.member.heading' as TranslationPaths,
            body: 'productMarketingWindow.october2026.member.body' as TranslationPaths,
        },
    } as const;
    
    function getActiveProductMarketingAnnouncement(isAdmin: boolean): ProductMarketingAnnouncement | null {
        const content = isAdmin ? OCTOBER_2026_ANNOUNCEMENT_CONTENT.admin : OCTOBER_2026_ANNOUNCEMENT_CONTENT.member;
    
        return {
            updateKey: CONST.MARKETING_WINDOW_UPDATE_KEYS.PRODUCT_UPDATE_OCTOBER_2026,
            visual: {type: 'image', source: October2026PromoImage},
            heading: content.heading,
            body: content.body,
            ctaLabel: 'common.learnMore',
            ctaUrl: CONST.DISTANCE_EXPENSES_HELP_URL,
        };
    }
    
    const ACTIVE_PRODUCT_MARKETING_ANNOUNCEMENT = getActiveProductMarketingAnnouncement(false);
    
    export {ACTIVE_PRODUCT_MARKETING_ANNOUNCEMENT, getActiveProductMarketingAnnouncement, isProductMarketingAnnouncementDismissed};

    4. Connect Audience Resolution in ProductMarketingWindowManager.tsx

    • In src/components/ProductMarketingWindow/ProductMarketingWindowManager.tsx, consume useHasActiveAdminPolicies():
    import useHasActiveAdminPolicies from '@hooks/useHasActiveAdminPolicies';
    import {getActiveProductMarketingAnnouncement, isProductMarketingAnnouncementDismissed} from '@libs/ProductMarketingWindowUtils';
    • In ProductMarketingWindowManager:
    const hasActiveAdminPolicies = useHasActiveAdminPolicies();
    const announcement = getActiveProductMarketingAnnouncement(hasActiveAdminPolicies);
    • Why useHasActiveAdminPolicies()?
      The audience specification in [Due for payment 2026-08-29] [$250] Add persistent bottom-right product marketing window #95791 dictates:

      • Non-Admin Member (not an admin on any workspace) -> Member variant.
      • Admin (admin on at least one workspace) -> Admin variant.
      • Both (member on some workspaces, admin on others) -> Admin prevails.
        useHasActiveAdminPolicies() uses hasActiveAdminPoliciesSelector(policies, login) which cleanly evaluates active workspaces where the user has an admin role across both free and paid policies (unlike useIsPaidPolicyAdmin() which excludes free workspace admins).
    • Dismissal logic (dismissMarketingWindow(announcement.updateKey)) remains unchanged and persists productUpdateOctober2026. This ensures that if an employee's role changes (e.g. member promoted to admin), a dismissed October announcement remains dismissed.

    5. Unit & UI Test Updates

    1. tests/unit/ProductMarketingWindowUtilsTest.ts:
      • Update UNKNOWN_UPDATE_KEY: In this file, UNKNOWN_UPDATE_KEY was hardcoded as 'productUpdateOctober2026'. Since productUpdateOctober2026 is now an active recognized key in CONST.MARKETING_WINDOW_UPDATE_KEYS, update UNKNOWN_UPDATE_KEY to 'productUpdateNovember2026' so the test asserting stale/unknown client detection continues to test an unregistered future key.
    2. tests/unit/ProductMarketingWindowTest.tsx:
      • Update baseline announcement assertions to verify October 2026 promo image and translation keys.
      • Add a test verifying that when hasActiveAdminPolicies is true, the admin heading/body is rendered, and when false, the member heading/body is rendered.
      • Verify that dismissing or clicking the CTA persists productUpdateOctober2026 and opens CONST.DISTANCE_EXPENSES_HELP_URL.
    3. tests/ui/ProductMarketingWindowNavigationTest.tsx:
      • Update the test translation lookup windowHeading to match the active announcement translation path.

    What alternative solutions did you explore? (Optional)

    • Using useIsPaidPolicyAdmin(): Rejected because the audience criteria is simply "admin of at least one workspace", not restricted to paid policy types. Free policies with admin roles must also receive the admin variant.
    • Using separate update keys for Admin vs Member (e.g., productUpdateOctober2026Admin and productUpdateOctober2026Member): Rejected because dismissing the October update should dismiss the monthly product update for the user account permanently, preventing duplicate popups if their workspace permissions fluctuate.
  8. melvin-bot commented on Oct 20, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  9. sijo0218 commented on Oct 20, 2026

    @sijo0218

    Contributor details
    Your Expensify account email: sijo0218@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/~011091133bbb09cd1c

    Proposal

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

    Add the persistent bottom-right product marketing window for the October 2026 Product Update feature, supporting two audience-targeted variants:

    1. Employee/Member Window Variant (Non-Admin Members):
      • Title: Mileage on cruise control
      • Subtext: Jump straight to your frequent trips and fix a trip’s stop location without starting over.
    2. Admin Window Variant (Admins):
      • Title: Mileage handled automatically
      • Subtext: Mileage rates that keep themselves current, plus total control over how trips get logged.
    3. Common Actions & CTA destination:
      • Buttons: [Dismiss] and [Learn more]
      • CTA destination: https://help.expensify.com/articles/new-expensify/reports-and-expenses/Distance-Expenses
    4. Visual Asset:
      • Promotional graphics asset provided in the issue description (assets/images/october2026-promo.png).

    What is the root cause of that problem?

    This is a feature addition on the persistent product marketing window framework introduced in issue #95791 / PR #96141. Currently:

    1. src/libs/ProductMarketingWindowUtils.ts statically exports ACTIVE_PRODUCT_MARKETING_ANNOUNCEMENT for the September 2026 release (PRODUCT_UPDATE_SEPTEMBER_2026) and only supports a single static heading/body without audience-role differentiation.
    2. The October 2026 update key (PRODUCT_UPDATE_OCTOBER_2026) and distance expenses help URL (DISTANCE_EXPENSES_HELP_URL) are not yet defined in src/CONST/index.ts.
    3. Marketing image asset (october2026-promo.png) and localized copy are not yet added.
    4. Audience targeting (Admin vs Non-Admin Member, where admin role takes precedence if a user has both) needs to be resolved dynamically via policy state.

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

    1. Constants & Visual Asset

    • In src/CONST/index.ts:
      • Add PRODUCT_UPDATE_OCTOBER_2026: 'productUpdateOctober2026' to CONST.MARKETING_WINDOW_UPDATE_KEYS.
      • Add DISTANCE_EXPENSES_HELP_URL: 'https://help.expensify.com/articles/new-expensify/reports-and-expenses/Distance-Expenses'.
    • In assets/images/:
      • Add the marketing illustration as october2026-promo.png.

    2. Localization Strings

    • In src/languages/en.ts (and mirror in other locale files):
    productMarketingWindow: {
        october2026: {
            member: {
                heading: 'Mileage on cruise control',
                body: 'Jump straight to your frequent trips and fix a trip’s stop location without starting over.',
            },
            admin: {
                heading: 'Mileage handled automatically',
                body: 'Mileage rates that keep themselves current, plus total control over how trips get logged.',
            },
        },
    },

    3. Update Announcement Configuration in src/libs/ProductMarketingWindowUtils.ts

    Export a helper function getActiveProductMarketingAnnouncement(isAdmin?: boolean) that returns the appropriate announcement while preserving ACTIVE_PRODUCT_MARKETING_ANNOUNCEMENT for backwards compatibility:

    import October2026PromoImage from '@assets/images/october2026-promo.png';
    
    function getActiveProductMarketingAnnouncement(isAdmin = false): ProductMarketingAnnouncement | null {
        return {
            updateKey: CONST.MARKETING_WINDOW_UPDATE_KEYS.PRODUCT_UPDATE_OCTOBER_2026,
            visual: {type: 'image', source: October2026PromoImage},
            heading: isAdmin ? 'productMarketingWindow.october2026.admin.heading' : 'productMarketingWindow.october2026.member.heading',
            body: isAdmin ? 'productMarketingWindow.october2026.admin.body' : 'productMarketingWindow.october2026.member.body',
            ctaLabel: 'common.learnMore',
            ctaUrl: CONST.DISTANCE_EXPENSES_HELP_URL,
        };
    }
    
    const ACTIVE_PRODUCT_MARKETING_ANNOUNCEMENT = getActiveProductMarketingAnnouncement(false);
    
    export {ACTIVE_PRODUCT_MARKETING_ANNOUNCEMENT, getActiveProductMarketingAnnouncement, isProductMarketingAnnouncementDismissed};

    4. Connect Audience Resolution in src/components/ProductMarketingWindow/ProductMarketingWindowManager.tsx

    • Import useHasActiveAdminPolicies and getActiveProductMarketingAnnouncement:
    import useHasActiveAdminPolicies from '@hooks/useHasActiveAdminPolicies';
    import {getActiveProductMarketingAnnouncement, isProductMarketingAnnouncementDismissed} from '@libs/ProductMarketingWindowUtils';
    • In ProductMarketingWindowManager:
    const hasActiveAdminPolicies = useHasActiveAdminPolicies();
    const announcement = getActiveProductMarketingAnnouncement(hasActiveAdminPolicies);
    • Why useHasActiveAdminPolicies()?
      Per issue requirements, anyone who is an admin of at least one workspace receives the Admin variant, and only users with no admin roles across all active workspaces receive the Member variant. useHasActiveAdminPolicies() directly uses hasActiveAdminPoliciesSelector which accounts for active workspaces across both free and paid policies (unlike useIsPaidPolicyAdmin which excludes free policy admins).
    • Dismissal persistence via dismissMarketingWindow(announcement.updateKey) continues to write productUpdateOctober2026, guaranteeing that dismissing either variant persists account-wide even if the user's role subsequently changes.

    5. Unit & Navigation Test Suite Updates

    • tests/unit/ProductMarketingWindowUtilsTest.ts:
      • Update UNKNOWN_UPDATE_KEY from 'productUpdateOctober2026' to 'productUpdateNovember2026'. Because productUpdateOctober2026 is now a recognized key in CONST.MARKETING_WINDOW_UPDATE_KEYS, updating this ensures the test asserting unrecognized future keys continues to verify stale-client behavior.
    • tests/unit/ProductMarketingWindowTest.tsx:
      • Update announcement assertions for October 2026 assets and copy.
      • Add test asserting that when hasActiveAdminPolicies is true, the admin heading/body is rendered, and when false, the member heading/body is rendered.
    • tests/ui/ProductMarketingWindowNavigationTest.tsx:
      • Update windowHeading translation reference to match the active announcement heading.

    What alternative solutions did you explore? (Optional)

    • Using useIsPaidPolicyAdmin(): Discarded because it excludes admins of free workspaces who must also receive the Admin variant.
    • Separate update keys per variant (e.g. productUpdateOctober2026Admin): Discarded because dismissing the monthly update should be persistent for the user regardless of future role changes.
  10. melvin-bot commented on Oct 20, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  11. RajGahoi commented on Oct 20, 2026

    @RajGahoi

    Contributor details
    Your Expensify account email: gahoiraj01@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/~014898525b9c53fc32

    Proposal

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

    Add the persistent bottom-right product marketing window for the October 2026 Product Update ("Mileage on cruise control" / "Mileage handled automatically"), supporting two audience variants:

    1. Employee/Member Window Variant (Non-Admin Members):
      • Title: Mileage on cruise control
      • Subtext: Jump straight to your frequent trips and fix a trip’s stop location without starting over.
    2. Admin Window Variant (Admins):
      • Title: Mileage handled automatically
      • Subtext: Mileage rates that keep themselves current, plus total control over how trips get logged.
    3. Buttons & Link (Both variants): [Dismiss] and [Learn more] linking to https://help.expensify.com/articles/new-expensify/reports-and-expenses/Distance-Expenses.
    4. Visual Asset: Provided marketing promo image (october2026-promo.png).

    What is the root cause of that problem?

    This is a feature addition on the persistent product marketing window infrastructure (introduced in #95791). Currently, ACTIVE_PRODUCT_MARKETING_ANNOUNCEMENT in src/libs/ProductMarketingWindowUtils.ts statically serves the September 2026 announcement (PRODUCT_UPDATE_SEPTEMBER_2026) and does not dynamically distinguish between Admin and Member audiences based on the user's active workspace permissions.

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

    1. Add Constants & Help URL

    • In src/CONST/index.ts:
      • Add PRODUCT_UPDATE_OCTOBER_2026: 'productUpdateOctober2026' to CONST.MARKETING_WINDOW_UPDATE_KEYS.
      • Add DISTANCE_EXPENSES_HELP_URL: 'https://help.expensify.com/articles/new-expensify/reports-and-expenses/Distance-Expenses'.

    2. Add Localization Strings

    • In src/languages/en.ts (and corresponding locale files):
      productMarketingWindow: {
          october2026: {
              member: {
                  heading: 'Mileage on cruise control',
                  body: 'Jump straight to your frequent trips and fix a trip’s stop location without starting over.',
              },
              admin: {
                  heading: 'Mileage handled automatically',
                  body: 'Mileage rates that keep themselves current, plus total control over how trips get logged.',
              },
          },
      },

    3. Update Announcement Configuration in src/libs/ProductMarketingWindowUtils.ts

    Export a function getActiveProductMarketingAnnouncement(isAdmin?: boolean) that returns the appropriate announcement while maintaining ACTIVE_PRODUCT_MARKETING_ANNOUNCEMENT for backwards compatibility:

    import October2026PromoImage from '@assets/images/october2026-promo.png';
    
    function getActiveProductMarketingAnnouncement(isAdmin = false): ProductMarketingAnnouncement | null {
        return {
            updateKey: CONST.MARKETING_WINDOW_UPDATE_KEYS.PRODUCT_UPDATE_OCTOBER_2026,
            visual: {type: 'image', source: October2026PromoImage},
            heading: isAdmin ? 'productMarketingWindow.october2026.admin.heading' : 'productMarketingWindow.october2026.member.heading',
            body: isAdmin ? 'productMarketingWindow.october2026.admin.body' : 'productMarketingWindow.october2026.member.body',
            ctaLabel: 'common.learnMore',
            ctaUrl: CONST.DISTANCE_EXPENSES_HELP_URL,
        };
    }
    
    const ACTIVE_PRODUCT_MARKETING_ANNOUNCEMENT = getActiveProductMarketingAnnouncement(false);
    
    export {ACTIVE_PRODUCT_MARKETING_ANNOUNCEMENT, getActiveProductMarketingAnnouncement, isProductMarketingAnnouncementDismissed};

    4. Connect Audience Resolution in ProductMarketingWindowManager.tsx

    • Subscribe to ONYXKEYS.COLLECTION.POLICY and resolve whether the current user has any active admin workspaces using hasActiveAdminPoliciesSelector (from src/selectors/Policy.ts) and current user email from ONYXKEYS.SESSION:
      const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: emailSelector});
      const [hasActiveAdminPolicies = false] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {
          selector: (policies) => hasActiveAdminPoliciesSelector(policies, currentUserLogin ?? ''),
      });
      const announcement = getActiveProductMarketingAnnouncement(hasActiveAdminPolicies);
    • Why hasActiveAdminPoliciesSelector?
      Per the issue requirements, any user who is an admin on at least one active workspace (Collect, Control, or Free) should see the Admin variant; members with zero admin workspaces see the Member variant. hasActiveAdminPoliciesSelector precisely utilizes getActiveAdminWorkspaces, which accounts for both paid and free policies.
    • Dismissing the window calls dismissMarketingWindow(announcement.updateKey), persisting productUpdateOctober2026. This guarantees that dismissing either variant persists across the account even if their workspace role changes later.

    5. Tests

    • tests/unit/ProductMarketingWindowUtilsTest.ts: Update UNKNOWN_UPDATE_KEY to 'productUpdateNovember2026' (since 'productUpdateOctober2026' is now registered in CONST.MARKETING_WINDOW_UPDATE_KEYS), ensuring the stale-client test continues to pass as expected.
    • tests/unit/ProductMarketingWindowTest.tsx: Add test coverage verifying that when hasActiveAdminPolicies is true, the admin variant heading/body is rendered, and when false, the member variant is rendered.
    • tests/ui/ProductMarketingWindowNavigationTest.tsx: Update expected heading translation keys.

    What alternative solutions did you explore? (Optional)

    • Separate keys per role (e.g. productUpdateOctober2026Admin): Discarded because dismissing the monthly update should apply account-wide regardless of role changes.
  12. melvin-bot commented on Oct 20, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  13. 7 remaining items

  14. shawnborton commented on Oct 22, 2026

    @shawnborton
    Contributor
  15. ahmedGaber93 commented on Oct 22, 2026

    @ahmedGaber93
    Contributor

    Thanks, I have asked melvin to use it #104699 (comment).

  16. catdevore commented on Oct 26, 2026

    @catdevore
    Contributor

    @ahmedGaber93 just checking on the status of this one so I can add it to my weekly update, thanks!

  17. ahmedGaber93 commented on Oct 26, 2026

    @ahmedGaber93
    Contributor

    @catdevore We’re updating the help docs in #104781 to align with Neil’s comment before shipping this. Once the docs are updated, we’ll merge the App PR.

  18. melvin-bot commented on Oct 28, 2026

    @melvin-bot

    Triggered auto assignment to @mjasikowski, see https://stackoverflow.com/c/expensify/questions/7972 for more details.

  19. ahmedGaber93 commented on Oct 29, 2026

    @ahmedGaber93
    Contributor

    Current statue: Waiting for design team input here #104699 (comment)

  20. shawnborton commented on Oct 29, 2026

    @shawnborton
    Contributor

    @catdevore can you chime in? It sounds like we need an additional image for the October product image? @ahmedGaber93 what exactly do you need from design?

  21. shawnborton commented on Oct 29, 2026

    @shawnborton
    Contributor

    Actually it looks like we want the same image for both, per the OP:

    Image
  22. ahmedGaber93 commented on Oct 29, 2026

    @ahmedGaber93
    Contributor

    @shawnborton @catdevore we do a few changes on the PR, feel free tow build adhoc and reviewing them.

    We now use two different links, Neil add a fair comment here: #104699 (comment) to use links that directly access the topics.

    Another fair comment here: #104699 (comment) about using different images, I think we need an image that clearly describes this feature.

    Please let us know if we’ve misunderstood the product’s intent.

    Screen.Recording.2026-10-29.at.11.00.35.PM.mov
  23. shawnborton commented on Oct 29, 2026

    @shawnborton
    Contributor

    Okay I see what you are saying. We could do something like this? cc @Expensify/design

    Image Image
  24. dannymcclain commented on Oct 29, 2026

    @dannymcclain
    Contributor

    Looks good to me!

  25. ahmedGaber93 commented on Oct 29, 2026

    @ahmedGaber93
    Contributor

    This looks good to me also. I think this is what we are looking for.

  26. shawnborton commented on Oct 29, 2026

    @shawnborton
    Contributor

    Let's see what @catdevore and @dubielzyk-expensify think.

  27. dubielzyk-expensify commented on Oct 30, 2026

    @dubielzyk-expensify
    Contributor

    LGTM! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

ExternalAdded to denote the issue can be worked on by a contributorReviewingHas a PR in reviewWeeklyKSv2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions