mirror of
https://github.com/mattermost/mattermost-mobile.git
synced 2026-11-06 19:00:14 +00:00
* Fix Gradle 9 build failure in @react-native/gradle-plugin (foojay 0.5.0)
The android/ gradle wrapper is on Gradle 9.0.0, but @react-native/gradle-plugin
(RN 0.83.9) pins foojay-resolver-convention 0.5.0. That plugin references
JvmVendorSpec.IBM_SEMERU, a field removed in Gradle 9, so it throws
NoSuchFieldError while its DistributionsKt class initializes. This happens
during settings evaluation, which means every ./gradlew invocation on a clean
checkout fails before any task runs (not even `gradlew help` works), and
`npm run android` can't build.
Patch bumps foojay-resolver-convention to 1.0.0, the first release compatible
with Gradle 9. (0.10.0 was tried first but still references the removed field.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* MM-69625 MM-69633: Collapse-all and task filtering on playbook run checklists
Bring two playbook-run capabilities that already exist in the webapp to mobile,
via two icons in the "Tasks" header of the run screen.
Collapse/expand all checklists
A header toggle (arrow-collapse / arrow-expand) that collapses or expands
every checklist at once. Per-checklist expand state already existed as local
state inside Checklist; rather than replace it (individual toggling is still
wanted), the run-level control is lifted into PlaybookRun and pushed down as
a `collapseAll` flag plus a monotonic `collapseAllEpoch`. The epoch is what
makes it work after a user has toggled a checklist on its own: without it,
pressing "collapse all" again when `collapseAll` hadn't changed value would
be a no-op, so each checklist re-syncs to `collapseAll` whenever the epoch
advances (via useDidUpdate).
Task filter
A filter button (filter-variant, tinted when a filter is active) opens a
bottom sheet modelled on components/files/file_filter.tsx: "All tasks", then
a Task state section (show checked / show skipped) and an Assignee section
(Me / Unassigned / Others), matching the webapp. "All tasks" is a
select-all/clear-all toggle. Filter state lives in PlaybookRun and is applied
when rendering each checklist's items.
Item index-safety fix (prerequisite for filtering)
The server mutates checklist items by their position in the checklist's
canonical order (client.setChecklistItemState(runId, checklistNum, itemNum,
...), skipChecklistItem, setAssignee, ...). checklist.tsx was rendering items
with itemNumber={renderIndex}, where renderIndex was the index into an
already-filtered array (the enhancer dropped condition-hidden items before
rendering). So whenever an item was omitted, every later item's itemNumber
shifted, and an action on one task would target a different task on the
server. This was already reachable on main via condition-hidden items;
user-facing filtering would have made it trivial to hit.
Fixed by moving all filtering to render time: the enhancer (checklist/index.ts)
now emits every item in canonical order (and additionally observes
assignee_id so the assignee filter reacts live), and checklist.tsx pairs each
item with its true index before filtering, so itemNumber is always the
server-side index regardless of what is hidden. Both the visible list and the
off-screen height-calculator copy use the same filtered set so the collapse
animation height stays correct.
Filtering logic is a pure util (utils/task_filters.ts: isItemVisible,
itemMatchesFilters, areDefaultTaskFilters) with its own unit tests; component
and screen tests cover both controls and pin the index-safety guarantee
(a filtered-out item must not shift the itemNumber of the items after it).
New i18n strings were added to en.json only, via npm run i18n-extract.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* MM-69627: Use defineMessages for fill requirements titles
Keep i18n extraction able to find the header titles after moving them to the route.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Revert "MM-69627: Use defineMessages for fill requirements titles"
This reverts commit 3f725a262c.
* Address review feedback on playbook collapse-all and filters
Drop the unrelated Gradle foojay patch from this PR, remove the
redundant collapseAllEpoch (collapseAll already toggles on each press),
seed checklist expanded state from collapseAll, and tighten filter tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address Lindy QA feedback on playbook filters and due date navigation
Dismiss the checklist item sheet before opening due date/assignee/command/edit so Android headers keep status-bar insets. Show a filtered-empty state with Clear filters, and make All tasks select-all only so it cannot hide every task.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix playbook task filter semantics and i18n key order.
Task state filters work without assignee selection, assignee toggles only refine further, and Show unchecked tasks is available. Also reorders en.json keys so the i18n precommit check passes.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix collapse-all dead press and strengthen dismiss-order tests.
Lift checklist expanded state to the run screen so the header icon
tracks actual expand/collapse status and never no-ops after manual
toggles. Assert navigation waits on dismissBottomSheet resolving.
Co-authored-by: Asaad Mahmood <asaadmahmood@users.noreply.github.com>
* Fix import order in checklist_list test.
Co-authored-by: Asaad Mahmood <asaadmahmood@users.noreply.github.com>
* Fix SendButton tutorial test broken by main merge.
Restore setting the watched flag before render; storeGlobal(null)
cleanup leaves a Global row that observeTutorialWatched treats as
watched, so the mid-test false assertion from #10023 always fails.
Co-authored-by: Asaad Mahmood <asaadmahmood@users.noreply.github.com>
* chore: retrigger E2E after unrelated detox-ios flakes
Re-run Matterwick E2E after MM-T4899_4 (markdown table scroll visibility)
and MM-T1719_1 (archived channel plus-menu blocked by leftover alert)
failed on iOS with no playbook involvement.
Co-authored-by: Asaad Mahmood <asaadmahmood@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
73 lines
2.8 KiB
TypeScript
73 lines
2.8 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import React from 'react';
|
|
|
|
import {storeGlobal} from '@actions/app/global';
|
|
import {Tutorial} from '@constants';
|
|
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
|
import DatabaseManager from '@database/manager';
|
|
import {renderWithEverything, waitFor} from '@test/intl-test-helper';
|
|
|
|
import SendButton from './send_button';
|
|
|
|
import EnhancedSendButton from './index';
|
|
|
|
import type ServerDataOperator from '@database/operator/server_data_operator';
|
|
import type {Database} from '@nozbe/watermelondb';
|
|
|
|
jest.mock('./send_button', () => ({
|
|
__esModule: true,
|
|
default: jest.fn(),
|
|
}));
|
|
|
|
jest.mocked(SendButton).mockImplementation((props) => React.createElement('SendButton', {...props, testID: 'send-button'}));
|
|
|
|
describe('SendButton', () => {
|
|
const serverUrl = 'server-1';
|
|
const teamId = 'team1';
|
|
let database: Database;
|
|
let operator: ServerDataOperator;
|
|
let unmount: () => void;
|
|
|
|
beforeEach(async () => {
|
|
await DatabaseManager.init([serverUrl]);
|
|
const serverDatabaseAndOperator = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
|
database = serverDatabaseAndOperator.database;
|
|
operator = serverDatabaseAndOperator.operator;
|
|
|
|
await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false});
|
|
});
|
|
|
|
afterEach(async () => {
|
|
// Unmount before DB destruction so withObservables subscriptions are
|
|
// torn down before the database emits a final completion, preventing
|
|
// unwrapped state updates after the test ends.
|
|
unmount?.();
|
|
await DatabaseManager.destroyServerDatabase(serverUrl);
|
|
await storeGlobal(Tutorial.SCHEDULED_POST, null, false);
|
|
});
|
|
|
|
const defaultProps: Parameters<typeof EnhancedSendButton>[0] = {
|
|
testID: 'send-button',
|
|
disabled: false,
|
|
sendMessage: jest.fn(),
|
|
showScheduledPostOptions: jest.fn(),
|
|
scheduledPostEnabled: true,
|
|
};
|
|
|
|
it('should return false if the scheduled post tutorial is not watched', async () => {
|
|
const {getByTestId, unmount: u} = renderWithEverything(<EnhancedSendButton {...defaultProps}/>, {database});
|
|
unmount = u;
|
|
await waitFor(() => expect(getByTestId('send-button').props.scheduledPostFeatureTooltipWatched).toBe(false));
|
|
});
|
|
|
|
it('should return true if the scheduled post tutorial is watched', async () => {
|
|
await storeGlobal(Tutorial.SCHEDULED_POST, 'true', false);
|
|
const {getByTestId, unmount: u} = renderWithEverything(<EnhancedSendButton {...defaultProps}/>, {database});
|
|
unmount = u;
|
|
|
|
await waitFor(() => expect(getByTestId('send-button').props.scheduledPostFeatureTooltipWatched).toBe(true));
|
|
});
|
|
});
|