forked from Ivasoft/mattermost-mobile
[Gekidou] Add performance and code improvements around post_list (#6113)
* Add performance and code improvements around post_list * Fix test * Move observers from utils to queries * remove Flipper on iOS to fix CI build * Fix observePermissionForChannel for DM/GM Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
committed by
GitHub
parent
c9d73d4512
commit
d1322e84ce
@@ -3,14 +3,14 @@
|
||||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {of as of$, from as from$, combineLatest, Observable} from 'rxjs';
|
||||
import {of as of$, combineLatest, Observable} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {Permissions} from '@constants';
|
||||
import {observeChannel} from '@queries/servers/channel';
|
||||
import {observePermissionForChannel} from '@queries/servers/role';
|
||||
import {observeLicense} from '@queries/servers/system';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {hasPermissionForChannel} from '@utils/role';
|
||||
|
||||
import AtMention from './at_mention';
|
||||
|
||||
@@ -28,9 +28,9 @@ const enhanced = withObservables([], ({database, channelId}: WithDatabaseArgs &
|
||||
let useGroupMentions: Observable<boolean>;
|
||||
if (channelId) {
|
||||
const currentChannel = observeChannel(database, channelId);
|
||||
useChannelMentions = combineLatest([currentUser, currentChannel]).pipe(switchMap(([u, c]) => (u && c ? from$(hasPermissionForChannel(c, u, Permissions.USE_CHANNEL_MENTIONS, false)) : of$(false))));
|
||||
useChannelMentions = combineLatest([currentUser, currentChannel]).pipe(switchMap(([u, c]) => (u && c ? observePermissionForChannel(c, u, Permissions.USE_CHANNEL_MENTIONS, false) : of$(false))));
|
||||
useGroupMentions = combineLatest([currentUser, currentChannel, hasLicense]).pipe(
|
||||
switchMap(([u, c, lcs]) => (lcs && u && c ? from$(hasPermissionForChannel(c, u, Permissions.USE_GROUP_MENTIONS, false)) : of$(false))),
|
||||
switchMap(([u, c, lcs]) => (lcs && u && c ? observePermissionForChannel(c, u, Permissions.USE_GROUP_MENTIONS, false) : of$(false))),
|
||||
);
|
||||
} else {
|
||||
useChannelMentions = of$(false);
|
||||
|
||||
@@ -21,6 +21,138 @@ exports[`components/channel_list should render channels error 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
animatedStyle={
|
||||
Object {
|
||||
"value": Object {
|
||||
"marginLeft": 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
collapsable={false}
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flexDirection": "row",
|
||||
"justifyContent": "space-between",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
focusable={false}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flexDirection": "row",
|
||||
"justifyContent": "space-between",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"fontFamily": "Metropolis-SemiBold",
|
||||
"fontSize": 28,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 36,
|
||||
}
|
||||
}
|
||||
testID="channel_list_header.team_display_name"
|
||||
>
|
||||
Test Team!
|
||||
</Text>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="chevron-down"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.8)",
|
||||
"fontSize": 24,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "rgba(255,255,255,0.08)",
|
||||
"borderRadius": 14,
|
||||
"height": 28,
|
||||
"justifyContent": "center",
|
||||
"opacity": 1,
|
||||
"width": 28,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
name="plus"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.8)",
|
||||
"fontSize": 18,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.64)",
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 11,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 16,
|
||||
}
|
||||
}
|
||||
testID="channel_list_header.server_display_name"
|
||||
>
|
||||
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
@@ -191,6 +323,36 @@ exports[`components/channel_list should render team error 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
animatedStyle={
|
||||
Object {
|
||||
"value": Object {
|
||||
"marginLeft": 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
collapsable={false}
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.64)",
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 11,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 16,
|
||||
}
|
||||
}
|
||||
testID="channel_list_header.server_display_name"
|
||||
>
|
||||
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {combineLatest, of as of$, from as from$} from 'rxjs';
|
||||
import {combineLatest, of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {Permissions} from '@constants';
|
||||
import {observePermissionForTeam} from '@queries/servers/role';
|
||||
import {observeCurrentTeam} from '@queries/servers/team';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {hasPermissionForTeam} from '@utils/role';
|
||||
|
||||
import ChannelListHeader from './header';
|
||||
|
||||
@@ -21,15 +21,15 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const currentUser = observeCurrentUser(database);
|
||||
|
||||
const canJoinChannels = combineLatest([currentUser, team]).pipe(
|
||||
switchMap(([u, t]) => (t && u ? from$(hasPermissionForTeam(t, u, Permissions.JOIN_PUBLIC_CHANNELS, true)) : of$(false))),
|
||||
switchMap(([u, t]) => (t && u ? observePermissionForTeam(t, u, Permissions.JOIN_PUBLIC_CHANNELS, true) : of$(false))),
|
||||
);
|
||||
|
||||
const canCreatePublicChannels = combineLatest([currentUser, team]).pipe(
|
||||
switchMap(([u, t]) => (t && u ? from$(hasPermissionForTeam(t, u, Permissions.CREATE_PUBLIC_CHANNEL, true)) : of$(false))),
|
||||
switchMap(([u, t]) => (t && u ? observePermissionForTeam(t, u, Permissions.CREATE_PUBLIC_CHANNEL, true) : of$(false))),
|
||||
);
|
||||
|
||||
const canCreatePrivateChannels = combineLatest([currentUser, team]).pipe(
|
||||
switchMap(([u, t]) => (t && u ? from$(hasPermissionForTeam(t, u, Permissions.CREATE_PRIVATE_CHANNEL, false)) : of$(false))),
|
||||
switchMap(([u, t]) => (t && u ? observePermissionForTeam(t, u, Permissions.CREATE_PRIVATE_CHANNEL, false) : of$(false))),
|
||||
);
|
||||
|
||||
const canCreateChannels = combineLatest([canCreatePublicChannels, canCreatePrivateChannels]).pipe(
|
||||
|
||||
@@ -5,6 +5,8 @@ import Database from '@nozbe/watermelondb/Database';
|
||||
import React from 'react';
|
||||
import {SafeAreaProvider} from 'react-native-safe-area-context';
|
||||
|
||||
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import {getTeamById} from '@queries/servers/team';
|
||||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
@@ -13,9 +15,11 @@ import ChannelsList from './';
|
||||
|
||||
describe('components/channel_list', () => {
|
||||
let database: Database;
|
||||
let operator: ServerDataOperator;
|
||||
beforeAll(async () => {
|
||||
const server = await TestHelper.setupServerDatabase();
|
||||
database = server.database;
|
||||
operator = server.operator;
|
||||
|
||||
const team = await getTeamById(database, TestHelper.basicTeam!.id);
|
||||
await database.write(async () => {
|
||||
@@ -40,7 +44,12 @@ describe('components/channel_list', () => {
|
||||
expect(wrapper.toJSON()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render team error', () => {
|
||||
it('should render team error', async () => {
|
||||
await operator.handleSystem({
|
||||
systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: ''}],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
|
||||
const wrapper = renderWithEverything(
|
||||
<SafeAreaProvider>
|
||||
<ChannelsList
|
||||
@@ -52,7 +61,13 @@ describe('components/channel_list', () => {
|
||||
</SafeAreaProvider>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(wrapper.toJSON()).toMatchSnapshot();
|
||||
|
||||
await operator.handleSystem({
|
||||
systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: TestHelper.basicTeam!.id}],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should render channels error', () => {
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import React from 'react';
|
||||
import {combineLatest, of as of$, from as from$} from 'rxjs';
|
||||
import {combineLatest, of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {General, Permissions} from '@constants';
|
||||
import {observeChannel} from '@queries/servers/channel';
|
||||
import {queryDraft} from '@queries/servers/drafts';
|
||||
import {observePermissionForChannel} from '@queries/servers/role';
|
||||
import {observeConfigBooleanValue, observeCurrentChannelId} from '@queries/servers/system';
|
||||
import {observeCurrentUser, observeUser} from '@queries/servers/user';
|
||||
import {hasPermissionForChannel} from '@utils/role';
|
||||
import {isSystemAdmin, getUserIdFromChannelName} from '@utils/user';
|
||||
|
||||
import PostDraft from './post_draft';
|
||||
@@ -50,7 +50,7 @@ const enhanced = withObservables([], (ownProps: WithDatabaseArgs & OwnProps) =>
|
||||
switchMap((id) => observeChannel(database, id!)),
|
||||
);
|
||||
|
||||
const canPost = combineLatest([channel, currentUser]).pipe(switchMap(([c, u]) => (c && u ? from$(hasPermissionForChannel(c, u, Permissions.CREATE_POST, false)) : of$(false))));
|
||||
const canPost = combineLatest([channel, currentUser]).pipe(switchMap(([c, u]) => (c && u ? observePermissionForChannel(c, u, Permissions.CREATE_POST, false) : of$(false))));
|
||||
const channelIsArchived = channel.pipe(switchMap((c) => (ownProps.channelIsArchived ? of$(true) : of$(c?.deleteAt !== 0))));
|
||||
|
||||
const experimentalTownSquareIsReadOnly = observeConfigBooleanValue(database, 'ExperimentalTownSquareIsReadOnly');
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {combineLatest, of as of$, from as from$} from 'rxjs';
|
||||
import {combineLatest, of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {General, Permissions} from '@constants';
|
||||
import {MAX_MESSAGE_LENGTH_FALLBACK} from '@constants/post_draft';
|
||||
import {observeChannel, observeCurrentChannel} from '@queries/servers/channel';
|
||||
import {queryAllCustomEmojis} from '@queries/servers/custom_emoji';
|
||||
import {observePermissionForChannel} from '@queries/servers/role';
|
||||
import {observeConfig, observeCurrentUserId} from '@queries/servers/system';
|
||||
import {observeUser} from '@queries/servers/user';
|
||||
import {hasPermissionForChannel} from '@utils/role';
|
||||
|
||||
import SendHandler from './send_handler';
|
||||
|
||||
@@ -59,7 +59,7 @@ const enhanced = withObservables([], (ownProps: WithDatabaseArgs & OwnProps) =>
|
||||
return of$(true);
|
||||
}
|
||||
|
||||
return u ? from$(hasPermissionForChannel(c, u, Permissions.USE_CHANNEL_MENTIONS, false)) : of$(false);
|
||||
return u ? observePermissionForChannel(c, u, Permissions.USE_CHANNEL_MENTIONS, false) : of$(false);
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import UploadItem from './upload_item';
|
||||
|
||||
const CONTAINER_HEIGHT_MAX = 67;
|
||||
const CONATINER_HEIGHT_MIN = 0;
|
||||
const CONTAINER_HEIGHT_MIN = 0;
|
||||
const ERROR_HEIGHT_MAX = 20;
|
||||
const ERROR_HEIGHT_MIN = 0;
|
||||
|
||||
@@ -80,7 +80,7 @@ function Uploads({
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
const errorHeight = useSharedValue(ERROR_HEIGHT_MIN);
|
||||
const containerHeight = useSharedValue(CONTAINER_HEIGHT_MAX);
|
||||
const containerHeight = useSharedValue(files.length ? CONTAINER_HEIGHT_MAX : CONTAINER_HEIGHT_MIN);
|
||||
const filesForGallery = useRef(files.filter((f) => !f.failed && !DraftUploadManager.isUploading(f.clientId!)));
|
||||
|
||||
const errorAnimatedStyle = useAnimatedStyle(() => {
|
||||
@@ -116,7 +116,7 @@ function Uploads({
|
||||
containerHeight.value = CONTAINER_HEIGHT_MAX;
|
||||
return;
|
||||
}
|
||||
containerHeight.value = CONATINER_HEIGHT_MIN;
|
||||
containerHeight.value = CONTAINER_HEIGHT_MIN;
|
||||
}, [files.length > 0]);
|
||||
|
||||
const openGallery = useCallback((file: FileInfo) => {
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import React from 'react';
|
||||
import {combineLatest, from as from$, of as of$} from 'rxjs';
|
||||
import {combineLatest, of as of$} from 'rxjs';
|
||||
import {map, switchMap} from 'rxjs/operators';
|
||||
|
||||
import {Permissions} from '@constants';
|
||||
import {queryPostsById} from '@queries/servers/post';
|
||||
import {observePermissionForPost} from '@queries/servers/role';
|
||||
import {observeCurrentUserId} from '@queries/servers/system';
|
||||
import {observeUser, queryUsersByIdsOrUsernames} from '@queries/servers/user';
|
||||
import {generateCombinedPost, getPostIdsForCombinedUserActivityPost} from '@utils/post_list';
|
||||
import {hasPermissionForPost} from '@utils/role';
|
||||
|
||||
import CombinedUserActivity from './combined_user_activity';
|
||||
|
||||
@@ -28,7 +28,7 @@ const withCombinedPosts = withObservables(['postId'], ({database, postId}: WithD
|
||||
const posts = queryPostsById(database, postIds).observe();
|
||||
const post = posts.pipe(map((ps) => generateCombinedPost(postId, ps)));
|
||||
const canDelete = combineLatest([posts, currentUser]).pipe(
|
||||
switchMap(([ps, u]) => (u ? from$(hasPermissionForPost(ps[0], u, Permissions.DELETE_OTHERS_POSTS, false)) : of$(false))),
|
||||
switchMap(([ps, u]) => (u ? observePermissionForPost(ps[0], u, Permissions.DELETE_OTHERS_POSTS, false) : of$(false))),
|
||||
);
|
||||
|
||||
const usernamesById = post.pipe(
|
||||
|
||||
@@ -99,7 +99,7 @@ const PostList = ({
|
||||
const onScrollEndIndexListener = useRef<onScrollEndIndexListenerEvent>();
|
||||
const onViewableItemsChangedListener = useRef<ViewableItemsChangedListenerEvent>();
|
||||
const scrolledToHighlighted = useRef(false);
|
||||
const [offsetY, setOffsetY] = useState(0);
|
||||
const [enableRefreshControl, setEnableRefreshControl] = useState(false);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const theme = useTheme();
|
||||
const serverUrl = useServerUrl();
|
||||
@@ -145,13 +145,9 @@ const PostList = ({
|
||||
const onScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
||||
if (Platform.OS === 'android') {
|
||||
const {y} = event.nativeEvent.contentOffset;
|
||||
if (y === 0) {
|
||||
setOffsetY(y);
|
||||
} else if (offsetY === 0 && y !== 0) {
|
||||
setOffsetY(y);
|
||||
}
|
||||
setEnableRefreshControl(y === 0);
|
||||
}
|
||||
}, [offsetY]);
|
||||
}, []);
|
||||
|
||||
const onScrollToIndexFailed = useCallback((info: ScrollIndexFailed) => {
|
||||
const index = Math.min(info.highestMeasuredFrameIndex, info.index);
|
||||
@@ -340,7 +336,7 @@ const PostList = ({
|
||||
return (
|
||||
<>
|
||||
<PostListRefreshControl
|
||||
enabled={offsetY === 0}
|
||||
enabled={enableRefreshControl}
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
style={styles.container}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {combineLatest, from as from$, of as of$} from 'rxjs';
|
||||
import {combineLatest, of as of$} from 'rxjs';
|
||||
import {map, switchMap} from 'rxjs/operators';
|
||||
|
||||
import {General, Permissions} from '@constants';
|
||||
import {observePermissionForPost} from '@queries/servers/role';
|
||||
import {observeConfigBooleanValue, observeCurrentUserId} from '@queries/servers/system';
|
||||
import {observeUser} from '@queries/servers/user';
|
||||
import {hasPermissionForPost} from '@utils/role';
|
||||
import {isSystemAdmin} from '@utils/user';
|
||||
|
||||
import Reactions from './reactions';
|
||||
@@ -34,8 +34,8 @@ const withReactions = withObservables(['post'], ({database, post}: WithReactions
|
||||
map(([u, c, readOnly]) => ((c && c.deleteAt > 0) || (c?.name === General.DEFAULT_CHANNEL && !isSystemAdmin(u?.roles || '') && readOnly))),
|
||||
);
|
||||
|
||||
const canAddReaction = currentUser.pipe(switchMap((u) => (u ? from$(hasPermissionForPost(post, u, Permissions.ADD_REACTION, true)) : of$(true))));
|
||||
const canRemoveReaction = currentUser.pipe(switchMap((u) => (u ? from$(hasPermissionForPost(post, u, Permissions.REMOVE_REACTION, true)) : of$(true))));
|
||||
const canAddReaction = currentUser.pipe(switchMap((u) => (u ? observePermissionForPost(post, u, Permissions.ADD_REACTION, true) : of$(true))));
|
||||
const canRemoveReaction = currentUser.pipe(switchMap((u) => (u ? observePermissionForPost(post, u, Permissions.REMOVE_REACTION, true) : of$(true))));
|
||||
|
||||
return {
|
||||
canAddReaction,
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import React from 'react';
|
||||
import {from as from$, of as of$} from 'rxjs';
|
||||
import {of as of$, combineLatest} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {Permissions, Preferences} from '@constants';
|
||||
import {queryAllCustomEmojis} from '@queries/servers/custom_emoji';
|
||||
import {queryPostsBetween} from '@queries/servers/post';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeCanManageChannelMembers, observePermissionForPost} from '@queries/servers/role';
|
||||
import {observeConfigBooleanValue} from '@queries/servers/system';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {hasJumboEmojiOnly} from '@utils/emoji/helpers';
|
||||
import {areConsecutivePosts, isPostEphemeral} from '@utils/post';
|
||||
import {canManageChannelMembers, hasPermissionForPost} from '@utils/role';
|
||||
|
||||
import Post from './post';
|
||||
|
||||
@@ -33,35 +33,47 @@ type PropsInput = WithDatabaseArgs & {
|
||||
previousPost: PostModel | undefined;
|
||||
}
|
||||
|
||||
async function shouldHighlightReplyBar(currentUser: UserModel, post: PostModel, postsInThread: PostsInThreadModel) {
|
||||
let commentsNotifyLevel = Preferences.COMMENTS_NEVER;
|
||||
let threadCreatedByCurrentUser = false;
|
||||
let rootPost: PostModel | undefined;
|
||||
const myPosts = await queryPostsBetween(postsInThread.database, postsInThread.earliest, postsInThread.latest, null, currentUser.id, '', post.rootId || post.id).fetch();
|
||||
function observeShouldHighlightReplyBar(currentUser: UserModel, post: PostModel, postsInThread: PostsInThreadModel) {
|
||||
const myPostsCount = queryPostsBetween(postsInThread.database, postsInThread.earliest, postsInThread.latest, null, currentUser.id, '', post.rootId || post.id).observeCount();
|
||||
const root = post.root.observe().pipe(switchMap((rl) => (rl.length ? rl[0].observe() : of$(undefined))));
|
||||
|
||||
const threadRepliedToByCurrentUser = myPosts.length > 0;
|
||||
const root = await post.root.fetch();
|
||||
if (root.length) {
|
||||
rootPost = root[0];
|
||||
return combineLatest([myPostsCount, root]).pipe(
|
||||
switchMap(([mpc, r]) => {
|
||||
const threadRepliedToByCurrentUser = mpc > 0;
|
||||
let threadCreatedByCurrentUser = false;
|
||||
if (r?.userId === currentUser.id) {
|
||||
threadCreatedByCurrentUser = true;
|
||||
}
|
||||
let commentsNotifyLevel = Preferences.COMMENTS_NEVER;
|
||||
if (currentUser.notifyProps?.comments) {
|
||||
commentsNotifyLevel = currentUser.notifyProps.comments;
|
||||
}
|
||||
|
||||
const notCurrentUser = post.userId !== currentUser.id || Boolean(post.props?.from_webhook);
|
||||
if (notCurrentUser) {
|
||||
if (commentsNotifyLevel === Preferences.COMMENTS_ANY && (threadCreatedByCurrentUser || threadRepliedToByCurrentUser)) {
|
||||
return of$(true);
|
||||
} else if (commentsNotifyLevel === Preferences.COMMENTS_ROOT && threadCreatedByCurrentUser) {
|
||||
return of$(true);
|
||||
}
|
||||
}
|
||||
|
||||
return of$(false);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function observeHasReplies(post: PostModel) {
|
||||
if (!post.rootId) {
|
||||
return post.postsInThread.observe().pipe(switchMap((c) => of$(c.length > 0)));
|
||||
}
|
||||
|
||||
if (rootPost?.userId === currentUser.id) {
|
||||
threadCreatedByCurrentUser = true;
|
||||
}
|
||||
if (currentUser.notifyProps?.comments) {
|
||||
commentsNotifyLevel = currentUser.notifyProps.comments;
|
||||
}
|
||||
|
||||
const notCurrentUser = post.userId !== currentUser.id || Boolean(post.props?.from_webhook);
|
||||
if (notCurrentUser) {
|
||||
if (commentsNotifyLevel === Preferences.COMMENTS_ANY && (threadCreatedByCurrentUser || threadRepliedToByCurrentUser)) {
|
||||
return true;
|
||||
} else if (commentsNotifyLevel === Preferences.COMMENTS_ROOT && threadCreatedByCurrentUser) {
|
||||
return true;
|
||||
return post.root.observe().pipe(switchMap((rl) => {
|
||||
if (rl.length) {
|
||||
return rl[0].postsInThread.observe().pipe(switchMap((c) => of$(c.length > 0)));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return of$(false);
|
||||
}));
|
||||
}
|
||||
|
||||
function isFirstReply(post: PostModel, previousPost?: PostModel) {
|
||||
@@ -87,20 +99,20 @@ const withPost = withObservables(
|
||||
let isPostAddChannelMember = of$(false);
|
||||
const isOwner = currentUser.id === post.userId;
|
||||
const author = post.userId ? post.author.observe() : of$(null);
|
||||
const canDelete = from$(hasPermissionForPost(post, currentUser, isOwner ? Permissions.DELETE_POST : Permissions.DELETE_OTHERS_POSTS, false));
|
||||
const canDelete = observePermissionForPost(post, currentUser, isOwner ? Permissions.DELETE_POST : Permissions.DELETE_OTHERS_POSTS, false);
|
||||
const isEphemeral = of$(isPostEphemeral(post));
|
||||
const isSaved = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SAVED_POST, post.id).observe().pipe(
|
||||
switchMap((pref) => of$(Boolean(pref.length))),
|
||||
);
|
||||
|
||||
if (post.props?.add_channel_member && isPostEphemeral(post)) {
|
||||
isPostAddChannelMember = from$(canManageChannelMembers(post, currentUser));
|
||||
isPostAddChannelMember = observeCanManageChannelMembers(post, currentUser);
|
||||
}
|
||||
|
||||
const highlightReplyBar = post.postsInThread.observe().pipe(
|
||||
switchMap((postsInThreads: PostsInThreadModel[]) => {
|
||||
if (postsInThreads.length) {
|
||||
return from$(shouldHighlightReplyBar(currentUser, post, postsInThreads[0]));
|
||||
return observeShouldHighlightReplyBar(currentUser, post, postsInThreads[0]);
|
||||
}
|
||||
return of$(false);
|
||||
}));
|
||||
@@ -118,7 +130,7 @@ const withPost = withObservables(
|
||||
),
|
||||
);
|
||||
}
|
||||
const hasReplies = from$(post.hasReplies());
|
||||
const hasReplies = observeHasReplies(post);
|
||||
const isConsecutivePost = author.pipe(
|
||||
switchMap((user) => of$(Boolean(post && previousPost && !user?.isBot && areConsecutivePosts(post, previousPost)))),
|
||||
);
|
||||
|
||||
@@ -2,10 +2,17 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Database, Q} from '@nozbe/watermelondb';
|
||||
import {of as of$, combineLatest} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {Database as DatabaseConstants} from '@constants';
|
||||
import {Database as DatabaseConstants, General, Permissions} from '@constants';
|
||||
import {hasPermission} from '@utils/role';
|
||||
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type RoleModel from '@typings/database/models/servers/role';
|
||||
import type TeamModel from '@typings/database/models/servers/team';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
const {ROLE} = DatabaseConstants.MM_TABLES.SERVER;
|
||||
|
||||
@@ -25,3 +32,51 @@ export const getRoleById = async (database: Database, roleId: string): Promise<R
|
||||
export const queryRolesByNames = (database: Database, names: string[]) => {
|
||||
return database.get<RoleModel>(ROLE).query(Q.where('name', Q.oneOf(names)));
|
||||
};
|
||||
|
||||
export function observePermissionForChannel(channel: ChannelModel, user: UserModel, permission: string, defaultValue: boolean) {
|
||||
const myChannel = channel.membership.observe();
|
||||
const myTeam = channel.teamId ? channel.team.observe().pipe(switchMap((t) => (t ? t.myTeam.observe() : of$(undefined)))) : of$(undefined);
|
||||
|
||||
return combineLatest([myChannel, myTeam]).pipe(switchMap(([mc, mt]) => {
|
||||
const rolesArray = [...user.roles.split(' ')];
|
||||
if (mc) {
|
||||
rolesArray.push(...mc.roles.split(' '));
|
||||
}
|
||||
if (mt) {
|
||||
rolesArray.push(...mt.roles.split(' '));
|
||||
}
|
||||
return queryRolesByNames(user.database, rolesArray).observe().pipe(
|
||||
switchMap((r) => of$(hasPermission(r, permission, defaultValue))),
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
export function observePermissionForTeam(team: TeamModel, user: UserModel, permission: string, defaultValue: boolean) {
|
||||
return team.myTeam.observe().pipe(switchMap((myTeam) => {
|
||||
const rolesArray = [...user.roles.split(' ')];
|
||||
|
||||
if (myTeam) {
|
||||
rolesArray.push(...myTeam.roles.split(' '));
|
||||
}
|
||||
|
||||
return queryRolesByNames(user.database, rolesArray).observe().pipe(
|
||||
switchMap((roles) => of$(hasPermission(roles, permission, defaultValue))),
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
export function observePermissionForPost(post: PostModel, user: UserModel, permission: string, defaultValue: boolean) {
|
||||
return post.channel.observe().pipe(switchMap((c) => (c ? observePermissionForChannel(c, user, permission, defaultValue) : of$(defaultValue))));
|
||||
}
|
||||
|
||||
export function observeCanManageChannelMembers(post: PostModel, user: UserModel) {
|
||||
return post.channel.observe().pipe((switchMap((c) => {
|
||||
const directTypes: ChannelType[] = [General.DM_CHANNEL, General.GM_CHANNEL];
|
||||
if (!c || c.deleteAt !== 0 || directTypes.includes(c.type) || c.name === General.DEFAULT_CHANNEL) {
|
||||
return of$(false);
|
||||
}
|
||||
|
||||
const permission = c.type === General.OPEN_CHANNEL ? Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS : Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS;
|
||||
return observePermissionForChannel(c, user, permission, true);
|
||||
})));
|
||||
}
|
||||
|
||||
@@ -286,6 +286,12 @@ export const getMyTeamById = async (database: Database, teamId: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const observeMyTeam = (database: Database, teamId: string) => {
|
||||
return database.get<MyTeamModel>(MY_TEAM).query(Q.where('id', teamId), Q.take(1)).observe().pipe(
|
||||
switchMap((result) => (result.length ? result[0].observe() : of$(undefined))),
|
||||
);
|
||||
};
|
||||
|
||||
export const getTeamById = async (database: Database, teamId: string) => {
|
||||
try {
|
||||
const team = (await database.get<TeamModel>(TEAM).find(teamId));
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {combineLatest, from as from$, of as of$, Observable} from 'rxjs';
|
||||
import {combineLatest, of as of$, Observable} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {General, Permissions, Post, Preferences, Screens} from '@constants';
|
||||
import {MAX_ALLOWED_REACTIONS} from '@constants/emoji';
|
||||
import {observePost} from '@queries/servers/post';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observePermissionForChannel, observePermissionForPost} from '@queries/servers/role';
|
||||
import {observeConfig, observeLicense} from '@queries/servers/system';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {isMinimumServerVersion} from '@utils/helpers';
|
||||
import {isSystemMessage} from '@utils/post';
|
||||
import {getPostIdsForCombinedUserActivityPost} from '@utils/post_list';
|
||||
import {hasPermissionForChannel, hasPermissionForPost} from '@utils/role';
|
||||
import {isSystemAdmin} from '@utils/user';
|
||||
|
||||
import PostOptions from './post_options';
|
||||
@@ -33,27 +33,24 @@ type EnhancedProps = WithDatabaseArgs & {
|
||||
location: string;
|
||||
}
|
||||
|
||||
const canEditPost = (isOwner: boolean, post: PostModel, postEditTimeLimit: number, isLicensed: boolean, channel: ChannelModel, user: UserModel): boolean => {
|
||||
const observeCanEditPost = (isOwner: boolean, post: PostModel, postEditTimeLimit: number, isLicensed: boolean, channel: ChannelModel, user: UserModel) => {
|
||||
if (!post || isSystemMessage(post)) {
|
||||
return false;
|
||||
return of$(false);
|
||||
}
|
||||
|
||||
let cep: boolean;
|
||||
|
||||
const permissions = [Permissions.EDIT_POST];
|
||||
if (!isOwner) {
|
||||
permissions.push(Permissions.EDIT_OTHERS_POSTS);
|
||||
}
|
||||
|
||||
cep = permissions.every((permission) => hasPermissionForChannel(channel, user, permission, false));
|
||||
if (isLicensed && postEditTimeLimit !== -1) {
|
||||
const timeLeft = (post.createAt + (postEditTimeLimit * 1000)) - Date.now();
|
||||
if (timeLeft <= 0) {
|
||||
cep = false;
|
||||
return of$(false);
|
||||
}
|
||||
}
|
||||
|
||||
return cep;
|
||||
return observePermissionForChannel(channel, user, Permissions.EDIT_POST, false).pipe(switchMap((v) => {
|
||||
if (!v || isOwner) {
|
||||
return of$(v);
|
||||
}
|
||||
return observePermissionForChannel(channel, user, Permissions.EDIT_OTHERS_POSTS, false);
|
||||
}));
|
||||
};
|
||||
|
||||
const withPost = withObservables([], ({post, database}: {post: Post | PostModel} & WithDatabaseArgs) => {
|
||||
@@ -81,11 +78,11 @@ const enhanced = withObservables([], ({combinedPost, post, showAddReaction, loca
|
||||
const serverVersion = config.pipe(switchMap((cfg) => of$(cfg?.Version || '')));
|
||||
const postEditTimeLimit = config.pipe(switchMap((cfg) => of$(parseInt(cfg?.PostEditTimeLimit || '-1', 10))));
|
||||
|
||||
const canPostPermission = combineLatest([channel, currentUser]).pipe(switchMap(([c, u]) => ((c && u) ? from$(hasPermissionForChannel(c, u, Permissions.CREATE_POST, false)) : of$(false))));
|
||||
const hasAddReactionPermission = currentUser.pipe(switchMap((u) => (u ? from$(hasPermissionForPost(post, u, Permissions.ADD_REACTION, true)) : of$(false))));
|
||||
const canPostPermission = combineLatest([channel, currentUser]).pipe(switchMap(([c, u]) => ((c && u) ? observePermissionForChannel(c, u, Permissions.CREATE_POST, false) : of$(false))));
|
||||
const hasAddReactionPermission = currentUser.pipe(switchMap((u) => (u ? observePermissionForPost(post, u, Permissions.ADD_REACTION, true) : of$(false))));
|
||||
const canDeletePostPermission = currentUser.pipe(switchMap((u) => {
|
||||
const isOwner = post.userId === u?.id;
|
||||
return u ? from$(hasPermissionForPost(post, u, isOwner ? Permissions.DELETE_POST : Permissions.DELETE_OTHERS_POSTS, false)) : of$(false);
|
||||
return u ? observePermissionForPost(post, u, isOwner ? Permissions.DELETE_POST : Permissions.DELETE_OTHERS_POSTS, false) : of$(false);
|
||||
}));
|
||||
|
||||
const experimentalTownSquareIsReadOnly = config.pipe(switchMap((value) => of$(value?.ExperimentalTownSquareIsReadOnly === 'true')));
|
||||
@@ -117,12 +114,17 @@ const enhanced = withObservables([], ({combinedPost, post, showAddReaction, loca
|
||||
|
||||
const isSaved = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SAVED_POST, post.id).observe().pipe(switchMap((pref) => of$(Boolean(pref[0]?.value === 'true'))));
|
||||
|
||||
const canEdit = combineLatest([postEditTimeLimit, isLicensed, channel, currentUser, channelIsArchived, channelIsReadOnly, canEditUntil, canPostPermission]).pipe(switchMap(([lt, ls, c, u, isArchived, isReadOnly, until, canPost]) => {
|
||||
const isOwner = u?.id === post.userId;
|
||||
const canEditPostPermission = (c && u) ? canEditPost(isOwner, post, lt, ls, c, u) : false;
|
||||
const timeNotReached = (until === -1) || (until > Date.now());
|
||||
return of$(canEditPostPermission && !isArchived && !isReadOnly && timeNotReached && canPost);
|
||||
}));
|
||||
const canEdit = combineLatest([postEditTimeLimit, isLicensed, channel, currentUser, channelIsArchived, channelIsReadOnly, canEditUntil, canPostPermission]).pipe(
|
||||
switchMap(([lt, ls, c, u, isArchived, isReadOnly, until, canPost]) => {
|
||||
const isOwner = u?.id === post.userId;
|
||||
const canEditPostPermission = (c && u) ? observeCanEditPost(isOwner, post, lt, ls, c, u) : of$(false);
|
||||
const timeNotReached = (until === -1) || (until > Date.now());
|
||||
return canEditPostPermission.pipe(
|
||||
// eslint-disable-next-line max-nested-callbacks
|
||||
switchMap((canEditPost) => of$(canEditPost && !isArchived && !isReadOnly && timeNotReached && canPost)),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
const canMarkAsUnread = combineLatest([currentUser, channelIsArchived]).pipe(
|
||||
switchMap(([user, isArchived]) => of$(!isArchived && user?.id !== post.userId && !isSystemMessage(post))),
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {General, Permissions} from '@constants';
|
||||
import {queryRolesByNames} from '@queries/servers/role';
|
||||
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type MyChannelModel from '@typings/database/models/servers/my_channel';
|
||||
import type MyTeamModel from '@typings/database/models/servers/my_team';
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type RoleModel from '@typings/database/models/servers/role';
|
||||
import type TeamModel from '@typings/database/models/servers/team';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
export function hasPermission(roles: RoleModel[] | Role[], permission: string, defaultValue: boolean) {
|
||||
const permissions = new Set<string>();
|
||||
@@ -21,83 +12,3 @@ export function hasPermission(roles: RoleModel[] | Role[], permission: string, d
|
||||
const exists = permissions.has(permission);
|
||||
return defaultValue === true || exists;
|
||||
}
|
||||
|
||||
export async function hasPermissionForChannel(channel: ChannelModel, user: UserModel, permission: string, defaultValue: boolean) {
|
||||
const rolesArray = [...user.roles.split(' ')];
|
||||
|
||||
const myChannel = await channel.membership.fetch() as MyChannelModel | undefined;
|
||||
if (myChannel) {
|
||||
rolesArray.push(...myChannel.roles.split(' '));
|
||||
}
|
||||
|
||||
const team = await channel.team.fetch() as TeamModel | undefined;
|
||||
if (team) {
|
||||
const myTeam = await team.myTeam.fetch() as MyTeamModel | undefined;
|
||||
if (myTeam) {
|
||||
rolesArray.push(...myTeam.roles.split(' '));
|
||||
}
|
||||
}
|
||||
|
||||
if (rolesArray.length) {
|
||||
const roles = await queryRolesByNames(user.database, rolesArray).fetch();
|
||||
return hasPermission(roles, permission, defaultValue);
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
export async function hasPermissionForTeam(team: TeamModel, user: UserModel, permission: string, defaultValue: boolean) {
|
||||
const rolesArray = [...user.roles.split(' ')];
|
||||
|
||||
const myTeam = await team.myTeam.fetch() as MyTeamModel | undefined;
|
||||
if (myTeam) {
|
||||
rolesArray.push(...myTeam.roles.split(' '));
|
||||
}
|
||||
|
||||
if (rolesArray.length) {
|
||||
const roles = await queryRolesByNames(user.database, rolesArray).fetch();
|
||||
return hasPermission(roles, permission, defaultValue);
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
export async function hasPermissionForPost(post: PostModel, user: UserModel, permission: string, defaultValue: boolean) {
|
||||
const channel = await post.channel.fetch() as ChannelModel | undefined;
|
||||
if (channel) {
|
||||
return hasPermissionForChannel(channel, user, permission, defaultValue);
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
export async function canManageChannelMembers(post: PostModel, user: UserModel) {
|
||||
const rolesArray = [...user.roles.split(' ')];
|
||||
const channel = await post.channel.fetch() as ChannelModel | undefined;
|
||||
|
||||
const directTypes: string[] = [General.DM_CHANNEL, General.GM_CHANNEL];
|
||||
if (!channel || channel.deleteAt !== 0 || directTypes.includes(channel.type) || channel.name === General.DEFAULT_CHANNEL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const myChannel = await channel.membership.fetch() as MyChannelModel | undefined;
|
||||
if (myChannel) {
|
||||
rolesArray.push(...myChannel.roles.split(' '));
|
||||
}
|
||||
|
||||
const team = await channel.team.fetch() as TeamModel | undefined;
|
||||
if (team) {
|
||||
const myTeam = await team.myTeam.fetch() as MyTeamModel | undefined;
|
||||
if (myTeam) {
|
||||
rolesArray.push(...myTeam.roles.split(' '));
|
||||
}
|
||||
}
|
||||
|
||||
if (rolesArray.length) {
|
||||
const roles = await queryRolesByNames(post.database, rolesArray).fetch() as RoleModel[];
|
||||
const permission = channel.type === General.OPEN_CHANNEL ? Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS : Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS;
|
||||
return hasPermission(roles, permission, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -707,14 +707,10 @@
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Mattermost/Pods-Mattermost-frameworks.sh",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/double-conversion.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
@@ -14,25 +14,6 @@
|
||||
#import "Mattermost-Swift.h"
|
||||
#import <os/log.h>
|
||||
|
||||
#ifdef FB_SONARKIT_ENABLED
|
||||
#import <FlipperKit/FlipperClient.h>
|
||||
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
|
||||
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
|
||||
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
|
||||
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
|
||||
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
|
||||
|
||||
static void InitializeFlipper(UIApplication *application) {
|
||||
FlipperClient *client = [FlipperClient sharedClient];
|
||||
SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
|
||||
[client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
|
||||
[client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
|
||||
[client addPlugin:[FlipperKitReactPlugin new]];
|
||||
[client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
|
||||
[client start];
|
||||
}
|
||||
#endif
|
||||
|
||||
@import Gekidou;
|
||||
|
||||
@interface AppDelegate () <RCTBridgeDelegate>
|
||||
@@ -64,10 +45,6 @@ MattermostBucket* bucket = nil;
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
#ifdef FB_SONARKIT_ENABLED
|
||||
InitializeFlipper(application);
|
||||
#endif
|
||||
|
||||
if (bucket == nil) {
|
||||
bucket = [[MattermostBucket alloc] init];
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ end
|
||||
#
|
||||
# Note that if you have use_frameworks! enabled, Flipper will not work and
|
||||
# you should disable these next few lines.
|
||||
use_flipper!({'Flipper' => '0.138.0'})
|
||||
#use_flipper!({'Flipper' => '0.138.0'})
|
||||
|
||||
post_install do |installer|
|
||||
react_native_post_install(installer)
|
||||
|
||||
115
ios/Podfile.lock
115
ios/Podfile.lock
@@ -3,7 +3,6 @@ PODS:
|
||||
- boost (1.76.0)
|
||||
- BVLinearGradient (2.5.6):
|
||||
- React
|
||||
- CocoaAsyncSocket (7.6.5)
|
||||
- DoubleConversion (1.1.6)
|
||||
- EXFileSystem (13.1.4):
|
||||
- ExpoModulesCore
|
||||
@@ -22,66 +21,6 @@ PODS:
|
||||
- React-Core (= 0.67.4)
|
||||
- React-jsi (= 0.67.4)
|
||||
- ReactCommon/turbomodule/core (= 0.67.4)
|
||||
- Flipper (0.138.0):
|
||||
- Flipper-Folly (~> 2.6)
|
||||
- Flipper-Boost-iOSX (1.76.0.1.11)
|
||||
- Flipper-DoubleConversion (3.1.7)
|
||||
- Flipper-Fmt (7.1.7)
|
||||
- Flipper-Folly (2.6.7):
|
||||
- Flipper-Boost-iOSX
|
||||
- Flipper-DoubleConversion
|
||||
- Flipper-Fmt (= 7.1.7)
|
||||
- Flipper-Glog
|
||||
- libevent (~> 2.1.12)
|
||||
- OpenSSL-Universal (= 1.1.180)
|
||||
- Flipper-Glog (0.3.6)
|
||||
- Flipper-PeerTalk (0.0.4)
|
||||
- Flipper-RSocket (1.4.3):
|
||||
- Flipper-Folly (~> 2.6)
|
||||
- FlipperKit (0.138.0):
|
||||
- FlipperKit/Core (= 0.138.0)
|
||||
- FlipperKit/Core (0.138.0):
|
||||
- Flipper (~> 0.138.0)
|
||||
- FlipperKit/CppBridge
|
||||
- FlipperKit/FBCxxFollyDynamicConvert
|
||||
- FlipperKit/FBDefines
|
||||
- FlipperKit/FKPortForwarding
|
||||
- SocketRocket (~> 0.6.0)
|
||||
- FlipperKit/CppBridge (0.138.0):
|
||||
- Flipper (~> 0.138.0)
|
||||
- FlipperKit/FBCxxFollyDynamicConvert (0.138.0):
|
||||
- Flipper-Folly (~> 2.6)
|
||||
- FlipperKit/FBDefines (0.138.0)
|
||||
- FlipperKit/FKPortForwarding (0.138.0):
|
||||
- CocoaAsyncSocket (~> 7.6)
|
||||
- Flipper-PeerTalk (~> 0.0.4)
|
||||
- FlipperKit/FlipperKitHighlightOverlay (0.138.0)
|
||||
- FlipperKit/FlipperKitLayoutHelpers (0.138.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitHighlightOverlay
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||
- FlipperKit/FlipperKitLayoutIOSDescriptors (0.138.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitHighlightOverlay
|
||||
- FlipperKit/FlipperKitLayoutHelpers
|
||||
- YogaKit (~> 1.18)
|
||||
- FlipperKit/FlipperKitLayoutPlugin (0.138.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitHighlightOverlay
|
||||
- FlipperKit/FlipperKitLayoutHelpers
|
||||
- FlipperKit/FlipperKitLayoutIOSDescriptors
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||
- YogaKit (~> 1.18)
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable (0.138.0)
|
||||
- FlipperKit/FlipperKitNetworkPlugin (0.138.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitReactPlugin (0.138.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitUserDefaultsPlugin (0.138.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/SKIOSNetworkPlugin (0.138.0):
|
||||
- FlipperKit/Core
|
||||
- FlipperKit/FlipperKitNetworkPlugin
|
||||
- fmt (6.2.1)
|
||||
- glog (0.3.5)
|
||||
- hermes-engine (0.9.0)
|
||||
@@ -102,7 +41,6 @@ PODS:
|
||||
- lottie-react-native (5.0.1):
|
||||
- lottie-ios (~> 3.2.3)
|
||||
- React-Core
|
||||
- OpenSSL-Universal (1.1.180)
|
||||
- Permission-Camera (3.3.1):
|
||||
- RNPermissions
|
||||
- Permission-PhotoLibrary (3.3.1):
|
||||
@@ -522,7 +460,6 @@ PODS:
|
||||
- Sentry/Core (= 7.11.0)
|
||||
- Sentry/Core (7.11.0)
|
||||
- simdjson (1.0.0)
|
||||
- SocketRocket (0.6.0)
|
||||
- Starscream (4.0.4)
|
||||
- SwiftyJSON (5.0.1)
|
||||
- Swime (3.0.6)
|
||||
@@ -531,8 +468,6 @@ PODS:
|
||||
- React-jsi
|
||||
- XCDYouTubeKit (2.8.2)
|
||||
- Yoga (1.14.0)
|
||||
- YogaKit (1.18.1):
|
||||
- Yoga (~> 1.14)
|
||||
- YoutubePlayer-in-WKWebView (0.3.8)
|
||||
|
||||
DEPENDENCIES:
|
||||
@@ -545,34 +480,12 @@ DEPENDENCIES:
|
||||
- EXVideoThumbnails (from `../node_modules/expo-video-thumbnails/ios`)
|
||||
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
||||
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
|
||||
- Flipper (= 0.138.0)
|
||||
- Flipper-Boost-iOSX (= 1.76.0.1.11)
|
||||
- Flipper-DoubleConversion (= 3.1.7)
|
||||
- Flipper-Fmt (= 7.1.7)
|
||||
- Flipper-Folly (= 2.6.7)
|
||||
- Flipper-Glog (= 0.3.6)
|
||||
- Flipper-PeerTalk (= 0.0.4)
|
||||
- Flipper-RSocket (= 1.4.3)
|
||||
- FlipperKit (= 0.138.0)
|
||||
- FlipperKit/Core (= 0.138.0)
|
||||
- FlipperKit/CppBridge (= 0.138.0)
|
||||
- FlipperKit/FBCxxFollyDynamicConvert (= 0.138.0)
|
||||
- FlipperKit/FBDefines (= 0.138.0)
|
||||
- FlipperKit/FKPortForwarding (= 0.138.0)
|
||||
- FlipperKit/FlipperKitHighlightOverlay (= 0.138.0)
|
||||
- FlipperKit/FlipperKitLayoutPlugin (= 0.138.0)
|
||||
- FlipperKit/FlipperKitLayoutTextSearchable (= 0.138.0)
|
||||
- FlipperKit/FlipperKitNetworkPlugin (= 0.138.0)
|
||||
- FlipperKit/FlipperKitReactPlugin (= 0.138.0)
|
||||
- FlipperKit/FlipperKitUserDefaultsPlugin (= 0.138.0)
|
||||
- FlipperKit/SKIOSNetworkPlugin (= 0.138.0)
|
||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- hermes-engine (~> 0.9.0)
|
||||
- jail-monkey (from `../node_modules/jail-monkey`)
|
||||
- libevent (~> 2.1.12)
|
||||
- lottie-ios (from `../node_modules/lottie-ios`)
|
||||
- lottie-react-native (from `../node_modules/lottie-react-native`)
|
||||
- OpenSSL-Universal (= 1.1.180)
|
||||
- Permission-Camera (from `../node_modules/react-native-permissions/ios/Camera`)
|
||||
- Permission-PhotoLibrary (from `../node_modules/react-native-permissions/ios/PhotoLibrary`)
|
||||
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
|
||||
@@ -648,31 +561,18 @@ DEPENDENCIES:
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- Alamofire
|
||||
- CocoaAsyncSocket
|
||||
- Flipper
|
||||
- Flipper-Boost-iOSX
|
||||
- Flipper-DoubleConversion
|
||||
- Flipper-Fmt
|
||||
- Flipper-Folly
|
||||
- Flipper-Glog
|
||||
- Flipper-PeerTalk
|
||||
- Flipper-RSocket
|
||||
- FlipperKit
|
||||
- fmt
|
||||
- hermes-engine
|
||||
- HMSegmentedControl
|
||||
- libevent
|
||||
- libwebp
|
||||
- OpenSSL-Universal
|
||||
- Rudder
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- Sentry
|
||||
- SocketRocket
|
||||
- SwiftyJSON
|
||||
- Swime
|
||||
- XCDYouTubeKit
|
||||
- YogaKit
|
||||
- YoutubePlayer-in-WKWebView
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
@@ -847,7 +747,6 @@ SPEC CHECKSUMS:
|
||||
Alamofire: 1c4fb5369c3fe93d2857c780d8bbe09f06f97e7c
|
||||
boost: a7c83b31436843459a1961bfd74b96033dc77234
|
||||
BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872
|
||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
|
||||
EXFileSystem: 08a3033ac372b6346becf07839e1ccef26fb1058
|
||||
Expo: 534e51e607aba8229293297da5585f4b26f50fa1
|
||||
@@ -855,15 +754,6 @@ SPEC CHECKSUMS:
|
||||
EXVideoThumbnails: 847d648d6f4bc0c1afad05caa56a487dc543445e
|
||||
FBLazyVector: f7b0632c6437e312acf6349288d9aa4cb6d59030
|
||||
FBReactNativeSpec: 0f4e1f4cfeace095694436e7c7fcc5bf4b03a0ff
|
||||
Flipper: a5ed0fd7212a369f0c0f8fe096a711355cbb22cf
|
||||
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
|
||||
Flipper-DoubleConversion: 57ffbe81ef95306cc9e69c4aa3aeeeeb58a6a28c
|
||||
Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b
|
||||
Flipper-Folly: 83af37379faa69497529e414bd43fbfc7cae259a
|
||||
Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6
|
||||
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
|
||||
Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541
|
||||
FlipperKit: 10424086e0f1b75cebf7a73427a8e3fbc793cd7b
|
||||
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
|
||||
glog: 85ecdd10ee8d8ec362ef519a6a45ff9aa27b2e85
|
||||
hermes-engine: bf7577d12ac6ccf53ab8b5af3c6ccf0dd8458c5c
|
||||
@@ -873,7 +763,6 @@ SPEC CHECKSUMS:
|
||||
libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
|
||||
lottie-ios: c058aeafa76daa4cf64d773554bccc8385d0150e
|
||||
lottie-react-native: a029a86e1689c86a07169c520ae770e84348cd20
|
||||
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
|
||||
Permission-Camera: bae27a8503530770c35aadfecbb97ec71823382a
|
||||
Permission-PhotoLibrary: ddb5a158725b29cb12e9e477e8a5f5151c66cc3c
|
||||
RCT-Folly: 803a9cfd78114b2ec0f140cfa6fa2a6bafb2d685
|
||||
@@ -942,16 +831,14 @@ SPEC CHECKSUMS:
|
||||
SDWebImageWebPCoder: f93010f3f6c031e2f8fb3081ca4ee6966c539815
|
||||
Sentry: 0c5cd63d714187b4a39c331c1f0eb04ba7868341
|
||||
simdjson: c96317b3a50dff3468a42f586ab7ed22c6ab2fd9
|
||||
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
|
||||
Starscream: 5178aed56b316f13fa3bc55694e583d35dd414d9
|
||||
SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e
|
||||
Swime: d7b2c277503b6cea317774aedc2dce05613f8b0b
|
||||
WatermelonDB: baec390a1039dcebeee959218900c978af3407c9
|
||||
XCDYouTubeKit: 79baadb0560673a67c771eba45f83e353fd12c1f
|
||||
Yoga: d6b6a80659aa3e91aaba01d0012e7edcbedcbecd
|
||||
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
||||
YoutubePlayer-in-WKWebView: 4fca3b4f6f09940077bfbae7bddb771f2b43aacd
|
||||
|
||||
PODFILE CHECKSUM: 662a6960377d343c74022598146341c50e2b0d11
|
||||
PODFILE CHECKSUM: 201f2384bf697d449c8d0025c6b76211a2e36f98
|
||||
|
||||
COCOAPODS: 1.11.3
|
||||
|
||||
@@ -19,8 +19,117 @@ index b121da3..82c1c24 100644
|
||||
<NativeDirectionalScrollView
|
||||
{...props}
|
||||
style={StyleSheet.compose(baseStyle, inner)}
|
||||
diff --git a/node_modules/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js b/node_modules/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js
|
||||
index ea21ce2..05e0def 100644
|
||||
--- a/node_modules/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js
|
||||
+++ b/node_modules/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js
|
||||
@@ -170,7 +170,9 @@ class ScrollViewStickyHeader extends React.Component<Props, State> {
|
||||
|
||||
this.props.onLayout(event);
|
||||
const child = React.Children.only(this.props.children);
|
||||
- if (child.props.onLayout) {
|
||||
+ if (child.props.onCellLayout) {
|
||||
+ child.props.onCellLayout(event, child.props.cellKey, child.props.index);
|
||||
+ } else if (child.props.onLayout) {
|
||||
child.props.onLayout(event);
|
||||
}
|
||||
};
|
||||
diff --git a/node_modules/react-native/Libraries/Lists/FlatList.js b/node_modules/react-native/Libraries/Lists/FlatList.js
|
||||
index 5e49715..88f8896 100644
|
||||
--- a/node_modules/react-native/Libraries/Lists/FlatList.js
|
||||
+++ b/node_modules/react-native/Libraries/Lists/FlatList.js
|
||||
@@ -26,6 +26,7 @@ import type {
|
||||
} from './ViewabilityHelper';
|
||||
import type {RenderItemType, RenderItemProps} from './VirtualizedList';
|
||||
import {keyExtractor as defaultKeyExtractor} from './VirtualizeUtils';
|
||||
+import memoizeOne from 'memoize-one';
|
||||
|
||||
type RequiredProps<ItemT> = {|
|
||||
/**
|
||||
@@ -141,6 +142,10 @@ type OptionalProps<ItemT> = {|
|
||||
* See `ScrollView` for flow type and further documentation.
|
||||
*/
|
||||
fadingEdgeLength?: ?number,
|
||||
+ /**
|
||||
+ * Enable an optimization to memoize the item renderer to prevent unnecessary rerenders.
|
||||
+ */
|
||||
+ strictMode?: boolean,
|
||||
|};
|
||||
|
||||
/**
|
||||
@@ -579,9 +584,14 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
};
|
||||
}
|
||||
|
||||
- _renderer = () => {
|
||||
- const {ListItemComponent, renderItem, columnWrapperStyle} = this.props;
|
||||
- const numColumns = numColumnsOrDefault(this.props.numColumns);
|
||||
+ _renderer = (
|
||||
+ ListItemComponent: ?(React.ComponentType<any> | React.Element<any>),
|
||||
+ renderItem: ?RenderItemType<ItemT>,
|
||||
+ columnWrapperStyle: ?ViewStyleProp,
|
||||
+ numColumns: ?number,
|
||||
+ extraData: ?any,
|
||||
+ ) => {
|
||||
+ const cols = numColumnsOrDefault(numColumns);
|
||||
|
||||
let virtualizedListRenderKey = ListItemComponent
|
||||
? 'ListItemComponent'
|
||||
@@ -606,7 +616,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
* This comment suppresses an error found when Flow v0.111 was deployed.
|
||||
* To see the error, delete this comment and run Flow. */
|
||||
[virtualizedListRenderKey]: (info: RenderItemProps<ItemT>) => {
|
||||
- if (numColumns > 1) {
|
||||
+ if (cols > 1) {
|
||||
const {item, index} = info;
|
||||
invariant(
|
||||
Array.isArray(item),
|
||||
@@ -617,7 +627,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
{item.map((it, kk) => {
|
||||
const element = renderer({
|
||||
item: it,
|
||||
- index: index * numColumns + kk,
|
||||
+ index: index * cols + kk,
|
||||
separators: info.separators,
|
||||
});
|
||||
return element != null ? (
|
||||
@@ -633,14 +643,19 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
};
|
||||
};
|
||||
|
||||
+ _memoizedRenderer = memoizeOne(this._renderer);
|
||||
+
|
||||
render(): React.Node {
|
||||
const {
|
||||
numColumns,
|
||||
columnWrapperStyle,
|
||||
removeClippedSubviews: _removeClippedSubviews,
|
||||
+ strictMode = false,
|
||||
...restProps
|
||||
} = this.props;
|
||||
|
||||
+ const renderer = strictMode ? this._memoizedRenderer : this._renderer;
|
||||
+
|
||||
return (
|
||||
<VirtualizedList
|
||||
{...restProps}
|
||||
@@ -652,7 +667,13 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
|
||||
removeClippedSubviews={removeClippedSubviewsOrDefault(
|
||||
_removeClippedSubviews,
|
||||
)}
|
||||
- {...this._renderer()}
|
||||
+ {...renderer(
|
||||
+ this.props.ListItemComponent,
|
||||
+ this.props.renderItem,
|
||||
+ columnWrapperStyle,
|
||||
+ numColumns,
|
||||
+ this.props.extraData,
|
||||
+ )}
|
||||
/>
|
||||
);
|
||||
}
|
||||
diff --git a/node_modules/react-native/Libraries/Lists/VirtualizedList.js b/node_modules/react-native/Libraries/Lists/VirtualizedList.js
|
||||
index 2648cc3..e0c2c13 100644
|
||||
index 2648cc3..fa2511d 100644
|
||||
--- a/node_modules/react-native/Libraries/Lists/VirtualizedList.js
|
||||
+++ b/node_modules/react-native/Libraries/Lists/VirtualizedList.js
|
||||
@@ -16,6 +16,7 @@ const ScrollView = require('../Components/ScrollView/ScrollView');
|
||||
@@ -31,7 +140,248 @@ index 2648cc3..e0c2c13 100644
|
||||
|
||||
const flattenStyle = require('../StyleSheet/flattenStyle');
|
||||
const infoLog = require('../Utilities/infoLog');
|
||||
@@ -2119,7 +2120,14 @@ function describeNestedLists(childList: {
|
||||
@@ -34,6 +35,7 @@ import type {
|
||||
ViewToken,
|
||||
ViewabilityConfigCallbackPair,
|
||||
} from './ViewabilityHelper';
|
||||
+import type {LayoutEvent} from '../Types/CoreEventTypes';
|
||||
import {
|
||||
VirtualizedListCellContextProvider,
|
||||
VirtualizedListContext,
|
||||
@@ -794,12 +796,17 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
const {
|
||||
CellRendererComponent,
|
||||
ItemSeparatorComponent,
|
||||
+ ListHeaderComponent,
|
||||
+ ListItemComponent,
|
||||
data,
|
||||
+ debug,
|
||||
getItem,
|
||||
getItemCount,
|
||||
+ getItemLayout,
|
||||
horizontal,
|
||||
+ renderItem,
|
||||
} = this.props;
|
||||
- const stickyOffset = this.props.ListHeaderComponent ? 1 : 0;
|
||||
+ const stickyOffset = ListHeaderComponent ? 1 : 0;
|
||||
const end = getItemCount(data) - 1;
|
||||
let prevCellKey;
|
||||
last = Math.min(end, last);
|
||||
@@ -814,27 +821,30 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
<CellRenderer
|
||||
CellRendererComponent={CellRendererComponent}
|
||||
ItemSeparatorComponent={ii < end ? ItemSeparatorComponent : undefined}
|
||||
+ ListItemComponent={ListItemComponent}
|
||||
cellKey={key}
|
||||
+ debug={debug}
|
||||
fillRateHelper={this._fillRateHelper}
|
||||
+ getItemLayout={getItemLayout}
|
||||
horizontal={horizontal}
|
||||
index={ii}
|
||||
inversionStyle={inversionStyle}
|
||||
item={item}
|
||||
key={key}
|
||||
prevCellKey={prevCellKey}
|
||||
+ onCellLayout={this._onCellLayout}
|
||||
onUpdateSeparators={this._onUpdateSeparators}
|
||||
- onLayout={e => this._onCellLayout(e, key, ii)}
|
||||
onUnmount={this._onCellUnmount}
|
||||
- parentProps={this.props}
|
||||
ref={ref => {
|
||||
this._cellRefs[key] = ref;
|
||||
}}
|
||||
+ renderItem={renderItem}
|
||||
/>,
|
||||
);
|
||||
prevCellKey = key;
|
||||
}
|
||||
}
|
||||
-
|
||||
+1
|
||||
_onUpdateSeparators = (keys: Array<?string>, newProps: Object) => {
|
||||
keys.forEach(key => {
|
||||
const ref = key != null && this._cellRefs[key];
|
||||
@@ -1269,7 +1279,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
- _onCellLayout(e, cellKey, index) {
|
||||
+ _onCellLayout = (e: LayoutEvent, cellKey: string, index: number): void => {
|
||||
const layout = e.nativeEvent.layout;
|
||||
const next = {
|
||||
offset: this._selectOffset(layout),
|
||||
@@ -1302,7 +1312,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
|
||||
this._computeBlankness();
|
||||
this._updateViewableItems(this.props.data);
|
||||
- }
|
||||
+ };
|
||||
|
||||
_onCellUnmount = (cellKey: string) => {
|
||||
const curr = this._frames[cellKey];
|
||||
@@ -1381,7 +1391,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
- _onLayout = (e: Object) => {
|
||||
+ _onLayout = (e: LayoutEvent) => {
|
||||
if (this._isNestedWithSameOrientation()) {
|
||||
// Need to adjust our scroll metrics to be relative to our containing
|
||||
// VirtualizedList before we can make claims about list item viewability
|
||||
@@ -1396,7 +1406,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
this._maybeCallOnEndReached();
|
||||
};
|
||||
|
||||
- _onLayoutEmpty = e => {
|
||||
+ _onLayoutEmpty = (e: LayoutEvent) => {
|
||||
this.props.onLayout && this.props.onLayout(e);
|
||||
};
|
||||
|
||||
@@ -1404,12 +1414,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||
return this._getCellKey() + '-footer';
|
||||
}
|
||||
|
||||
- _onLayoutFooter = e => {
|
||||
+ _onLayoutFooter = (e: LayoutEvent) => {
|
||||
this._triggerRemeasureForChildListsInCell(this._getFooterCellKey());
|
||||
this._footerLength = this._selectLength(e.nativeEvent.layout);
|
||||
};
|
||||
|
||||
- _onLayoutHeader = e => {
|
||||
+ _onLayoutHeader = (e: LayoutEvent) => {
|
||||
this._headerLength = this._selectLength(e.nativeEvent.layout);
|
||||
};
|
||||
|
||||
@@ -1898,32 +1908,29 @@ type CellRendererProps = {
|
||||
ItemSeparatorComponent: ?React.ComponentType<
|
||||
any | {highlighted: boolean, leadingItem: ?Item},
|
||||
>,
|
||||
+ ListItemComponent?: ?(React.ComponentType<any> | React.Element<any>),
|
||||
cellKey: string,
|
||||
+ debug?: ?boolean,
|
||||
fillRateHelper: FillRateHelper,
|
||||
+ getItemLayout?: (
|
||||
+ data: any,
|
||||
+ index: number,
|
||||
+ ) => {
|
||||
+ length: number,
|
||||
+ offset: number,
|
||||
+ index: number,
|
||||
+ ...
|
||||
+ },
|
||||
horizontal: ?boolean,
|
||||
index: number,
|
||||
inversionStyle: ViewStyleProp,
|
||||
item: Item,
|
||||
// This is extracted by ScrollViewStickyHeader
|
||||
- onLayout: (event: Object) => void,
|
||||
+ onCellLayout: (event: Object, cellKey: string, index: number) => void,
|
||||
onUnmount: (cellKey: string) => void,
|
||||
onUpdateSeparators: (cellKeys: Array<?string>, props: Object) => void,
|
||||
- parentProps: {
|
||||
- // e.g. height, y,
|
||||
- getItemLayout?: (
|
||||
- data: any,
|
||||
- index: number,
|
||||
- ) => {
|
||||
- length: number,
|
||||
- offset: number,
|
||||
- index: number,
|
||||
- ...
|
||||
- },
|
||||
- renderItem?: ?RenderItemType<Item>,
|
||||
- ListItemComponent?: ?(React.ComponentType<any> | React.Element<any>),
|
||||
- ...
|
||||
- },
|
||||
prevCellKey: ?string,
|
||||
+ renderItem?: ?RenderItemType<Item>,
|
||||
...
|
||||
};
|
||||
|
||||
@@ -1935,7 +1942,7 @@ type CellRendererState = {
|
||||
...
|
||||
};
|
||||
|
||||
-class CellRenderer extends React.Component<
|
||||
+class CellRenderer extends React.PureComponent<
|
||||
CellRendererProps,
|
||||
CellRendererState,
|
||||
> {
|
||||
@@ -1950,12 +1957,16 @@ class CellRenderer extends React.Component<
|
||||
props: CellRendererProps,
|
||||
prevState: CellRendererState,
|
||||
): ?CellRendererState {
|
||||
- return {
|
||||
- separatorProps: {
|
||||
- ...prevState.separatorProps,
|
||||
- leadingItem: props.item,
|
||||
- },
|
||||
- };
|
||||
+ if (prevState.separatorProps.leadingItem !== props.item) {
|
||||
+ return {
|
||||
+ separatorProps: {
|
||||
+ ...prevState.separatorProps,
|
||||
+ leadingItem: props.item,
|
||||
+ },
|
||||
+ };
|
||||
+ } else {
|
||||
+ return prevState;
|
||||
+ }
|
||||
}
|
||||
|
||||
// TODO: consider factoring separator stuff out of VirtualizedList into FlatList since it's not
|
||||
@@ -1992,6 +2003,15 @@ class CellRenderer extends React.Component<
|
||||
this.props.onUnmount(this.props.cellKey);
|
||||
}
|
||||
|
||||
+ _onLayout = (nativeEvent: LayoutEvent): void => {
|
||||
+ this.props.onCellLayout &&
|
||||
+ this.props.onCellLayout(
|
||||
+ nativeEvent,
|
||||
+ this.props.cellKey,
|
||||
+ this.props.index,
|
||||
+ );
|
||||
+ };
|
||||
+
|
||||
_renderElement(renderItem, ListItemComponent, item, index) {
|
||||
if (renderItem && ListItemComponent) {
|
||||
console.warn(
|
||||
@@ -2032,14 +2052,16 @@ class CellRenderer extends React.Component<
|
||||
const {
|
||||
CellRendererComponent,
|
||||
ItemSeparatorComponent,
|
||||
+ ListItemComponent,
|
||||
+ debug,
|
||||
fillRateHelper,
|
||||
+ getItemLayout,
|
||||
horizontal,
|
||||
item,
|
||||
index,
|
||||
inversionStyle,
|
||||
- parentProps,
|
||||
+ renderItem,
|
||||
} = this.props;
|
||||
- const {renderItem, getItemLayout, ListItemComponent} = parentProps;
|
||||
const element = this._renderElement(
|
||||
renderItem,
|
||||
ListItemComponent,
|
||||
@@ -2048,12 +2070,10 @@ class CellRenderer extends React.Component<
|
||||
);
|
||||
|
||||
const onLayout =
|
||||
- /* $FlowFixMe[prop-missing] (>=0.68.0 site=react_native_fb) This comment
|
||||
- * suppresses an error found when Flow v0.68 was deployed. To see the
|
||||
- * error delete this comment and run Flow. */
|
||||
- getItemLayout && !parentProps.debug && !fillRateHelper.enabled()
|
||||
+ (getItemLayout && !debug && !fillRateHelper.enabled()) ||
|
||||
+ !this.props.onCellLayout
|
||||
? undefined
|
||||
- : this.props.onLayout;
|
||||
+ : this._onLayout;
|
||||
// NOTE: that when this is a sticky header, `onLayout` will get automatically extracted and
|
||||
// called explicitly by `ScrollViewStickyHeader`.
|
||||
const itemSeparator = ItemSeparatorComponent && (
|
||||
@@ -2119,7 +2139,14 @@ function describeNestedLists(childList: {
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
verticallyInverted: {
|
||||
@@ -48,7 +398,7 @@ index 2648cc3..e0c2c13 100644
|
||||
horizontallyInverted: {
|
||||
transform: [{scaleX: -1}],
|
||||
diff --git a/node_modules/react-native/react.gradle b/node_modules/react-native/react.gradle
|
||||
index d9e2714..bed8756 100644
|
||||
index 2aefa12..dea1771 100644
|
||||
--- a/node_modules/react-native/react.gradle
|
||||
+++ b/node_modules/react-native/react.gradle
|
||||
@@ -88,7 +88,7 @@ def enableHermesForVariant = config.enableHermesForVariant ?: {
|
||||
|
||||
@@ -68,7 +68,7 @@ index a3aafb2..141f0cd 100644
|
||||
namespace Animated {
|
||||
type Nullable<T> = T | null | undefined;
|
||||
diff --git a/node_modules/react-native-reanimated/src/createAnimatedComponent.tsx b/node_modules/react-native-reanimated/src/createAnimatedComponent.tsx
|
||||
index 6bead55..1cf0c3f 100644
|
||||
index 6bead55..7ae12ba 100644
|
||||
--- a/node_modules/react-native-reanimated/src/createAnimatedComponent.tsx
|
||||
+++ b/node_modules/react-native-reanimated/src/createAnimatedComponent.tsx
|
||||
@@ -161,7 +161,7 @@ interface ComponentRef extends Component {
|
||||
@@ -80,6 +80,52 @@ index 6bead55..1cf0c3f 100644
|
||||
ref?: Ref<Component>;
|
||||
collapsable?: boolean;
|
||||
}
|
||||
@@ -195,6 +195,7 @@ export default function createAnimatedComponent(
|
||||
_isFirstRender = true;
|
||||
animatedStyle: { value: StyleProps } = { value: {} };
|
||||
initialStyle = {};
|
||||
+ _lastSentStyle?: StyleProps;
|
||||
sv: SharedValue<null | Record<string, unknown>> | null;
|
||||
_propsAnimated?: PropsAnimated;
|
||||
_component: ComponentRef | null = null;
|
||||
@@ -580,17 +581,24 @@ export default function createAnimatedComponent(
|
||||
|
||||
_filterNonAnimatedStyle(inputStyle: StyleProps) {
|
||||
const style: StyleProps = {};
|
||||
+ let changed = false;
|
||||
for (const key in inputStyle) {
|
||||
const value = inputStyle[key];
|
||||
if (!hasAnimatedNodes(value)) {
|
||||
style[key] = value;
|
||||
+ changed = changed || style[key] !== this._lastSentStyle?.[key];
|
||||
} else if (value instanceof AnimatedValue) {
|
||||
// if any style in animated component is set directly to the `Value` we set those styles to the first value of `Value` node in order
|
||||
// to avoid flash of default styles when `Value` is being asynchrounously sent via bridge and initialized in the native side.
|
||||
style[key] = value._startingValue;
|
||||
+ changed = changed || style[key] !== this._lastSentStyle?.[key]
|
||||
}
|
||||
}
|
||||
- return style;
|
||||
+ if (changed) {
|
||||
+ return style;
|
||||
+ } else {
|
||||
+ return this._lastSentStyle;
|
||||
+ }
|
||||
}
|
||||
|
||||
_filterNonAnimatedProps(
|
||||
@@ -617,9 +625,11 @@ export default function createAnimatedComponent(
|
||||
return style;
|
||||
}
|
||||
});
|
||||
+
|
||||
props[key] = this._filterNonAnimatedStyle(
|
||||
StyleSheet.flatten(processedStyle)
|
||||
);
|
||||
+ this._lastSentStyle = props[key]
|
||||
} else if (key === 'animatedProps') {
|
||||
const animatedProp = inputProps.animatedProps as Partial<
|
||||
AnimatedComponentProps<AnimatedProps>
|
||||
diff --git a/node_modules/react-native-reanimated/src/reanimated2/component/FlatList.tsx b/node_modules/react-native-reanimated/src/reanimated2/component/FlatList.tsx
|
||||
index f02fc16..32bf952 100644
|
||||
--- a/node_modules/react-native-reanimated/src/reanimated2/component/FlatList.tsx
|
||||
|
||||
2
types/database/models/servers/channel.d.ts
vendored
2
types/database/models/servers/channel.d.ts
vendored
@@ -53,7 +53,7 @@ export default class ChannelModel extends Model {
|
||||
teamId: string;
|
||||
|
||||
/** type : The type of the channel ( e.g. G: group messages, D: direct messages, P: private channel and O: public channel) */
|
||||
type: string;
|
||||
type: ChannelType;
|
||||
|
||||
/** members : Users belonging to this channel */
|
||||
members: Query<ChannelMembershipModel>;
|
||||
|
||||
Reference in New Issue
Block a user