void>(func: T, duration: number): T;
5 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/extractPathFromURL.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | declare export default function extractPathFromURL(prefixes: string[], url: string): string | void;
5 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/theming/DarkTheme.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type Theme } from '../types';
5 | declare var DarkTheme: Theme;
6 | export default DarkTheme;
7 |
--------------------------------------------------------------------------------
/types/@react-native-clipboard/clipboard/dist/index.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { Clipboard } from './Clipboard';
5 | export { useClipboard } from './useClipboard';
6 | export default Clipboard;
7 |
--------------------------------------------------------------------------------
/jest/savePromise.js:
--------------------------------------------------------------------------------
1 | // For facebook/jest#10221. Before Jest's setup files have run, take
2 | // note of what the natural value of `global.Promise` is, so we can
3 | // restore it in restorePromise.js.
4 | global.originalPromise = Promise;
5 |
--------------------------------------------------------------------------------
/types/@react-native-camera-roll/camera-roll/lib/typescript/index.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | export * from './useCameraRoll';
5 | export * from './CameraRoll';
6 | export * from './CameraRollIOSPermission';
7 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/theming/DefaultTheme.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type Theme } from '../types';
5 | declare var DefaultTheme: Theme;
6 | export default DefaultTheme;
7 |
--------------------------------------------------------------------------------
/types/react-native-safe-area-context/lib/typescript/src/NativeSafeAreaProvider.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import NativeSafeAreaProvider from './specs/NativeSafeAreaProvider';
5 | export { NativeSafeAreaProvider };
6 |
--------------------------------------------------------------------------------
/flow-typed/npm/flow-bin_v0.x.x.js:
--------------------------------------------------------------------------------
1 | // flow-typed signature: 4e6a5da3290fe9ea49e6bcdced64f358
2 | // flow-typed version: c6154227d1/flow-bin_v0.x.x/flow_>=v0.25.x <=v0.103.x
3 |
4 | declare module "flow-bin" {
5 | declare module.exports: string;
6 | }
7 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/useLinkBuilder.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | declare export default function useLinkBuilder(): (
5 | name: string,
6 | params?: { ... } | void,
7 | ) => string | void;
8 |
--------------------------------------------------------------------------------
/types/expo-modules-core/build/NativeViewManagerAdapter.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import * as React from 'react';
5 | declare export function requireNativeViewManager(viewName: string): React.ComponentType
;
6 |
--------------------------------------------------------------------------------
/types/expo-modules-core/build/errors/CodedError.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | declare export class CodedError extends Error {
5 | code: string;
6 | info?: any;
7 | constructor(code: string, message: string): any;
8 | }
9 |
--------------------------------------------------------------------------------
/types/react-native-reanimated/index.js.flow:
--------------------------------------------------------------------------------
1 | // @flow
2 | // Simple manual stubs, while we focus on other libraries.
3 |
4 | export class Node {}
5 | export class Value {}
6 | export class View {}
7 |
8 | export default { Node, Value, View };
9 |
--------------------------------------------------------------------------------
/.tx/config:
--------------------------------------------------------------------------------
1 | [main]
2 | host = https://www.transifex.com
3 |
4 | [o:zulip:p:zulip:r:mobile]
5 | type = KEYVALUEJSON
6 | file_filter = static/translations/messages_.json
7 | source_lang = en
8 | source_file = static/translations/messages_en.json
9 |
--------------------------------------------------------------------------------
/src/webview/js/sendMessage.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { WebViewOutboundEvent } from '../handleOutboundEvents';
3 |
4 | export default (msg: WebViewOutboundEvent) => {
5 | window.ReactNativeWebView.postMessage(JSON.stringify(msg));
6 | };
7 |
--------------------------------------------------------------------------------
/tools/gradle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | this_dir=${BASH_SOURCE[0]%/*}
5 | . "${this_dir}"/lib/ensure-coreutils.sh
6 | root_dir=$(readlink -f "${this_dir}"/..)
7 |
8 | exec "${root_dir}"/android/gradlew -p "${root_dir}"/android "$@"
9 |
--------------------------------------------------------------------------------
/types/@react-navigation/bottom-tabs/lib/typescript/src/utils/useWindowDimensions.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | declare export default function useWindowDimensions(): {
5 | height: number,
6 | width: number,
7 | ...
8 | };
9 |
--------------------------------------------------------------------------------
/types/expo-modules-core/build/NativeViewManagerAdapter.native.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import * as React from 'react';
5 | declare export function requireNativeViewManager(viewName: string): React.ComponentType
;
6 |
--------------------------------------------------------------------------------
/src/api/apiTypes.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | export type * from './transportTypes';
4 | export type * from './modelTypes';
5 | export type * from './eventTypes';
6 | export type * from './initialDataTypes';
7 | export type * from './permissionsTypes';
8 |
--------------------------------------------------------------------------------
/flow-typed/npm/deep-freeze_v0.0.1.js:
--------------------------------------------------------------------------------
1 | // flow-typed signature: 2a3c1f56ec559e89dd447792f9debaf6
2 | // flow-typed version: c6154227d1/deep-freeze_v0.0.1/flow_>=v0.25.x <=v0.103.x
3 |
4 | declare module 'deep-freeze' {
5 | declare module.exports: (o: T) => T;
6 | }
7 |
--------------------------------------------------------------------------------
/ios/ZulipMobile/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char *argv[])
6 | {
7 | @autoreleasepool {
8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/types/react-native-safe-area-context/lib/typescript/src/index.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | export * from './SafeAreaContext';
5 | export * from './SafeAreaView';
6 | export * from './InitialWindow';
7 | export * from './SafeArea.types';
8 |
--------------------------------------------------------------------------------
/src/api/mark_as_read/markAllAsRead.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponse, Auth } from '../transportTypes';
3 | import { apiPost } from '../apiFetch';
4 |
5 | export default async (auth: Auth): Promise => apiPost(auth, 'mark_all_as_read');
6 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useFocusEffect.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | type EffectCallback = () => void | void | (() => void);
5 | declare export default function useFocusEffect(effect: EffectCallback): void;
6 | export {};
7 |
--------------------------------------------------------------------------------
/types/expo-screen-orientation/plugin/build/withScreenOrientation.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 |
5 | // This module seems pretty useless, so we've erased everything in it. If
6 | // that seems wrong, we can process it (or part of it) as usual.
7 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/useThenable.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { PromiseLike } from 'tsflower/subst/lib';
5 | declare export default function useThenable(create: () => PromiseLike): [boolean, T | void];
6 |
--------------------------------------------------------------------------------
/types/expo-modules-core/build/NativeModulesProxy.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type ProxyNativeModule } from './NativeModulesProxy.types';
5 | declare var _default: { [moduleName: string]: ProxyNativeModule, ... };
6 | export default _default;
7 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/theming/useTheme.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Theme as $tsflower_import_type$_$_2e__2e_$Theme } from '..';
5 | declare export default function useTheme(): $tsflower_import_type$_$_2e__2e_$Theme;
6 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/utils/getInvertedMultiplier.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type GestureDirection } from '../types';
5 | declare export default function getInvertedMultiplier(gestureDirection: GestureDirection): 1 | -1;
6 |
--------------------------------------------------------------------------------
/types/expo-modules-core/build/errors/UnavailabilityError.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { CodedError } from './CodedError';
5 | declare export class UnavailabilityError extends CodedError {
6 | constructor(moduleName: string, propertyName: string): any;
7 | }
8 |
--------------------------------------------------------------------------------
/types/expo-modules-core/build/NativeModulesProxy.native.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type ProxyNativeModule } from './NativeModulesProxy.types';
5 | declare var NativeModulesProxy: { [moduleName: string]: ProxyNativeModule, ... };
6 | export default NativeModulesProxy;
7 |
--------------------------------------------------------------------------------
/types/expo-modules-core/build/NativeModulesProxy.types.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | export type ProxyNativeModule = {
5 | [propertyName: string]: any,
6 | addListener: (eventName: string) => void,
7 | removeListeners: (count: number) => void,
8 | ...
9 | };
10 |
--------------------------------------------------------------------------------
/src/__flow-tests__/types-test.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Action } from '../types';
3 |
4 | // Assert that Action does not allow arbitrary objects.
5 | {
6 | const foo = { nonexistent_key: 'bar' };
7 | // $FlowExpectedError[incompatible-type]
8 | const bar: Action = foo;
9 | }
10 |
--------------------------------------------------------------------------------
/src/drafts/draftsSelectors.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Narrow, PerAccountState } from '../types';
3 | import { keyFromNarrow } from '../utils/narrow';
4 |
5 | export const getDraftForNarrow = (state: PerAccountState, narrow: Narrow): string =>
6 | state.drafts[keyFromNarrow(narrow)] || '';
7 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useRoute.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type ParamListBase } from '@react-navigation/routers';
5 | import { type RouteProp } from './types';
6 | declare export default function useRoute>(): T;
7 |
--------------------------------------------------------------------------------
/types/expo-modules-core/build/sweet/NativeErrorManager.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { ProxyNativeModule as $tsflower_import_type$_$_2e__2e_$ProxyNativeModule } from '..';
5 | declare var _default: $tsflower_import_type$_$_2e__2e_$ProxyNativeModule;
6 | export default _default;
7 |
--------------------------------------------------------------------------------
/tools/verify-webview-js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | oldjs=$(<./src/webview/js/generatedEs3.js)
3 |
4 | tools/generate-webview-js
5 |
6 | newjs=$(<./src/webview/js/generatedEs3.js)
7 |
8 | if [ "$oldjs" != "$newjs" ]; then
9 | echo "generatedEs3.js is not updated with current js file."
10 | exit 1
11 | fi
12 |
--------------------------------------------------------------------------------
/ios/ZulipMobile.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useNavigation.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type ParamListBase } from '@react-navigation/routers';
5 | import { type NavigationProp } from './types';
6 | declare export default function useNavigation>(): T;
7 |
--------------------------------------------------------------------------------
/types/react-native-safe-area-context/lib/typescript/src/InitialWindow.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type EdgeInsets, type Metrics } from './SafeArea.types';
5 | declare export var initialWindowMetrics: Metrics | null;
6 | declare export var initialWindowSafeAreaInsets: EdgeInsets | null;
7 |
--------------------------------------------------------------------------------
/ios/ZulipMobile.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/ZulipMobile/ZLPConstantsBridge.m:
--------------------------------------------------------------------------------
1 | #import "React/RCTBridgeModule.h"
2 |
3 | // Register the ZLPConstants implementation with React Native, needed
4 | // because ZLPConstants is in Swift:
5 | // https://reactnative.dev/docs/0.68/native-modules-ios#exporting-swift
6 | @interface RCT_EXTERN_MODULE(ZLPConstants, NSObject)
7 | @end
8 |
--------------------------------------------------------------------------------
/android/app/src/main/res/anim/slide_in_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/anim/slide_in_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/anim/slide_out_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/anim/slide_out_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/src/drafts/draftsActions.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Narrow, PerAccountAction } from '../types';
3 | import { DRAFT_UPDATE } from '../actionConstants';
4 |
5 | export const draftUpdate = (narrow: Narrow, content: string): PerAccountAction => ({
6 | type: DRAFT_UPDATE,
7 | narrow,
8 | content,
9 | });
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/color.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ffffff
4 | #d7ccc8
5 |
6 | #6492fe
7 |
8 |
--------------------------------------------------------------------------------
/src/api/mark_as_read/markStreamAsRead.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponse, Auth } from '../transportTypes';
3 | import { apiPost } from '../apiFetch';
4 |
5 | export default async (auth: Auth, streamId: number): Promise =>
6 | apiPost(auth, 'mark_stream_as_read', {
7 | stream_id: streamId,
8 | });
9 |
--------------------------------------------------------------------------------
/src/third/redux-persist/index.js:
--------------------------------------------------------------------------------
1 | // @flow strict-local
2 |
3 | import autoRehydrate from './autoRehydrate';
4 | import createPersistor from './createPersistor';
5 | import persistStore from './persistStore';
6 |
7 | export type { Persistor, Config } from './types';
8 |
9 | export { autoRehydrate, createPersistor, persistStore };
10 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/utils/getDistanceForDirection.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type GestureDirection, type Layout } from '../types';
5 | declare export default function getDistanceForDirection(
6 | layout: Layout,
7 | gestureDirection: GestureDirection,
8 | ): number;
9 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useNavigationState.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type NavigationState } from '@react-navigation/routers';
5 | type Selector = (state: NavigationState<>) => T;
6 | declare export default function useNavigationState(selector: Selector): T;
7 | export {};
8 |
--------------------------------------------------------------------------------
/types/expo-application/build/ExpoApplication.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { ProxyNativeModule as $tsflower_import_type$_$expo_2d_modules_2d_core$ProxyNativeModule } from 'expo-modules-core';
5 | declare var _default: $tsflower_import_type$_$expo_2d_modules_2d_core$ProxyNativeModule;
6 | export default _default;
7 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/utils/HeaderHeightContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | declare var _default: $tsflower_subst$React$Context;
7 | export default _default;
8 |
--------------------------------------------------------------------------------
/types/expo-mail-composer/build/ExpoMailComposer.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { ProxyNativeModule as $tsflower_import_type$_$expo_2d_modules_2d_core$ProxyNativeModule } from 'expo-modules-core';
5 | declare var _default: $tsflower_import_type$_$expo_2d_modules_2d_core$ProxyNativeModule;
6 | export default _default;
7 |
--------------------------------------------------------------------------------
/types/expo-modules-core/build/deprecate.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | declare export default function deprecate(
5 | library: string,
6 | deprecatedAPI: string,
7 | options?: {
8 | replacement?: string,
9 | currentVersion?: string,
10 | versionToRemove?: string,
11 | ...
12 | },
13 | ): void;
14 |
--------------------------------------------------------------------------------
/types/expo-web-browser/build/ExpoWebBrowser.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { ProxyNativeModule as $tsflower_import_type$_$expo_2d_modules_2d_core$ProxyNativeModule, } from "expo-modules-core";
5 | declare var _default: $tsflower_import_type$_$expo_2d_modules_2d_core$ProxyNativeModule;
6 | export default _default;
7 |
--------------------------------------------------------------------------------
/src/settings/settingsSelectors.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ColorSchemeName } from 'react-native/Libraries/Utilities/NativeAppearance';
3 | import type { ThemeSetting, ThemeName } from '../reduxTypes';
4 |
5 | export const getThemeToUse = (theme: ThemeSetting, osScheme: ?ColorSchemeName): ThemeName =>
6 | theme === 'default' ? 'light' : 'dark';
7 |
--------------------------------------------------------------------------------
/types/@react-navigation/bottom-tabs/lib/typescript/src/utils/BottomTabBarHeightContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | declare var _default: $tsflower_subst$React$Context;
7 | export default _default;
8 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/utils/HeaderShownContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | declare var HeaderShownContext: $tsflower_subst$React$Context;
7 | export default HeaderShownContext;
8 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/utils/useCardAnimation.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { StackCardInterpolationProps as $tsflower_import_type$_$_2e__2e_$StackCardInterpolationProps } from '..';
5 | declare export default function useCardAnimation(): $tsflower_import_type$_$_2e__2e_$StackCardInterpolationProps;
6 |
--------------------------------------------------------------------------------
/types/expo-screen-orientation/build/ExpoScreenOrientation.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { ProxyNativeModule as $tsflower_import_type$_$expo_2d_modules_2d_core$ProxyNativeModule } from 'expo-modules-core';
5 | declare var _default: $tsflower_import_type$_$expo_2d_modules_2d_core$ProxyNativeModule;
6 | export default _default;
7 |
--------------------------------------------------------------------------------
/src/isAppOwnDomain.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import config from './config';
3 |
4 | /**
5 | * Whether a URL is hosted by the same org that publishes the app.
6 | */
7 | export default function isAppOwnDomain(url: URL): boolean {
8 | return config.appOwnDomains.some(
9 | domain => url.host === domain || url.host.endsWith(`.${domain}`),
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/src/lightbox/share.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import { Share } from 'react-native';
3 |
4 | import { BRAND_COLOR } from '../styles';
5 |
6 | export default (url: string) => {
7 | const shareOptions = {
8 | message: url,
9 | title: 'Shared using Zulip!',
10 | };
11 | Share.share(shareOptions, { tintColor: BRAND_COLOR }).catch(err => {});
12 | };
13 |
--------------------------------------------------------------------------------
/src/api/deleteEventQueue.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from './transportTypes';
3 | import { apiDelete } from './apiFetch';
4 |
5 | /** See https://zulip.com/api/delete-queue */
6 | export default (auth: Auth, queueId: string): Promise =>
7 | apiDelete(auth, 'events', {
8 | queue_id: queueId,
9 | });
10 |
--------------------------------------------------------------------------------
/types/expo-modules-core/build/requireNativeModule.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | /* tsflower-unimplemented: ModuleDeclaration */
5 | /* declare global {
6 | var ExpoModules: undefined | {
7 | [key: string]: any;
8 | };
9 | } */
10 |
11 | declare export function requireNativeModule(moduleName: string): ModuleType;
12 |
--------------------------------------------------------------------------------
/types/react-native-tab-view/lib/typescript/src/memoize.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | declare export default function memoize<
5 | Result,
6 | Deps: /* tsflower-warning: unimplemented: 'readonly' as type operator */
7 | any[] /* readonly any[] */,
8 | >(
9 | callback: (...deps: Deps) => Result,
10 | ): (...dependencies: Deps) => Result;
11 |
--------------------------------------------------------------------------------
/ios/ZulipMobile/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 |
5 | #import
6 |
7 | @interface AppDelegate : EXAppDelegateWrapper
8 | @property (nonatomic, strong) UIWindow *window;
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/src/api/mark_as_read/markTopicAsRead.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponse, Auth } from '../transportTypes';
3 | import { apiPost } from '../apiFetch';
4 |
5 | export default async (auth: Auth, streamId: number, topic: string): Promise =>
6 | apiPost(auth, 'mark_topic_as_read', {
7 | stream_id: streamId,
8 | topic_name: topic,
9 | });
10 |
--------------------------------------------------------------------------------
/types/@react-navigation/bottom-tabs/lib/typescript/src/utils/BottomTabBarHeightCallbackContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | declare var _default: $tsflower_subst$React$Context<(height: number) => void | void>;
7 | export default _default;
8 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/utils/memoize.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | declare export default function memoize<
5 | Result,
6 | Deps: /* tsflower-warning: unimplemented: 'readonly' as type operator */
7 | any[] /* readonly any[] */,
8 | >(
9 | callback: (...deps: Deps) => Result,
10 | ): (...dependencies: Deps) => Result;
11 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | // React Navigation requires this react-native-gesture-handler import,
3 | // as the very first import of this entry-point file. See our #5373.
4 | import 'react-native-gesture-handler';
5 | import { AppRegistry } from 'react-native';
6 | import ZulipMobile from './src/ZulipMobile';
7 |
8 | AppRegistry.registerComponent('ZulipMobile', () => ZulipMobile);
9 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/theming/ThemeContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | import { type Theme } from '../types';
7 | declare var ThemeContext: $tsflower_subst$React$Context;
8 | export default ThemeContext;
9 |
--------------------------------------------------------------------------------
/src/common/__tests__/getStatusBarStyle-test.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import { getStatusBarStyle } from '../ZulipStatusBar';
3 |
4 | describe('getStatusBarStyle', () => {
5 | test('return bar style according to given color', () => {
6 | expect(getStatusBarStyle('#fff')).toEqual('dark-content');
7 | expect(getStatusBarStyle('#000')).toEqual('light-content');
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/src/nativeModules/ShareFileAndroid.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | /**
3 | * This exposes the native CustomTabsAndroid module as a JS module. This has a
4 | * function 'openURL' which takes the following parameters:
5 | *
6 | * 1. String url: A url to be opened in customTabs
7 | */
8 | import { NativeModules } from 'react-native';
9 |
10 | export default NativeModules.ShareFileAndroid;
11 |
--------------------------------------------------------------------------------
/src/settings/settingsActions.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { AccountIndependentAction, GlobalSettingsState } from '../types';
3 | import { SET_GLOBAL_SETTINGS } from '../actionConstants';
4 |
5 | export const setGlobalSettings = (
6 | update: $Shape<$Exact>,
7 | ): AccountIndependentAction => ({
8 | type: SET_GLOBAL_SETTINGS,
9 | update,
10 | });
11 |
--------------------------------------------------------------------------------
/types/expo-mail-composer/build/MailComposer.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type MailComposerOptions, type MailComposerResult } from './MailComposer.types';
5 | declare export function composeAsync(options: MailComposerOptions): Promise;
6 | declare export function isAvailableAsync(): Promise;
7 | export * from './MailComposer.types';
8 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/static/assets/fonts/zulip-icons.map.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is @generated -- do not edit.
3 | * To make changes, see tools/build-icon-font.
4 | *
5 | * @flow strict
6 | */
7 | export default {
8 | "bot": 61697,
9 | "ellipsis-v-solid": 61698,
10 | "gif": 61699,
11 | "globe": 61700,
12 | "language": 61701,
13 | "mute": 61702,
14 | "readreceipts": 61703,
15 | "follow": 61704
16 | };
17 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/checkLegacyPathConfig.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type PathConfigMap } from './types';
5 |
6 | type Options = {
7 | initialRouteName?: string,
8 | screens: PathConfigMap,
9 | ...
10 | };
11 |
12 | declare export default function checkLegacyPathConfig(config?: Options): [boolean, Options | void];
13 | export {};
14 |
--------------------------------------------------------------------------------
/types/@react-navigation/material-top-tabs/lib/typescript/src/views/MaterialTopTabBar.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { JSX$Element as $tsflower_subst$React$JSX$Element } from 'tsflower/subst/react';
5 | import { type MaterialTopTabBarProps } from '../types';
6 | declare export default function TabBarTop(
7 | props: MaterialTopTabBarProps,
8 | ): $tsflower_subst$React$JSX$Element;
9 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/CurrentRenderContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | declare var CurrentRenderContext: $tsflower_subst$React$Context<{
7 | options?: { ... } | void,
8 | ...
9 | } | void>;
10 | export default CurrentRenderContext;
11 |
--------------------------------------------------------------------------------
/ios/ZulipMobile/ZulipMobile.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | aps-environment
6 | development
7 | com.apple.developer.applesignin
8 |
9 | Default
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useRouteCache.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type NavigationState, type Route } from '@react-navigation/routers';
5 | declare export var SUPPRESS_STATE_ACCESS_WARNING: { value: boolean, ... };
6 | declare export default function useRouteCache>(
7 | routes: $ElementType,
8 | ): Route[];
9 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/LinkingContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | import { type LinkingOptions } from './types';
7 | declare var LinkingContext: $tsflower_subst$React$Context<{ options: LinkingOptions | void, ... }>;
8 | export default LinkingContext;
9 |
--------------------------------------------------------------------------------
/jest/presetIos.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The preset we're making tweaks to.
3 | */
4 | const basePreset = require('../node_modules/jest-expo/ios/jest-preset.js');
5 |
6 | // See comment in our Jest config about how this is used.
7 | module.exports = {
8 | ...basePreset,
9 | setupFiles: [
10 | require.resolve('./savePromise.js'),
11 | ...basePreset.setupFiles,
12 | require.resolve('./restorePromise.js'),
13 | ],
14 | };
15 |
--------------------------------------------------------------------------------
/src/__tests__/jsBackport-test.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | import { objectFromEntries } from '../jsBackport';
4 |
5 | describe('objectFromEntries', () => {
6 | test('basic', () => {
7 | expect(
8 | // prettier-ignore
9 | objectFromEntries([['number', 1], ['null', null], ['obj', {}], ['undef', undefined]]),
10 | ).toStrictEqual({ number: 1, null: null, obj: {}, undef: undefined });
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/api/devFetchApiKey.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from './transportTypes';
3 | import { apiPost } from './apiFetch';
4 |
5 | type ApiResponseDevFetchApiKey = {|
6 | ...$Exact,
7 | api_key: string,
8 | |};
9 |
10 | export default (auth: Auth, email: string): Promise =>
11 | apiPost(auth, 'dev_fetch_api_key', { username: email });
12 |
--------------------------------------------------------------------------------
/src/nav/globalTypes.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | import type { AppNavigatorParamList } from './AppNavigator';
4 | import type { SharingNavigatorParamList } from '../sharing/SharingScreen';
5 | import type { MainTabsNavigatorParamList } from '../main/MainTabsScreen';
6 |
7 | export type GlobalParamList = {|
8 | ...AppNavigatorParamList,
9 | ...SharingNavigatorParamList,
10 | ...MainTabsNavigatorParamList,
11 | |};
12 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/utils/CardAnimationContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | import { type StackCardInterpolationProps } from '../types';
7 | declare var _default: $tsflower_subst$React$Context;
8 | export default _default;
9 |
--------------------------------------------------------------------------------
/jest/presetAndroid.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The preset we're making tweaks to.
3 | */
4 | const basePreset = require('../node_modules/jest-expo/android/jest-preset.js');
5 |
6 | // See comment in our Jest config about how this is used.
7 | module.exports = {
8 | ...basePreset,
9 | setupFiles: [
10 | require.resolve('./savePromise.js'),
11 | ...basePreset.setupFiles,
12 | require.resolve('./restorePromise.js'),
13 | ],
14 | };
15 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/StaticContainer.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { MemoExoticComponent as $tsflower_subst$React$MemoExoticComponent } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | declare function StaticContainer(props: any): any;
7 | declare var _default: $tsflower_subst$React$MemoExoticComponent;
8 | export default _default;
9 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/views/Header/Header.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { NamedExoticComponent as $tsflower_subst$React$NamedExoticComponent } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | import { type StackHeaderProps } from '../../types';
7 | declare var _default: $tsflower_subst$React$NamedExoticComponent;
8 | export default _default;
9 |
--------------------------------------------------------------------------------
/src/api/messages/deleteTopic.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponse, Auth } from '../transportTypes';
3 | import { apiPost } from '../apiFetch';
4 |
5 | /**
6 | * Delete all messages in a stream for a given topic.
7 | */
8 | export default async (auth: Auth, streamId: number, topicName: string): Promise =>
9 | apiPost(auth, `streams/${streamId}/delete_topic`, {
10 | topic_name: topicName,
11 | });
12 |
--------------------------------------------------------------------------------
/tools/zulip-icon-debug.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/NavigationRouteContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | import { type Route } from '@react-navigation/routers';
7 | declare var NavigationContext: $tsflower_subst$React$Context | void>;
8 | export default NavigationContext;
9 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/views/Header/HeaderBackButton.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { JSX$Element as $tsflower_subst$React$JSX$Element } from 'tsflower/subst/react';
5 | import { type StackHeaderLeftButtonProps } from '../../types';
6 | type Props = StackHeaderLeftButtonProps;
7 | declare export default function HeaderBackButton(Props): $tsflower_subst$React$JSX$Element;
8 | export {};
9 |
--------------------------------------------------------------------------------
/src/styles/navStyles.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import { BRAND_COLOR } from './constants';
3 |
4 | export const statics = {
5 | navWrapper: {
6 | flex: 1,
7 | flexDirection: 'row',
8 | alignItems: 'center',
9 | justifyContent: 'flex-start',
10 | },
11 | navSubtitle: {
12 | fontSize: 13,
13 | },
14 | navTitle: {
15 | color: BRAND_COLOR,
16 | textAlign: 'left',
17 | fontSize: 20,
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/checkSerializable.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | type ReactText = string | number; // verbatim from @types/react
5 |
6 | declare export default function checkSerializable(o: { [key: string]: any, ... }):
7 | | { serializable: true, ... }
8 | | {
9 | serializable: false,
10 | location: ReactText[],
11 | reason: string,
12 | ...
13 | };
14 |
--------------------------------------------------------------------------------
/src/api/typing.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponse, Auth } from './transportTypes';
3 | import { apiPost } from './apiFetch';
4 |
5 | type TypingOperation = 'start' | 'stop';
6 |
7 | /** See https://zulip.com/api/set-typing-status */
8 | export default (auth: Auth, recipients: string, operation: TypingOperation): Promise =>
9 | apiPost(auth, 'typing', {
10 | to: recipients,
11 | op: operation,
12 | });
13 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/useBackButton.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { RefObject as $tsflower_subst$React$RefObject } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | import { type NavigationContainerRef } from '@react-navigation/core';
7 | declare export default function useBackButton(
8 | ref: $tsflower_subst$React$RefObject,
9 | ): void;
10 |
--------------------------------------------------------------------------------
/src/api/notifications/sendTestNotification.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | import type { ApiResponse, Auth } from '../transportTypes';
4 | import { apiPost } from '../apiFetch';
5 |
6 | /** See https://zulip.com/api/test-notify */
7 | export default async (
8 | auth: Auth,
9 | params: {|
10 | token: string,
11 | |},
12 | ): Promise =>
13 | apiPost(auth, 'mobile_push/test_notification', {
14 | token: params.token,
15 | });
16 |
--------------------------------------------------------------------------------
/src/api/fetchApiKey.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from './transportTypes';
3 | import { apiPost } from './apiFetch';
4 |
5 | type ApiResponseFetchApiKey = {|
6 | ...$Exact,
7 | email: string,
8 | api_key: string,
9 | |};
10 |
11 | export default (auth: Auth, email: string, password: string): Promise =>
12 | apiPost(auth, 'fetch_api_key', { username: email, password });
13 |
--------------------------------------------------------------------------------
/src/styles/composeBoxStyles.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | export default {
4 | disabledComposeBox: {
5 | flexDirection: 'row',
6 | alignItems: 'center',
7 | justifyContent: 'space-around',
8 | backgroundColor: 'gray',
9 | paddingHorizontal: 16,
10 | paddingVertical: 8,
11 | },
12 | disabledComposeButton: {
13 | padding: 12,
14 | },
15 | disabledComposeText: {
16 | flex: 1,
17 | color: 'white',
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/getFocusedRouteNameFromRoute.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Partial } from 'tsflower/subst/lib';
5 | import { type Route, type PartialState, type NavigationState } from '@react-navigation/routers';
6 |
7 | declare export default function getFocusedRouteNameFromRoute(
8 | route: Partial> & { state?: PartialState>, ... },
9 | ): string | void;
10 |
--------------------------------------------------------------------------------
/ios/ZulipMobile/Assets.xcassets/Brand.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xFE",
9 | "green" : "0x92",
10 | "red" : "0x64"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/api/streams/getStreamId.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from '../transportTypes';
3 | import { apiGet } from '../apiFetch';
4 |
5 | type ApiResponseStreamId = {|
6 | ...$Exact,
7 | stream_id: number,
8 | |};
9 |
10 | /** See https://zulip.com/api/get-stream-id */
11 | export default async (auth: Auth, stream: string): Promise =>
12 | apiGet(auth, 'get_stream_id', { stream });
13 |
--------------------------------------------------------------------------------
/src/i18n/i18n.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { LocalizableText } from '../types';
3 |
4 | /**
5 | * A LocalizableText that always resolves to the given string.
6 | *
7 | * Useful for wrapping user data, like the name of a user or a stream,
8 | * in a context where we sometimes also show a string that needs translation.
9 | */
10 | export function noTranslation(value: string): LocalizableText {
11 | return { text: '{_}', values: { _: value } };
12 | }
13 |
--------------------------------------------------------------------------------
/types/react-native-safe-area-context/lib/typescript/src/InitialWindow.native.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { EdgeInsets as $tsflower_import_type$_$_2e__2f_SafeArea_2e_types$EdgeInsets } from './SafeArea.types';
5 | import { type Metrics } from './SafeArea.types';
6 | declare export var initialWindowMetrics: Metrics | null;
7 | declare export var initialWindowSafeAreaInsets: $tsflower_import_type$_$_2e__2f_SafeArea_2e_types$EdgeInsets | void;
8 |
--------------------------------------------------------------------------------
/types/react-native-safe-area-context/lib/typescript/src/CompatNativeSafeAreaProvider.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { JSX$Element as $tsflower_subst$React$JSX$Element } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | import { type NativeSafeAreaProviderProps } from './SafeArea.types';
7 | declare export function CompatNativeSafeAreaProvider(
8 | NativeSafeAreaProviderProps,
9 | ): $tsflower_subst$React$JSX$Element;
10 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # These are purely type definitions, no runtime code. Most of them
2 | # are third-party code, too, so naturally don't match our style.
3 | **/flow-typed/**
4 | types/react-intl.js.flow
5 | types/@react-native-community/netinfo/**
6 | types/@sentry/react-native.js.flow
7 | types/expo-web-browser/**
8 | types/react-native-webview.js.flow
9 |
10 | # Third-party code: react-native. We leave this code in the style we
11 | # received it in.
12 | src/third/react-native
13 |
--------------------------------------------------------------------------------
/src/api/streams/getStreams.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from '../transportTypes';
3 | import type { Stream } from '../apiTypes';
4 | import { apiGet } from '../apiFetch';
5 |
6 | type ApiResponseStreams = {|
7 | ...$Exact,
8 | streams: $ReadOnlyArray,
9 | |};
10 |
11 | /** See https://zulip.com/api/get-streams */
12 | export default async (auth: Auth): Promise => apiGet(auth, 'streams');
13 |
--------------------------------------------------------------------------------
/src/title/TitlePlain.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import React from 'react';
3 | import type { Node } from 'react';
4 | import { Text } from 'react-native';
5 |
6 | import styles from '../styles';
7 |
8 | type Props = $ReadOnly<{|
9 | text: string,
10 | color: string,
11 | |}>;
12 |
13 | export default function TitlePlain(props: Props): Node {
14 | const { text, color } = props;
15 | return {text};
16 | }
17 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useSyncState.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | declare export default function useSyncState(
5 | initialState?: () => T | T /* tsflower-warning: unimplemented: 'readonly' as type operator */,
6 | ): [
7 | T,
8 | () => T,
9 | (state: T) => void,
10 | (callback: () => void) => void,
11 | () => void,
12 | ] /* readonly [T, () => T, (state: T) => void, (callback: () => void) => void, () => void] */;
13 |
--------------------------------------------------------------------------------
/types/react-native-image-picker/lib/typescript/platforms/NativeImagePicker.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type TurboModule } from 'react-native/Libraries/TurboModule/RCTExport';
5 |
6 | export interface Spec extends TurboModule {
7 | launchCamera(options: Object, callback: () => void): void;
8 | launchImageLibrary(options: Object, callback: () => void): void;
9 | }
10 |
11 | declare var _default: Spec | null;
12 | export default _default;
13 |
--------------------------------------------------------------------------------
/src/api/devListUsers.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from './transportTypes';
3 | import type { DevUser } from './apiTypes';
4 | import { apiGet } from './apiFetch';
5 |
6 | type ApiResponseDevListUsers = {|
7 | ...$Exact,
8 | direct_admins: $ReadOnlyArray,
9 | direct_users: $ReadOnlyArray,
10 | |};
11 |
12 | export default (auth: Auth): Promise => apiGet(auth, 'dev_list_users');
13 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/UnhandledActionContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | import { type NavigationAction } from '@react-navigation/routers';
7 | declare var UnhandledActionContext: $tsflower_subst$React$Context<
8 | (action: NavigationAction) => void | void,
9 | >;
10 | export default UnhandledActionContext;
11 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useEventEmitter.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type EventEmitter, type EventConsumer } from './types';
5 | export type NavigationEventEmitter = EventEmitter & {
6 | create: (target: string) => EventConsumer,
7 | ...
8 | };
9 | declare export default function useEventEmitter(
10 | listen?: (e: any) => void,
11 | ): NavigationEventEmitter;
12 |
--------------------------------------------------------------------------------
/types/react-native-image-picker/lib/typescript/platforms/web.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type ImageLibraryOptions, type Callback, type ImagePickerResponse } from '../types';
5 | declare export function camera(
6 | options?: ImageLibraryOptions,
7 | callback?: Callback,
8 | ): Promise;
9 | declare export function imageLibrary(
10 | options?: ImageLibraryOptions,
11 | callback?: Callback,
12 | ): Promise;
13 |
--------------------------------------------------------------------------------
/src/webview/html/__tests__/render-test.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import messageTypingAsHtml from '../messageTypingAsHtml';
3 | import * as eg from '../../../__tests__/lib/exampleData';
4 |
5 | describe('typing', () => {
6 | it('escapes &< (e.g., in `avatar_url` and `email`', () => {
7 | const name = '&&2 < 0 )); then
21 | usage
22 | fi
23 |
24 | direct_dep_names
25 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useOnGetState.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type NavigationState } from '@react-navigation/routers';
5 | import { type GetStateListener } from './NavigationBuilderContext';
6 |
7 | type Options = {
8 | getState: () => NavigationState<>,
9 | getStateListeners: { [key: string]: GetStateListener | void },
10 | ...
11 | };
12 |
13 | declare export default function useOnGetState(Options): void;
14 | export {};
15 |
--------------------------------------------------------------------------------
/src/api/messages/toggleMessageStarred.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth } from '../transportTypes';
3 | import updateMessageFlags from './updateMessageFlags';
4 | import type { ApiResponseUpdateMessageFlags } from './updateMessageFlags';
5 |
6 | export default (
7 | auth: Auth,
8 | messageIds: $ReadOnlyArray,
9 | starMessage: boolean,
10 | ): Promise =>
11 | updateMessageFlags(auth, messageIds, starMessage ? 'add' : 'remove', 'starred');
12 |
--------------------------------------------------------------------------------
/types/react-native-tab-view/lib/typescript/src/SceneMap.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { JSX$Element as $tsflower_subst$React$JSX$Element } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | import { type SceneRendererProps } from './types';
7 | declare export default function SceneMap(scenes: {
8 | [key: string]: React.ComponentType,
9 | ...
10 | }): (SceneRendererProps & { route: any, ... }) => $tsflower_subst$React$JSX$Element;
11 |
--------------------------------------------------------------------------------
/src/api/transport.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth } from './transportTypes';
3 | import { base64Utf8Encode } from '../utils/encoding';
4 |
5 | export const getAuthHeaders = (auth: Auth): {| Authorization?: string |} =>
6 | // The `Object.freeze`` in the `:` case avoids a Flow issue:
7 | // https://github.com/facebook/flow/issues/2386#issuecomment-695064325
8 | auth.apiKey
9 | ? { Authorization: `Basic ${base64Utf8Encode(`${auth.email}:${auth.apiKey}`)}` }
10 | : Object.freeze({});
11 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/NavigationHelpersContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | import { type NavigationHelpers } from './types';
7 | declare var NavigationHelpersContext: $tsflower_subst$React$Context | void>;
11 | export default NavigationHelpersContext;
12 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/TransitionConfigs/TransitionSpecs.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type TransitionSpec } from '../types';
5 | declare export var TransitionIOSSpec: TransitionSpec;
6 | declare export var FadeInFromBottomAndroidSpec: TransitionSpec;
7 | declare export var FadeOutToBottomAndroidSpec: TransitionSpec;
8 | declare export var RevealFromBottomAndroidSpec: TransitionSpec;
9 | declare export var ScaleFromCenterAndroidSpec: TransitionSpec;
10 |
--------------------------------------------------------------------------------
/src/api/checkCompatibility.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import userAgent from '../utils/userAgent';
3 |
4 | // check with server if current mobile app is compatible with latest backend
5 | // compatibility fails only if server responds with 400 (but not with 200 or 404)
6 | export default (): Promise =>
7 | fetch('https://zulip.com/compatibility', {
8 | headers: {
9 | 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
10 | 'User-Agent': userAgent,
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/src/api/getTopics.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from './transportTypes';
3 | import type { Topic } from './apiTypes';
4 | import { apiGet } from './apiFetch';
5 |
6 | type ApiResponseTopics = {|
7 | ...$Exact,
8 | topics: $ReadOnlyArray,
9 | |};
10 |
11 | /** See https://zulip.com/api/get-stream-topics */
12 | export default async (auth: Auth, streamId: number): Promise =>
13 | apiGet(auth, `users/me/${streamId}/topics`);
14 |
--------------------------------------------------------------------------------
/src/common/SectionSeparator.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import React from 'react';
3 | import type { Node } from 'react';
4 | import { View } from 'react-native';
5 |
6 | import { createStyleSheet } from '../styles';
7 |
8 | const styles = createStyleSheet({
9 | separator: {
10 | height: 1,
11 | margin: 10,
12 | backgroundColor: 'hsla(0, 0%, 50%, 0.75)',
13 | },
14 | });
15 |
16 | export default function SectionSeparator(props: {||}): Node {
17 | return ;
18 | }
19 |
--------------------------------------------------------------------------------
/src/streams/getIsNotificationEnabled.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Subscription } from '../types';
3 |
4 | /**
5 | * Whether push notifications are enabled for this stream.
6 | *
7 | * The `userSettingsStreamNotification` parameter should correspond to
8 | * `state.settings.streamNotification`.
9 | */
10 | export default (
11 | subscription: Subscription | void,
12 | userSettingStreamNotification: boolean,
13 | ): boolean => subscription?.push_notifications ?? userSettingStreamNotification;
14 |
--------------------------------------------------------------------------------
/src/third/redux-persist/utils/isStatePlainEnough.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | export default function isStatePlainEnough(a: mixed): boolean {
4 | // isPlainObject + duck type not immutable
5 | if (a === null) {
6 | return false;
7 | }
8 | if (typeof a !== 'object') {
9 | return false;
10 | }
11 | if (typeof a.asMutable === 'function') {
12 | return false;
13 | }
14 | const proto = Object.getPrototypeOf(a);
15 | return proto === null || Object.getPrototypeOf(proto) === null;
16 | }
17 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/views/SafeAreaProviderCompat.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type {
5 | ReactNode as $tsflower_subst$React$ReactNode,
6 | JSX$Element as $tsflower_subst$React$JSX$Element,
7 | } from 'tsflower/subst/react';
8 |
9 | import * as React from 'react';
10 | type Props = { children: $tsflower_subst$React$ReactNode, ... };
11 | declare export default function SafeAreaProviderCompat(Props): $tsflower_subst$React$JSX$Element;
12 | export {};
13 |
--------------------------------------------------------------------------------
/types/expo-modules-core/build/environment/browser.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | declare export var isDOMAvailable: $FlowFixMe /* false */ /* tsflower-error: const type inferred from FalseKeyword */;
5 | declare export var canUseEventListeners: $FlowFixMe /* false */ /* tsflower-error: const type inferred from FalseKeyword */;
6 | declare export var canUseViewport: $FlowFixMe /* false */ /* tsflower-error: const type inferred from FalseKeyword */;
7 | declare export var isAsyncDebugging: boolean;
8 |
--------------------------------------------------------------------------------
/src/api/emoji_reactions/emojiReactionAdd.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponse, Auth } from '../transportTypes';
3 | import { apiPost } from '../apiFetch';
4 |
5 | export default (
6 | auth: Auth,
7 | messageId: number,
8 | reactionType: string,
9 | emojiCode: string,
10 | emojiName: string,
11 | ): Promise =>
12 | apiPost(auth, `messages/${messageId}/reactions`, {
13 | reaction_type: reactionType,
14 | emoji_code: emojiCode,
15 | emoji_name: emojiName,
16 | });
17 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/NavigationContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | import { type NavigationProp } from './types';
7 | declare var NavigationContext: $tsflower_subst$React$Context | void>;
14 | export default NavigationContext;
15 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useScheduleUpdate.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 |
7 | declare export var ScheduleUpdateContext: $tsflower_subst$React$Context<{
8 | scheduleUpdate: (callback: () => void) => void,
9 | flushUpdates: () => void,
10 | ...
11 | }>;
12 |
13 | declare export default function useScheduleUpdate(callback: () => void): void;
14 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/ServerContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 |
7 | export type ServerContextType = {
8 | location?: {
9 | pathname: string,
10 | search: string,
11 | ...
12 | },
13 | ...
14 | };
15 |
16 | declare var ServerContext: $tsflower_subst$React$Context;
17 | export default ServerContext;
18 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/utils/PreviousSceneContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { Context as $tsflower_subst$React$Context } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | import { type Route } from '@react-navigation/native';
7 | import { type Scene } from '../types';
8 | declare var PreviousSceneContext: $tsflower_subst$React$Context,
10 | > | void>;
11 | export default PreviousSceneContext;
12 |
--------------------------------------------------------------------------------
/jest/globalFetch.js:
--------------------------------------------------------------------------------
1 | global.fetch = jest.fn();
2 |
3 | fetch.mockResponseSuccess = body => {
4 | fetch.mockImplementation(() =>
5 | Promise.resolve({ json: () => Promise.resolve(JSON.parse(body)), status: 200, ok: true }),
6 | );
7 | };
8 |
9 | fetch.mockResponseFailure = error => {
10 | fetch.mockImplementation(() => Promise.reject(error));
11 | };
12 |
13 | fetch.mockErrorStatusCode = status => {
14 | fetch.mockImplementation({ status });
15 | };
16 |
17 | fetch.reset = () => {
18 | fetch.mockReset();
19 | };
20 |
--------------------------------------------------------------------------------
/src/api/emoji_reactions/emojiReactionRemove.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponse } from '../transportTypes';
3 | import { apiDelete } from '../apiFetch';
4 |
5 | export default (
6 | auth: Auth,
7 | messageId: number,
8 | reactionType: string,
9 | emojiCode: string,
10 | emojiName: string,
11 | ): Promise =>
12 | apiDelete(auth, `messages/${messageId}/reactions`, {
13 | reaction_type: reactionType,
14 | emoji_code: emojiCode,
15 | emoji_name: emojiName,
16 | });
17 |
--------------------------------------------------------------------------------
/types/expo-modules-core/build/PermissionsInterface.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | /* tsflower-unimplemented: EnumDeclaration */
5 | /* export declare enum PermissionStatus {
6 | GRANTED = "granted",
7 | UNDETERMINED = "undetermined",
8 | DENIED = "denied"
9 | } */
10 |
11 | export type PermissionExpiration = 'never' | number;
12 |
13 | export interface PermissionResponse {
14 | status: PermissionStatus;
15 | expires: PermissionExpiration;
16 | granted: boolean;
17 | canAskAgain: boolean;
18 | }
19 |
--------------------------------------------------------------------------------
/src/api/subscriptions/getSubscriptions.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from '../transportTypes';
3 | import type { Subscription } from '../apiTypes';
4 | import { apiGet } from '../apiFetch';
5 |
6 | type ApiResponseSubscriptions = {|
7 | ...$Exact,
8 | subscriptions: $ReadOnlyArray,
9 | |};
10 |
11 | /** See https://zulip.com/api/get-subscriptions */
12 | export default (auth: Auth): Promise =>
13 | apiGet(auth, 'users/me/subscriptions');
14 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/useDocumentTitle.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { RefObject as $tsflower_subst$React$RefObject } from 'tsflower/subst/react';
5 | import * as React from 'react';
6 | import { type NavigationContainerRef } from '@react-navigation/core';
7 | import { type DocumentTitleOptions } from './types';
8 |
9 | declare export default function useDocumentTitle(
10 | ref: $tsflower_subst$React$RefObject,
11 | ?DocumentTitleOptions,
12 | ): void;
13 |
--------------------------------------------------------------------------------
/types/react-native-image-picker/lib/typescript/platforms/native.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import {
5 | type CameraOptions,
6 | type ImageLibraryOptions,
7 | type Callback,
8 | type ImagePickerResponse,
9 | } from '../types';
10 |
11 | declare export function camera(
12 | options: CameraOptions,
13 | callback?: Callback,
14 | ): Promise;
15 | declare export function imageLibrary(
16 | options: ImageLibraryOptions,
17 | callback?: Callback,
18 | ): Promise;
19 |
--------------------------------------------------------------------------------
/src/chat/fetchingSelectors.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Fetching, PerAccountState, Narrow } from '../types';
3 | import { getFetching } from '../directSelectors';
4 | import { keyFromNarrow } from '../utils/narrow';
5 |
6 | /** The value implicitly represented by a missing entry in FetchingState. */
7 | export const DEFAULT_FETCHING = { older: false, newer: false };
8 |
9 | export const getFetchingForNarrow = (state: PerAccountState, narrow: Narrow): Fetching =>
10 | getFetching(state)[keyFromNarrow(narrow)] || DEFAULT_FETCHING;
11 |
--------------------------------------------------------------------------------
/src/webview/css/cssNight.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | export default `
4 | body {
5 | color: hsl(210, 11%, 85%);
6 | background: hsl(212, 28%, 18%);
7 | }
8 | .poll-vote {
9 | color: hsl(210, 11%, 85%);
10 | }
11 | .topic-header {
12 | background: hsl(212, 13%, 38%);
13 | }
14 | .msg-timestamp {
15 | background: hsl(212, 28%, 25%);
16 | }
17 | .highlight {
18 | background-color: hsla(51, 100%, 64%, 0.42);
19 | }
20 | .message_inline_image img.image-loading-placeholder {
21 | content: url("images/loader-white.svg");
22 | }
23 | `;
24 |
--------------------------------------------------------------------------------
/types/@react-native-camera-roll/camera-roll/lib/typescript/useCameraRoll.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import {
5 | type GetPhotosParams,
6 | type PhotoIdentifiersPage,
7 | type SaveToCameraRollOptions,
8 | } from './CameraRoll';
9 |
10 | type UseCameraRollResult = [
11 | PhotoIdentifiersPage,
12 | (config?: GetPhotosParams) => Promise,
13 | (tag: string, options?: SaveToCameraRollOptions) => Promise,
14 | ];
15 |
16 | declare export function useCameraRoll(): UseCameraRollResult;
17 | export {};
18 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/utils/useGestureHandlerRef.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { PanGestureHandler as $tsflower_import_type$_$react_2d_native_2d_gesture_2d_handler$PanGestureHandler } from 'react-native-gesture-handler';
5 | import type { Ref as $tsflower_subst$React$Ref } from 'tsflower/subst/react';
6 | import * as React from 'react';
7 | declare export default function useGestureHandlerRef(): $tsflower_subst$React$Ref<$tsflower_import_type$_$react_2d_native_2d_gesture_2d_handler$PanGestureHandler>;
8 |
--------------------------------------------------------------------------------
/src/api/submessages/sendSubmessage.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | import type { ApiResponse, Auth } from '../transportTypes';
4 | import { apiPost } from '../apiFetch';
5 |
6 | /** See https://zulip.readthedocs.io/en/latest/subsystems/widgets.html#poll-todo-lists-and-games */
7 | // `msg_type` only exists as widget at the moment, see #3205.
8 | export default async (auth: Auth, messageId: number, content: string): Promise =>
9 | apiPost(auth, 'submessage', {
10 | message_id: messageId,
11 | msg_type: 'widget',
12 | content,
13 | });
14 |
--------------------------------------------------------------------------------
/src/common/Logo.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import React from 'react';
3 | import type { Node } from 'react';
4 | import { Image } from 'react-native';
5 |
6 | import logoImg from '../../static/img/logo.png';
7 | import { createStyleSheet } from '../styles';
8 |
9 | const styles = createStyleSheet({
10 | logo: {
11 | width: 40,
12 | height: 40,
13 | margin: 20,
14 | alignSelf: 'center',
15 | },
16 | });
17 |
18 | export default function Logo(): Node {
19 | return ;
20 | }
21 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/Screen.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type ParamListBase, type NavigationState } from '@react-navigation/routers';
5 | import { type RouteConfig, type EventMapBase } from './types';
6 | declare export default function Screen<
7 | ParamList: ParamListBase,
8 | RouteName: $Keys,
9 | State: NavigationState<>,
10 | ScreenOptions: { ... },
11 | EventMap: EventMapBase,
12 | >(
13 | _: RouteConfig,
14 | ): null;
15 |
--------------------------------------------------------------------------------
/src/api/realm/createRealmFilter.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from '../transportTypes';
3 | import { apiPost } from '../apiFetch';
4 |
5 | type ApiResponseRealmCreateFilters = {|
6 | ...$Exact,
7 | id: number,
8 | |};
9 |
10 | /** https://zulip.com/api/add-linkifier */
11 | export default async (
12 | auth: Auth,
13 | pattern: string,
14 | urlFormatString: string,
15 | ): Promise =>
16 | apiPost(auth, 'realm/filters', { pattern, url_format_string: urlFormatString });
17 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/getPathFromState.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type NavigationState, type PartialState } from '@react-navigation/routers';
5 | import { type PathConfigMap } from './types';
6 |
7 | type Options = {
8 | initialRouteName?: string,
9 | screens: PathConfigMap,
10 | ...
11 | };
12 |
13 | type State = NavigationState<> | $Diff>, {| stale: mixed |}>;
14 | declare export default function getPathFromState(state: State, options?: Options): string;
15 | export {};
16 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useFocusEvents.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type NavigationState } from '@react-navigation/routers';
5 | import { type NavigationEventEmitter } from './useEventEmitter';
6 | import { type EventMapCore } from './types';
7 |
8 | type Options> = {
9 | state: State,
10 | emitter: NavigationEventEmitter>,
11 | ...
12 | };
13 |
14 | declare export default function useFocusEvents>(Options): void;
15 | export {};
16 |
--------------------------------------------------------------------------------
/ios/ZulipMobile/ZLPNotificationsBridge.m:
--------------------------------------------------------------------------------
1 | #import "React/RCTBridgeModule.h"
2 | #import "React/RCTEventEmitter.h"
3 |
4 | // Register our Swift modules with React Native:
5 | // https://reactnative.dev/docs/0.68/native-modules-ios#exporting-swift
6 |
7 | @interface RCT_EXTERN_MODULE(ZLPNotificationsEvents, RCTEventEmitter)
8 | @end
9 |
10 | @interface RCT_EXTERN_MODULE(ZLPNotificationsStatus, NSObject)
11 |
12 | RCT_EXTERN_METHOD(areNotificationsAuthorized:
13 | (RCTPromiseResolveBlock) resolve
14 | rejecter: (RCTPromiseRejectBlock) reject
15 | )
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/types/@react-navigation/material-top-tabs/lib/typescript/src/index.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | export { default as createMaterialTopTabNavigator } from './navigators/createMaterialTopTabNavigator';
5 | export { default as MaterialTopTabView } from './views/MaterialTopTabView';
6 | export { default as MaterialTopTabBar } from './views/MaterialTopTabBar';
7 | export {
8 | MaterialTopTabNavigationOptions,
9 | MaterialTopTabNavigationProp,
10 | MaterialTopTabScreenProps,
11 | MaterialTopTabBarProps,
12 | MaterialTopTabBarOptions,
13 | } from './types';
14 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useFocusedListenersChildrenAdapter.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type ParamListBase } from '@react-navigation/routers';
5 | import { type FocusedNavigationListener } from './NavigationBuilderContext';
6 | import { type NavigationHelpers } from './types';
7 |
8 | type Options = {
9 | navigation: NavigationHelpers,
10 | focusedListeners: FocusedNavigationListener[],
11 | ...
12 | };
13 |
14 | declare export default function useFocusedListenersChildrenAdapter(Options): void;
15 | export {};
16 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/theming/ThemeProvider.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type {
5 | ReactNode as $tsflower_subst$React$ReactNode,
6 | JSX$Element as $tsflower_subst$React$JSX$Element,
7 | } from 'tsflower/subst/react';
8 |
9 | import * as React from 'react';
10 | import { type Theme } from '../types';
11 |
12 | type Props = {
13 | value: Theme,
14 | children: $tsflower_subst$React$ReactNode,
15 | ...
16 | };
17 |
18 | declare export default function ThemeProvider(Props): $tsflower_subst$React$JSX$Element;
19 | export {};
20 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/views/GestureHandlerNative.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { JSX$Element as $tsflower_subst$React$JSX$Element } from 'tsflower/subst/react';
5 | import { type PanGestureHandlerProperties } from 'react-native-gesture-handler';
6 | declare export function PanGestureHandler(
7 | props: PanGestureHandlerProperties,
8 | ): $tsflower_subst$React$JSX$Element;
9 | export {
10 | GestureHandlerRootView,
11 | State as GestureState,
12 | PanGestureHandlerGestureEvent,
13 | } from 'react-native-gesture-handler';
14 |
--------------------------------------------------------------------------------
/android/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/docs/background/recommended-reading.md:
--------------------------------------------------------------------------------
1 | # Recommended reading
2 |
3 | ## React Native
4 |
5 | We recommend just official documentation of React Native - take a look at their
6 | [Getting started](https://reactnative.dev/docs/getting-started) section.
7 |
8 |
9 | ## Redux
10 |
11 | To know and understand what actually Redux is we encourage you to watch
12 | [this free video tutorial](https://egghead.io/lessons/javascript-redux-generating-containers-with-connect-from-react-redux-visibletodolist).
13 |
14 |
15 | ## Flexbox
16 | Play [the game](http://flexboxfroggy.com/) and learn how Flexbox works.
17 |
--------------------------------------------------------------------------------
/src/api/users/createUser.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponseSuccess, Auth } from '../transportTypes';
3 | import { apiPost } from '../apiFetch';
4 |
5 | /**
6 | * See https://zulip.com/api/create-user
7 | * Note: The requesting user must be an administrator.
8 | */
9 | export default (
10 | auth: Auth,
11 | email: string,
12 | password: string,
13 | fullName: string,
14 | shortName: string,
15 | ): Promise =>
16 | apiPost(auth, 'users', {
17 | email,
18 | password,
19 | full_name: fullName,
20 | short_name: shortName,
21 | });
22 |
--------------------------------------------------------------------------------
/src/message/AnnouncementOnly.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import React from 'react';
3 | import type { Node } from 'react';
4 | import { View } from 'react-native';
5 |
6 | import ZulipTextIntl from '../common/ZulipTextIntl';
7 | import styles from '../styles';
8 |
9 | export default function AnnouncementOnly(props: {||}): Node {
10 | return (
11 |
12 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/tools/flow-todo.eslintrc.yaml:
--------------------------------------------------------------------------------
1 | parser: hermes-eslint
2 | plugins:
3 | - ft-flow
4 | rules:
5 | ft-flow/no-weak-types: error
6 | ft-flow/require-parameter-type:
7 | - error
8 | - excludeArrowFunctions: true
9 | ft-flow/require-return-type:
10 | - error
11 | - always
12 | - excludeArrowFunctions: true
13 | excludeMatching:
14 | - "^render$"
15 |
16 | # Things that might also be good and we might do after the areas above
17 | # are clean or nearly clean:
18 | # - change `excludeArrowFunctions` to "expressionsOnly", for both
19 | # parameter and return types
20 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/getStateFromPath.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type NavigationState, type PartialState } from '@react-navigation/routers';
5 | import { type PathConfigMap } from './types';
6 |
7 | type Options = {
8 | initialRouteName?: string,
9 | screens: PathConfigMap,
10 | ...
11 | };
12 |
13 | type ResultState = PartialState> & { state?: ResultState, ... };
14 | declare export default function getStateFromPath(
15 | path: string,
16 | options?: Options,
17 | ): ResultState | void;
18 | export {};
19 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useComponent.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type {
5 | ComponentProps as $tsflower_subst$React$ComponentProps,
6 | JSX$Element as $tsflower_subst$React$JSX$Element,
7 | } from 'tsflower/subst/react';
8 |
9 | import * as React from 'react';
10 |
11 | declare export default function useComponent, P: { ... }>(
12 | Component: T,
13 | props: P,
14 | ): (
15 | rest: $Diff<$tsflower_subst$React$ComponentProps, {| [key: $Keys]: mixed |}>,
16 | ) => $tsflower_subst$React$JSX$Element;
17 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/full_description.txt:
--------------------------------------------------------------------------------
1 | The Zulip mobile app.
2 |
3 | Zulip is a team chat product for enterprises, open-source projects, working groups, and communities. Zulip’s unique threading model allows everyone to stay involved, even those that spend most of their day away from chat. https://zulipchat.com/why-zulip
4 |
5 | Like everything Zulip, this app is 100% free and open-source software: https://github.com/zulip/zulip-mobile . Thank you to the hundreds of contributors who have made Zulip what it is!
6 |
7 | Please send questions, comments, and bug reports to support@zulipchat.com, or tweet @zulip.
--------------------------------------------------------------------------------
/src/actions.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | export * from './account/accountActions';
3 | export * from './events/eventActions';
4 | export * from './nav/navActions';
5 | export * from './drafts/draftsActions';
6 | export * from './message/fetchActions';
7 | export * from './message/messagesActions';
8 | export * from './outbox/outboxActions';
9 | export * from './session/sessionActions';
10 | export * from './settings/settingsActions';
11 | export * from './streams/streamsActions';
12 | export * from './topics/topicActions';
13 | export * from './typing/typingActions';
14 | export * from './users/usersActions';
15 |
--------------------------------------------------------------------------------
/src/emoji/zulipExtraEmojiMap.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ImageEmoji } from '../types';
3 |
4 | /**
5 | * Make this to ressemble with realm emoji
6 | * so that both emoji type can be handled in similiar way
7 | * thus id has no meaning here
8 | */
9 | const zulipExtraEmojiMap: {| [id: string]: ImageEmoji |} = {
10 | zulip: {
11 | reaction_type: 'zulip_extra_emoji',
12 | deactivated: false,
13 | code: 'zulip',
14 | name: 'zulip',
15 | source_url: '/static/generated/emoji/images/emoji/unicode/zulip.png',
16 | },
17 | };
18 |
19 | export default zulipExtraEmojiMap;
20 |
--------------------------------------------------------------------------------
/tools/z-white.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/api/notifications/forgetPushToken.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth } from '../transportTypes';
3 | import { apiDelete } from '../apiFetch';
4 |
5 | /**
6 | * Tell the server to forget this device token for push notifications.
7 | *
8 | * @param mobileOS - Choose the server-side API intended for iOS or Android clients.
9 | */
10 | export default (auth: Auth, mobileOS: 'ios' | 'android', token: string): Promise => {
11 | const routeName = mobileOS === 'android' ? 'android_gcm_reg_id' : 'apns_device_token';
12 | return apiDelete(auth, `users/me/${routeName}`, { token });
13 | };
14 |
--------------------------------------------------------------------------------
/types/@react-navigation/bottom-tabs/lib/typescript/src/views/ResourceSavingScene.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type {
5 | ReactNode as $tsflower_subst$React$ReactNode,
6 | JSX$Element as $tsflower_subst$React$JSX$Element,
7 | } from 'tsflower/subst/react';
8 |
9 | import * as React from 'react';
10 |
11 | type Props = {
12 | isVisible: boolean,
13 | children: $tsflower_subst$React$ReactNode,
14 | enabled: boolean,
15 | style?: any,
16 | ...
17 | };
18 |
19 | declare export default function ResourceSavingScene(Props): $tsflower_subst$React$JSX$Element;
20 | export {};
21 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useCurrentRender.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type NavigationState, type ParamListBase } from '@react-navigation/routers';
5 | import { type Descriptor, type NavigationHelpers } from './types';
6 |
7 | type Options = {
8 | state: NavigationState<>,
9 | navigation: NavigationHelpers,
10 | descriptors: {
11 | [key: string]: Descriptor, { ... }>,
12 | ...
13 | },
14 | ...
15 | };
16 |
17 | declare export default function useCurrentRender(Options): void;
18 | export {};
19 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Workspace Settings for the zulip-mobile repo.
2 | {
3 | // Disable TS-centric validation. We'll rely on ESLint for general validation.
4 | // See https://github.com/Microsoft/vscode/issues/15171
5 | "javascript.validate.enable": false,
6 |
7 | // Makes IntelliSense work with Flow. See
8 | // https://github.com/Microsoft/vscode-react-native/blob/master/doc/intellisense.md .
9 | "flow.useNPMPackagedFlow": true,
10 |
11 | "[javascript]": {
12 | // Format JS code with prettier-eslint.
13 | "editor.defaultFormatter": "esbenp.prettier-vscode",
14 | },
15 | }
16 |
--------------------------------------------------------------------------------
/src/api/messages/getMessageHistory.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from '../transportTypes';
3 | import type { MessageSnapshot } from '../apiTypes';
4 | import { apiGet } from '../apiFetch';
5 |
6 | type ApiResponseMessageHistory = {|
7 | ...$Exact,
8 | message_history: $ReadOnlyArray,
9 | |};
10 |
11 | /** See https://zulip.com/api/get-message-history */
12 | export default async (auth: Auth, messageId: number): Promise =>
13 | apiGet(auth, `messages/${messageId}/history`, {
14 | message_id: messageId,
15 | });
16 |
--------------------------------------------------------------------------------
/src/common/LineSeparator.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import React, { useContext } from 'react';
3 | import type { Node } from 'react';
4 | import { View } from 'react-native';
5 |
6 | import { ThemeContext, createStyleSheet } from '../styles';
7 |
8 | const componentStyles = createStyleSheet({
9 | lineSeparator: {
10 | height: 1,
11 | margin: 4,
12 | },
13 | });
14 |
15 | export default function LineSeparator(props: {||}): Node {
16 | const themeContext = useContext(ThemeContext);
17 | return (
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/src/utils/color.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | // $FlowFixMe[untyped-import]
3 | import Color from 'color';
4 | import type { ColorValue } from 'react-native/Libraries/StyleSheet/StyleSheet';
5 |
6 | export const foregroundColorFromBackground = (color: ColorValue): 'black' | 'white' =>
7 | Color(color).luminosity() > 0.4 ? 'black' : 'white';
8 |
9 | export const colorHashFromString = (name: string): string => {
10 | let hash = 0;
11 | for (let i = 0; i < name.length; i++) {
12 | hash = hash * 31 + name.charCodeAt(i);
13 | hash %= 0x1000000;
14 | }
15 |
16 | return `#${hash.toString(16).padStart(6, '0')}`;
17 | };
18 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useOnRouteFocus.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import {
5 | type NavigationAction,
6 | type NavigationState,
7 | type Router,
8 | } from '@react-navigation/routers';
9 |
10 | type Options = {
11 | router: Router, Action>,
12 | getState: () => NavigationState<>,
13 | setState: (state: NavigationState<>) => void,
14 | key?: string,
15 | ...
16 | };
17 |
18 | declare export default function useOnRouteFocus(
19 | Options,
20 | ): (key: string) => void;
21 | export {};
22 |
--------------------------------------------------------------------------------
/src/__tests__/lib/intl.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | import type { GetText } from '../../types';
4 |
5 | // Our translation function, usually given the name _.
6 | // eslint-disable-next-line no-underscore-dangle
7 | export const mock_: GetText = m => {
8 | if (typeof m === 'object') {
9 | if (m.text === '{_}') {
10 | // $FlowIgnore[incompatible-indexer]
11 | /* $FlowIgnore[incompatible-cast]
12 | We expect an `m.values` that corresponds to `m.text`. */
13 | const values = (m.values: {| +_: string |});
14 | return values._;
15 | }
16 | return m.text;
17 | }
18 | return m;
19 | };
20 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/zulipmobile/sharing/SharingModule.kt:
--------------------------------------------------------------------------------
1 | package com.zulipmobile.sharing
2 |
3 | import com.facebook.react.bridge.*
4 |
5 | internal class SharingModule(reactContext: ReactApplicationContext) :
6 | ReactContextBaseJavaModule(reactContext) {
7 |
8 | override fun getName(): String {
9 | return "Sharing"
10 | }
11 |
12 | @ReactMethod
13 | fun readInitialSharedContent(promise: Promise) {
14 | promise.resolve(initialSharedData)
15 | initialSharedData = null
16 | }
17 |
18 | companion object {
19 | var initialSharedData: WritableMap? = null
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/wallaby.js:
--------------------------------------------------------------------------------
1 | module.exports = (wallaby) => ({
2 | files: ['src/**/*.js', '!src/**/__tests__/*.js'],
3 | tests: ['src/**/__tests__/*.js'],
4 | env: {
5 | type: 'node',
6 | runner: 'node',
7 | },
8 | testFramework: 'jest',
9 | compilers: {
10 | 'src/**/*.js': wallaby.compilers.babel(),
11 | },
12 | setup: () =>
13 | wallaby.testFramework.configure({
14 | // https://facebook.github.io/jest/docs/api.html#config-options
15 | // you may just pass `require('./package.json').jest`, if you use it for your Jest config
16 | // don't forget to include package.json in the files list in this case
17 | }),
18 | });
19 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/api/subscriptions/updateUserTopic.js:
--------------------------------------------------------------------------------
1 | // @flow strict-local
2 | import type { UserTopicVisibilityPolicy } from '../modelTypes';
3 | import type { ApiResponseSuccess, Auth } from '../transportTypes';
4 | import { apiPost } from '../apiFetch';
5 |
6 | /** https://chat.zulip.org/api/update-user-topic */
7 | export default function updateUserTopic(
8 | auth: Auth,
9 | stream_id: number,
10 | topic: string,
11 | visibility_policy: UserTopicVisibilityPolicy,
12 | ): Promise {
13 | return apiPost(auth, '/user_topics', {
14 | stream_id,
15 | topic,
16 | visibility_policy: (visibility_policy: number),
17 | });
18 | }
19 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Debug Android",
9 | "program": "${workspaceRoot}/.vscode/launchReactNative.js",
10 | "type": "reactnative",
11 | "request": "launch",
12 | "platform": "android",
13 | "sourceMaps": true,
14 | "outDir": "${workspaceRoot}/.vscode/.react"
15 | },
16 | ]
17 | }
--------------------------------------------------------------------------------
/ios/upload.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | method
6 | app-store
7 | destination
8 | upload
9 | signingStyle
10 | manual
11 | provisioningProfiles
12 |
13 | org.zulip.Zulip
14 |
15 | iOS app distribution 2022
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/selectors.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | export * from './account/accountsSelectors';
3 | export * from './pm-conversations/pmConversationsSelectors';
4 | export * from './caughtup/caughtUpSelectors';
5 | export * from './chat/narrowsSelectors';
6 | export * from './chat/fetchingSelectors';
7 | export * from './directSelectors';
8 | export * from './emoji/emojiSelectors';
9 | export * from './message/messageSelectors';
10 | export * from './subscriptions/subscriptionSelectors';
11 | export * from './topics/topicSelectors';
12 | export * from './typing/typingSelectors';
13 | export * from './unread/unreadSelectors';
14 | export * from './users/userSelectors';
15 |
--------------------------------------------------------------------------------
/src/session/sessionActions.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { PerAccountAction, AccountIndependentAction, Orientation } from '../types';
3 | import { APP_ONLINE, APP_ORIENTATION, DISMISS_SERVER_COMPAT_NOTICE } from '../actionConstants';
4 |
5 | export const appOnline = (isOnline: boolean | null): AccountIndependentAction => ({
6 | type: APP_ONLINE,
7 | isOnline,
8 | });
9 |
10 | export const appOrientation = (orientation: Orientation): AccountIndependentAction => ({
11 | type: APP_ORIENTATION,
12 | orientation,
13 | });
14 |
15 | export const dismissCompatNotice = (): PerAccountAction => ({
16 | type: DISMISS_SERVER_COMPAT_NOTICE,
17 | });
18 |
--------------------------------------------------------------------------------
/src/webview/html/time.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import template from './template';
3 | import type { TimeMessageListElement } from '../../types';
4 | import { humanDate } from '../../utils/date';
5 |
6 | /**
7 | * The HTML string for a message-list element of the "time" type.
8 | *
9 | * This is a private helper of messageListElementHtml.
10 | */
11 | export default (element: TimeMessageListElement): string => template`\
12 |
13 |
14 | ${humanDate(new Date(element.timestamp * 1000))}
15 |
16 |
`;
17 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | # How to upgrade Gradle:
2 | # $ tools/gradle wrapper --distribution-type=all --gradle-version=NEW_VERSION
3 | # $ tools/gradle wrapper --distribution-type=all --gradle-version=NEW_VERSION
4 | # (Yep, run the same command twice. The first updates this file so we use
5 | # the new Gradle; the second updates the wrapper's jar and scripts, so that
6 | # the wrapper is the one from the new Gradle too.)
7 | distributionBase=GRADLE_USER_HOME
8 | distributionPath=wrapper/dists
9 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
10 | zipStoreBase=GRADLE_USER_HOME
11 | zipStorePath=wrapper/dists
12 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/utils/GestureHandlerRefContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { PanGestureHandler as $tsflower_import_type$_$react_2d_native_2d_gesture_2d_handler$PanGestureHandler } from 'react-native-gesture-handler';
5 | import type {
6 | Context as $tsflower_subst$React$Context,
7 | Ref as $tsflower_subst$React$Ref,
8 | } from 'tsflower/subst/react';
9 | import * as React from 'react';
10 | declare var _default: $tsflower_subst$React$Context<
11 | $tsflower_subst$React$Ref<$tsflower_import_type$_$react_2d_native_2d_gesture_2d_handler$PanGestureHandler>,
12 | >;
13 | export default _default;
14 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/__mocks__/window.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | declare var _default: {
5 | document: { title: string, ... },
6 | location: URL,
7 | history: {
8 | +state: any,
9 | pushState(state: any, _: string, path: string): void,
10 | replaceState(state: any, _: string, path: string): void,
11 | go(n: number): void,
12 | back(): void,
13 | forward(): void,
14 | ...
15 | },
16 | addEventListener: (type: 'popstate', listener: () => void) => void,
17 | removeEventListener: (type: 'popstate', listener: () => void) => void,
18 | ...
19 | };
20 |
21 | export default _default;
22 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/zulipmobile/sharing/SharingPackage.kt:
--------------------------------------------------------------------------------
1 | package com.zulipmobile.sharing
2 |
3 | import com.facebook.react.ReactPackage
4 | import com.facebook.react.bridge.NativeModule
5 | import com.facebook.react.bridge.ReactApplicationContext
6 | import com.facebook.react.uimanager.ViewManager
7 |
8 | class SharingPackage : ReactPackage {
9 | override fun createViewManagers(reactContext: ReactApplicationContext): List> {
10 | return emptyList()
11 | }
12 |
13 | override fun createNativeModules(reactContext: ReactApplicationContext): List {
14 | return arrayListOf(SharingModule(reactContext))
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/BaseNavigationContainer.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type {
5 | ForwardRefExoticComponent as $tsflower_subst$React$ForwardRefExoticComponent,
6 | RefAttributes as $tsflower_subst$React$RefAttributes,
7 | } from 'tsflower/subst/react';
8 |
9 | import * as React from 'react';
10 | import { type NavigationContainerRef, type NavigationContainerProps } from './types';
11 | declare var BaseNavigationContainer: $tsflower_subst$React$ForwardRefExoticComponent<
12 | NavigationContainerProps & $tsflower_subst$React$RefAttributes,
13 | >;
14 | export default BaseNavigationContainer;
15 |
--------------------------------------------------------------------------------
/types/expo-mail-composer/build/MailComposer.types.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | export type MailComposerOptions = {
5 | recipients?: string[],
6 | ccRecipients?: string[],
7 | bccRecipients?: string[],
8 | subject?: string,
9 | body?: string,
10 | isHtml?: boolean,
11 | attachments?: string[],
12 | ...
13 | };
14 |
15 | export type MailComposerResult = { status: MailComposerStatusT, ... };
16 |
17 | declare export var MailComposerStatus: {|
18 | +UNDETERMINED: 'undetermined',
19 | +SENT: 'sent',
20 | +SAVED: 'saved',
21 | +CANCELLED: 'cancelled',
22 | |};
23 | export type MailComposerStatusT = $Values;
24 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/TransitionConfigs/TransitionPresets.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type TransitionPreset } from '../types';
5 | declare export var SlideFromRightIOS: TransitionPreset;
6 | declare export var ModalSlideFromBottomIOS: TransitionPreset;
7 | declare export var ModalPresentationIOS: TransitionPreset;
8 | declare export var FadeFromBottomAndroid: TransitionPreset;
9 | declare export var RevealFromBottomAndroid: TransitionPreset;
10 | declare export var ScaleFromCenterAndroid: TransitionPreset;
11 | declare export var DefaultTransition: TransitionPreset;
12 | declare export var ModalTransition: TransitionPreset;
13 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/utils/conditional.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type {
5 | AnimatedInterpolation as $tsflower_subst$RN$Animated$AnimatedInterpolation,
6 | AnimatedAddition as $tsflower_subst$RN$Animated$AnimatedAddition,
7 | } from 'tsflower/subst/react-native';
8 |
9 | import { Animated } from 'react-native';
10 |
11 | declare export default function conditional(
12 | condition: $tsflower_subst$RN$Animated$AnimatedInterpolation,
13 | main: $tsflower_subst$RN$Animated$AnimatedInterpolation,
14 | fallback: $tsflower_subst$RN$Animated$AnimatedInterpolation,
15 | ): $tsflower_subst$RN$Animated$AnimatedAddition;
16 |
--------------------------------------------------------------------------------
/src/common/OwnAvatar.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import React from 'react';
3 | import type { Node } from 'react';
4 |
5 | import { useSelector } from '../react-redux';
6 | import UserAvatar from './UserAvatar';
7 | import { getOwnUser } from '../users/userSelectors';
8 |
9 | type Props = $ReadOnly<{|
10 | size: number,
11 | |}>;
12 |
13 | /**
14 | * Renders an image of the current user's avatar
15 | *
16 | * @prop size - Sets width and height in logical pixels.
17 | */
18 | export default function OwnAvatar(props: Props): Node {
19 | const { size } = props;
20 | const user = useSelector(getOwnUser);
21 | return ;
22 | }
23 |
--------------------------------------------------------------------------------
/types/expo-modules-core/src/ts-declarations/NativeEventEmitter.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | /* tsflower-unimplemented: ModuleDeclaration */
5 | /* declare module 'react-native/Libraries/EventEmitter/NativeEventEmitter' {
6 | import { EventEmitter } from 'react-native';
7 |
8 | interface NativeEventEmitter extends EventEmitter {
9 | new (nativeModule: NativeModule): NativeEventEmitters;
10 | }
11 |
12 | const NativeEventEmitter: NativeEventEmitter;
13 |
14 | export default NativeEventEmitter;
15 |
16 | type NativeModule = {
17 | addListener: (eventType: string) => void;
18 | removeListeners: (count: number) => void;
19 | };
20 | } */
21 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/useOptionsGetters.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type ParamListBase, type NavigationState } from '@react-navigation/routers';
5 | import { type NavigationProp } from './types';
6 |
7 | type Options = {
8 | key?: string,
9 | navigation?: NavigationProp, { ... }>,
10 | options?: { ... } | void,
11 | ...
12 | };
13 |
14 | declare export default function useOptionsGetters(Options): {
15 | addOptionsGetter: (key: string, getter: () => { ... } | void | null) => () => void,
16 | getCurrentOptions: () => { ... } | null | void,
17 | ...
18 | };
19 |
20 | export {};
21 |
--------------------------------------------------------------------------------
/types/react-native-image-picker/lib/typescript/index.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { ImagePickerResponse as $tsflower_import_type$_$_2e__2f_types$ImagePickerResponse } from './types';
5 | import { type CameraOptions, type ImageLibraryOptions, type Callback } from './types';
6 | export * from './types';
7 | declare export function launchCamera(
8 | options: CameraOptions,
9 | callback?: Callback,
10 | ): Promise<$tsflower_import_type$_$_2e__2f_types$ImagePickerResponse>;
11 | declare export function launchImageLibrary(
12 | options: ImageLibraryOptions,
13 | callback?: Callback,
14 | ): Promise<$tsflower_import_type$_$_2e__2f_types$ImagePickerResponse>;
15 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
4 |
5 | // List of extensions which should be recommended for users of this workspace.
6 | "recommendations": [
7 | "msjsdiag.vscode-react-native",
8 | "flowtype.flow-for-vscode",
9 | "dbaeumer.vscode-eslint",
10 | "esbenp.prettier-vscode"
11 | ],
12 |
13 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
14 | // Nothing in here but adding this in order to add extentions later as required.
15 | "unwantedRecommendations": []
16 | }
17 |
--------------------------------------------------------------------------------
/static/icons/follow.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Zulip
3 | Messages
4 | No Browser Found
5 |
6 | - %d conversation
7 | - %d conversations
8 |
9 | Share To
10 |
11 | - %1$s to you and %2$s other
12 | - %1$s to you and %2$s others
13 |
14 | You
15 |
16 |
--------------------------------------------------------------------------------
/src/boot/StoreHydratedGate.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import React from 'react';
3 | import type { Node } from 'react';
4 |
5 | import { useGlobalSelector } from '../react-redux';
6 | import { getIsHydrated } from '../selectors';
7 | import FullScreenLoading from '../common/FullScreenLoading';
8 |
9 | type Props = $ReadOnly<{|
10 | children: Node,
11 | |}>;
12 |
13 | /**
14 | * Where we prevent everything from rendering while waiting for rehydration.
15 | */
16 | export default function StoreHydratedGate(props: Props): Node {
17 | const { children } = props;
18 |
19 | const isHydrated = useGlobalSelector(getIsHydrated);
20 |
21 | return isHydrated ? children : ;
22 | }
23 |
--------------------------------------------------------------------------------
/types/react-native-gesture-handler.js.flow:
--------------------------------------------------------------------------------
1 | // These are hand-written stubs, while we focus on other libraries
2 | // that consume this one.
3 | // @flow
4 |
5 | import * as React from 'react';
6 |
7 | export type PanGestureHandlerProps = $FlowFixMe;
8 | export type PanGestureHandlerProperties = $FlowFixMe;
9 | export type PanGestureHandlerGestureEvent = $FlowFixMe;
10 | export class PanGestureHandler extends React.Component {}
11 |
12 | export type TapGestureHandler = $FlowFixMe;
13 | export type TapGestureHandlerProperties = $FlowFixMe;
14 | export type State = $FlowFixMe;
15 |
16 | declare export var GestureHandlerRootView: $FlowFixMe;
17 | declare export var BaseButton: $FlowFixMe;
18 |
--------------------------------------------------------------------------------
/src/webview/static/images/follow.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/views/GestureHandler.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import * as React from 'react';
5 | import { View } from 'react-native';
6 | import { type PanGestureHandlerProperties } from 'react-native-gesture-handler';
7 | declare export var PanGestureHandler: React.ComponentType;
8 | declare export var GestureHandlerRootView: typeof View;
9 |
10 | declare export var GestureState: {
11 | UNDETERMINED: number,
12 | FAILED: number,
13 | BEGAN: number,
14 | CANCELLED: number,
15 | ACTIVE: number,
16 | END: number,
17 | ...
18 | };
19 |
20 | export { PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';
21 |
--------------------------------------------------------------------------------
/src/account/accountMisc.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Account, Auth, Identity } from '../types';
3 |
4 | const identitySlice = ({ realm, email }): Identity => ({ realm, email });
5 |
6 | export const identityOfAuth: Auth => Identity = identitySlice;
7 |
8 | export const identityOfAccount: ($ReadOnly<{ ...Identity, ... }>) => Identity = identitySlice;
9 |
10 | /** A string corresponding uniquely to an identity, for use in `Map`s. */
11 | export const keyOfIdentity = ({ realm, email }: Identity): string =>
12 | `${realm.toString()}\0${email}`;
13 |
14 | export const authOfAccount = (account: Account): Auth => {
15 | const { realm, email, apiKey } = account;
16 | return { realm, email, apiKey };
17 | };
18 |
--------------------------------------------------------------------------------
/src/api/messages/deleteMessage.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponse, Auth } from '../transportTypes';
3 | import { apiPatch } from '../apiFetch';
4 |
5 | // TODO(#4701): Make an API method for DELETE /messages/{id}. Use it to
6 | // implement the permanent message deletion capability:
7 | // https://zulip.com/help/configure-message-editing-and-deletion
8 | // To do so, we'll need input from realm_delete_own_message_policy,
9 | // realm_message_content_delete_limit_seconds, the user's role, the current
10 | // time, and maybe more; we'll want #3898 if we can.
11 | export default async (auth: Auth, id: number): Promise =>
12 | apiPatch(auth, `messages/${id}`, {
13 | content: '',
14 | });
15 |
--------------------------------------------------------------------------------
/src/api/users/getUsers.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from '../transportTypes';
3 | import type { User } from '../apiTypes';
4 | import { apiGet } from '../apiFetch';
5 |
6 | type ApiResponseUsers = {|
7 | ...$Exact,
8 | members: $ReadOnlyArray<{| ...User, avatar_url: string | null |}>,
9 | |};
10 |
11 | // TODO: If we start to use this, we need to convert `.avatar_url` to
12 | // an AvatarURL instance, like we do in `registerForEvents` and
13 | // `EVENT_USER_ADD` and RealmUserUpdateEvent.
14 |
15 | /** See https://zulip.com/api/get-users */
16 | export default (auth: Auth): Promise =>
17 | apiGet(auth, 'users', { client_gravatar: true });
18 |
--------------------------------------------------------------------------------
/src/common/__tests__/getStatusBarColor-test.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import { getStatusBarColor } from '../ZulipStatusBar';
3 |
4 | const themeDark = 'dark';
5 | const themeLight = 'light';
6 |
7 | describe('getStatusBarColor', () => {
8 | test('returns specific color when given, regardless of theme', () => {
9 | expect(getStatusBarColor('#fff', themeLight)).toEqual('#fff');
10 | expect(getStatusBarColor('#fff', themeDark)).toEqual('#fff');
11 | });
12 |
13 | test('returns color according to theme for default case', () => {
14 | expect(getStatusBarColor(undefined, themeLight)).toEqual('white');
15 | expect(getStatusBarColor(undefined, themeDark)).toEqual('hsl(212, 28%, 18%)');
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/createNavigatorFactory.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import * as React from 'react';
5 | import { type NavigationState } from '@react-navigation/routers';
6 | import { type TypedNavigator, type EventMapBase } from './types';
7 | declare export default function createNavigatorFactory<
8 | State: NavigationState<>,
9 | ScreenOptions: { ... },
10 | EventMap: EventMapBase,
11 | NavigatorComponent: React.ComponentType,
12 | >(
13 | Navigator: NavigatorComponent,
14 | ): () => TypedNavigator<
15 | ParamList,
16 | State,
17 | ScreenOptions,
18 | EventMap,
19 | NavigatorComponent,
20 | >;
21 |
--------------------------------------------------------------------------------
/types/@react-navigation/bottom-tabs/lib/typescript/src/index.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | export { default as createBottomTabNavigator } from './navigators/createBottomTabNavigator';
5 | export { default as BottomTabView } from './views/BottomTabView';
6 | export { default as BottomTabBar } from './views/BottomTabBar';
7 | export { default as BottomTabBarHeightContext } from './utils/BottomTabBarHeightContext';
8 | export { default as useBottomTabBarHeight } from './utils/useBottomTabBarHeight';
9 | export {
10 | BottomTabNavigationOptions,
11 | BottomTabNavigationProp,
12 | BottomTabScreenProps,
13 | BottomTabBarProps,
14 | BottomTabBarOptions,
15 | BottomTabBarButtonProps,
16 | } from './types';
17 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/zulipmobile/notifications/NotificationsPackage.kt:
--------------------------------------------------------------------------------
1 | package com.zulipmobile.notifications
2 |
3 | import com.facebook.react.ReactPackage
4 | import com.facebook.react.bridge.ReactApplicationContext
5 | import com.facebook.react.bridge.NativeModule
6 | import com.facebook.react.uimanager.ViewManager
7 | import java.util.ArrayList
8 |
9 | class NotificationsPackage : ReactPackage {
10 | override fun createViewManagers(reactContext: ReactApplicationContext): List> {
11 | return emptyList()
12 | }
13 |
14 | override fun createNativeModules(reactContext: ReactApplicationContext): List {
15 | return listOf(NotificationsModule(reactContext))
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/api/reportPresence.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponseSuccess, Auth } from './transportTypes';
3 | import type { PresenceSnapshot } from './apiTypes';
4 | import { apiPost } from './apiFetch';
5 |
6 | type ApiResponseWithPresence = {|
7 | ...$Exact,
8 | server_timestamp: number,
9 | presences: PresenceSnapshot,
10 | |};
11 |
12 | /** See https://zulip.readthedocs.io/en/latest/subsystems/presence.html . */
13 | export default (
14 | auth: Auth,
15 | isActive: boolean = true,
16 | newUserInput: boolean = false,
17 | ): Promise =>
18 | apiPost(auth, 'users/me/presence', {
19 | status: isActive ? 'active' : 'idle',
20 | new_user_input: newUserInput,
21 | });
22 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/EnsureSingleNavigator.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type {
5 | ReactNode as $tsflower_subst$React$ReactNode,
6 | Context as $tsflower_subst$React$Context,
7 | JSX$Element as $tsflower_subst$React$JSX$Element,
8 | } from 'tsflower/subst/react';
9 |
10 | import * as React from 'react';
11 | type Props = { children: $tsflower_subst$React$ReactNode, ... };
12 |
13 | declare export var SingleNavigatorContext: $tsflower_subst$React$Context<{
14 | register(key: string): void,
15 | unregister(key: string): void,
16 | ...
17 | } | void>;
18 |
19 | declare export default function EnsureSingleNavigator(Props): $tsflower_subst$React$JSX$Element;
20 | export {};
21 |
--------------------------------------------------------------------------------
/src/api/subscriptions/getSubscriptionToStream.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from '../transportTypes';
3 | import { type UserId } from '../idTypes';
4 | import { apiGet } from '../apiFetch';
5 |
6 | type ApiResponseSubscriptionStatus = {|
7 | ...$Exact,
8 | is_subscribed: boolean,
9 | |};
10 |
11 | /**
12 | * Get whether a user is subscribed to a particular stream.
13 | *
14 | * See https://zulip.com/api/get-subscription-status for
15 | * documentation of this endpoint.
16 | */
17 | export default (
18 | auth: Auth,
19 | userId: UserId,
20 | streamId: number,
21 | ): Promise =>
22 | apiGet(auth, `users/${userId}/subscriptions/${streamId}`);
23 |
--------------------------------------------------------------------------------
/src/common/ViewPlaceholder.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import React from 'react';
3 | import type { Node } from 'react';
4 | import { View } from 'react-native';
5 |
6 | type Props = $ReadOnly<{|
7 | width?: number,
8 | height?: number,
9 | |}>;
10 |
11 | /**
12 | * An empty layout component used to simplify UI alignment.
13 | * Use when it is easier to use this instead of setting component
14 | * padding and margin.
15 | *
16 | * @prop [width] - Width of the component in pixels.
17 | * @prop [height] - Height of the component in pixels.
18 | */
19 | export default function ViewPlaceholder(props: Props): Node {
20 | const { width, height } = props;
21 | const style = { width, height };
22 | return ;
23 | }
24 |
--------------------------------------------------------------------------------
/src/api/uploadFile.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from './transportTypes';
3 | import { apiFile } from './apiFetch';
4 | import { getFileExtension, getMimeTypeFromFileExtension } from '../utils/url';
5 |
6 | type ApiResponseUploadFile = {|
7 | ...$Exact,
8 | uri: string,
9 | |};
10 |
11 | export default (auth: Auth, uri: string, name: string): Promise => {
12 | const formData = new FormData();
13 | const extension = getFileExtension(name);
14 | const type = getMimeTypeFromFileExtension(extension);
15 | // $FlowFixMe[incompatible-call]
16 | formData.append('file', { uri, name, type, extension });
17 | return apiFile(auth, 'user_uploads', formData);
18 | };
19 |
--------------------------------------------------------------------------------
/src/api/users/getUserProfile.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth, ApiResponseSuccess } from '../transportTypes';
3 | import { type UserId } from '../idTypes';
4 | import { apiGet } from '../apiFetch';
5 |
6 | type ApiResponseUserProfile = {|
7 | ...$Exact,
8 | client_id: string,
9 | email: string,
10 | full_name: string,
11 | is_admin: boolean,
12 | is_bot: boolean,
13 | max_message_id: number,
14 | short_name: string,
15 | user_id: UserId,
16 | // pointer: number, /* deprecated 2020-02; see zulip/zulip#8994 */
17 | |};
18 |
19 | /** See https://zulip.com/api/get-own-user */
20 | export default (auth: Auth, clientGravatar: boolean = true): Promise =>
21 | apiGet(auth, 'users/me');
22 |
--------------------------------------------------------------------------------
/src/webview/js/matchesPolyfill.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | // Official MDN polyfill for Element.matches
3 | // Source: https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
4 | export default `
5 | if (!Element.prototype.matches) {
6 | Element.prototype.matches =
7 | Element.prototype.matchesSelector ||
8 | Element.prototype.mozMatchesSelector ||
9 | Element.prototype.msMatchesSelector ||
10 | Element.prototype.oMatchesSelector ||
11 | Element.prototype.webkitMatchesSelector ||
12 | function(s) {
13 | var matches = (this.document || this.ownerDocument).querySelectorAll(s),
14 | i = matches.length;
15 | while (--i >= 0 && matches.item(i) !== this) {}
16 | return i > -1;
17 | };
18 | }
19 | `;
20 |
--------------------------------------------------------------------------------
/src/utils/unread.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { FlagsState, Message, Outbox } from '../types';
3 |
4 | export const filterUnreadMessageIds = (
5 | messageIds: $ReadOnlyArray,
6 | flags: FlagsState,
7 | ): number[] => messageIds.filter((msgId: number) => !flags || !flags.read || !flags.read[msgId]);
8 |
9 | export const filterUnreadMessagesInRange = (
10 | messages: $ReadOnlyArray,
11 | flags: FlagsState,
12 | fromId: number,
13 | toId: number,
14 | ): number[] => {
15 | const messagesInRange = messages
16 | .filter(msg => !msg.isOutbox)
17 | .filter(msg => msg.id >= fromId && msg.id <= toId);
18 | return filterUnreadMessageIds(
19 | messagesInRange.map(x => x.id),
20 | flags,
21 | );
22 | };
23 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # This should be the only .eslintignore file in our tree.
2 | # See apply_eslintignore in tools/test.
3 |
4 | # These are purely type definitions, no runtime code. Most of them
5 | # are third-party code, too, so naturally don't match our style.
6 | **/flow-typed/**
7 | types/react-intl.js.flow
8 | types/@react-native-community/netinfo/**
9 | types/@sentry/react-native.js.flow
10 | types/expo-web-browser/**
11 | types/react-native-webview.js.flow
12 |
13 | # These are type-tests, made up of code that gets type-checked but
14 | # never actually run. They're naturally full of dead code which
15 | # ESLint would complain about; and because the code never runs, other
16 | # things it might complain about don't really matter anyway.
17 | **/__flow-tests__/**
18 |
--------------------------------------------------------------------------------
/src/api/subscriptions/subscriptionRemove.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponse, Auth } from '../transportTypes';
3 | import { apiDelete } from '../apiFetch';
4 |
5 | /** See https://zulip.com/api/unsubscribe */
6 | export default (
7 | auth: Auth,
8 | // TODO(server-future): This should use a stream ID (#3918), not stream name.
9 | // Server issue: https://github.com/zulip/zulip/issues/10744
10 | subscriptions: $ReadOnlyArray,
11 | // TODO(server-3.0): Send numeric user IDs (#3764), not emails.
12 | principals?: $ReadOnlyArray,
13 | ): Promise =>
14 | apiDelete(auth, 'users/me/subscriptions', {
15 | subscriptions: JSON.stringify(subscriptions),
16 | principals: JSON.stringify(principals),
17 | });
18 |
--------------------------------------------------------------------------------
/src/api/messages/sendMessage.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | import type { ApiResponse, Auth } from '../transportTypes';
4 | import { apiPost } from '../apiFetch';
5 |
6 | /** See https://zulip.com/api/send-message */
7 | export default async (
8 | auth: Auth,
9 | params: {|
10 | type: 'private' | 'stream',
11 | to: string,
12 | // TODO(server-2.0): Say "topic", not "subject"
13 | subject?: string,
14 | content: string,
15 | localId?: number,
16 | eventQueueId?: string,
17 | |},
18 | ): Promise =>
19 | apiPost(auth, 'messages', {
20 | type: params.type,
21 | to: params.to,
22 | subject: params.subject,
23 | content: params.content,
24 | local_id: params.localId,
25 | queue_id: params.eventQueueId,
26 | });
27 |
--------------------------------------------------------------------------------
/src/utils/networkActivity.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import { StatusBar, Platform } from 'react-native';
3 |
4 | // Network activity indicators should be visible if *any* network activity is occurring
5 |
6 | let activityCounter = 0;
7 |
8 | export const networkActivityStart = (isSilent: boolean) => {
9 | if (isSilent) {
10 | return;
11 | }
12 |
13 | activityCounter++;
14 | if (Platform.OS === 'ios') {
15 | StatusBar.setNetworkActivityIndicatorVisible(true);
16 | }
17 | };
18 |
19 | export const networkActivityStop = (isSilent: boolean) => {
20 | if (isSilent) {
21 | return;
22 | }
23 |
24 | activityCounter--;
25 | if (activityCounter === 0 && Platform.OS === 'ios') {
26 | StatusBar.setNetworkActivityIndicatorVisible(false);
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/src/boot/__tests__/reducers-test.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import reducers from '../reducers';
3 | import { discardKeys, storeKeys, cacheKeys } from '../store';
4 | import * as eg from '../../__tests__/lib/exampleData';
5 |
6 | describe('reducers', () => {
7 | test('reducers return the default states on unknown action', () => {
8 | // $FlowFixMe[incompatible-call] bogus action object
9 | expect(() => reducers({}, { type: 'UNKNOWN_ACTION' })).not.toThrow();
10 | });
11 |
12 | test('every reducer is listed in config as "discard", "store" or "cache"', () => {
13 | const configKeys = [...discardKeys, ...storeKeys, ...cacheKeys];
14 | const reducerKeys = Object.keys(eg.baseReduxState);
15 | expect(configKeys.sort()).toEqual(reducerKeys.sort());
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/sharing/types.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | export type SharedFile = {|
4 | name: string,
5 | mimeType: string,
6 | url: string,
7 | |};
8 |
9 | /**
10 | * The data we get when the user "shares" to Zulip from another app.
11 | *
12 | * On Android, these objects are sent to JS from our platform-native code,
13 | * constructed there by `getParamsFromIntent` in `SharingHelper.kt`.
14 | * The correspondence of that code with this type isn't type-checked.
15 | *
16 | * (On iOS, we don't currently support this feature in the first place.)
17 | */
18 | // prettier-ignore
19 | export type SharedData =
20 | // Note: Keep these in sync with platform-native code.
21 | | {| type: 'text', sharedText: string |}
22 | | {| type: 'file', files: $ReadOnlyArray |};
23 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/ServerContainer.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type {
5 | ForwardRefExoticComponent as $tsflower_subst$React$ForwardRefExoticComponent,
6 | ReactNode as $tsflower_subst$React$ReactNode,
7 | RefAttributes as $tsflower_subst$React$RefAttributes,
8 | } from 'tsflower/subst/react';
9 |
10 | import * as React from 'react';
11 | import { type ServerContextType } from './ServerContext';
12 | import { type ServerContainerRef } from './types';
13 | declare var _default: $tsflower_subst$React$ForwardRefExoticComponent<
14 | ServerContextType & {
15 | children: $tsflower_subst$React$ReactNode,
16 | ...
17 | } & $tsflower_subst$React$RefAttributes,
18 | >;
19 | export default _default;
20 |
--------------------------------------------------------------------------------
/types/react-native-safe-area-context/lib/typescript/src/specs/NativeSafeAreaView.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { ViewProps as $tsflower_subst$RN$ViewProps } from 'tsflower/subst/react-native';
5 | import type { Readonly } from 'tsflower/subst/lib';
6 | import { type WithDefault } from 'react-native/Libraries/Types/CodegenTypes';
7 | import { type HostComponent } from 'react-native';
8 |
9 | export type NativeProps = {
10 | ...$tsflower_subst$RN$ViewProps,
11 | mode?: WithDefault<'padding' | 'margin', 'padding'>,
12 | edges?: Readonly<{
13 | top: string,
14 | right: string,
15 | bottom: string,
16 | left: string,
17 | ...
18 | }>,
19 | ...
20 | };
21 |
22 | declare var _default: HostComponent;
23 | export default _default;
24 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/zulipmobile/notifications/FcmListenerService.kt:
--------------------------------------------------------------------------------
1 | package com.zulipmobile.notifications
2 |
3 | import com.facebook.react.ReactApplication
4 | import com.google.firebase.messaging.FirebaseMessagingService
5 | import com.google.firebase.messaging.RemoteMessage
6 |
7 | class FcmListenerService : FirebaseMessagingService() {
8 | override fun onMessageReceived(message: RemoteMessage) {
9 | onReceived(this, message.data)
10 | }
11 |
12 | override fun onNewToken(token: String) {
13 | super.onNewToken(token)
14 | val reactContext = (application as ReactApplication)
15 | .reactNativeHost
16 | .reactInstanceManager
17 | .currentReactContext
18 | NotificationsModule.emitToken(reactContext, token)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ios/ZulipMobile/UtilManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // UtilManager.m
3 | // ZulipMobile
4 | //
5 |
6 | #import "UtilManager.h"
7 |
8 | @implementation UtilManager
9 |
10 | RCT_EXPORT_MODULE();
11 |
12 | RCT_EXPORT_METHOD(randomBase64:(NSUInteger)length
13 | resolver:(RCTPromiseResolveBlock)resolve
14 | rejecter:(RCTPromiseRejectBlock)reject)
15 | {
16 | NSMutableData *data = [NSMutableData dataWithLength:length];
17 | int ret = SecRandomCopyBytes(kSecRandomDefault, length, [data mutableBytes]);
18 |
19 | if (ret != 0) {
20 | NSError *error = [NSError errorWithDomain:@"zulip" code:ret userInfo:nil];
21 | reject(@"random_failed", @"Could not generate random data", error);
22 | } else {
23 | resolve([data base64EncodedStringWithOptions:0]);
24 | }
25 | }
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/src/webview/css/cssEmojis.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ServerEmojiData } from '../../api/modelTypes';
3 |
4 | import { displayCharacterForUnicodeEmojiCode, availableUnicodeEmojiCodes } from '../../emoji/data';
5 |
6 | const codeToCss = (code, serverEmojiData): string =>
7 | `.emoji-${code}:before { content: '${displayCharacterForUnicodeEmojiCode(
8 | code,
9 | serverEmojiData,
10 | )}'; }`;
11 |
12 | const cssEmojis = (serverEmojiData: ServerEmojiData | null): string => {
13 | const availableCodes = serverEmojiData?.code_to_names.keys() ?? availableUnicodeEmojiCodes;
14 |
15 | const chunks = [];
16 | for (const code of availableCodes) {
17 | chunks.push(codeToCss(code, serverEmojiData));
18 | }
19 |
20 | return chunks.join('\n');
21 | };
22 |
23 | export default cssEmojis;
24 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type StackHeaderInterpolationProps, type StackHeaderInterpolatedStyle } from '../types';
5 | declare export function forUIKit(StackHeaderInterpolationProps): StackHeaderInterpolatedStyle;
6 | declare export function forFade(StackHeaderInterpolationProps): StackHeaderInterpolatedStyle;
7 | declare export function forSlideLeft(StackHeaderInterpolationProps): StackHeaderInterpolatedStyle;
8 | declare export function forSlideRight(StackHeaderInterpolationProps): StackHeaderInterpolatedStyle;
9 | declare export function forSlideUp(StackHeaderInterpolationProps): StackHeaderInterpolatedStyle;
10 | declare export function forNoAnimation(): StackHeaderInterpolatedStyle;
11 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/views/TouchableItem.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { ViewProps as $tsflower_subst$RN$ViewProps } from 'tsflower/subst/react-native';
5 |
6 | import type {
7 | ReactNode as $tsflower_subst$React$ReactNode,
8 | JSX$Element as $tsflower_subst$React$JSX$Element,
9 | } from 'tsflower/subst/react';
10 |
11 | import * as React from 'react';
12 | import 'react-native';
13 |
14 | export type Props = $tsflower_subst$RN$ViewProps & {
15 | pressColor?: string,
16 | disabled?: boolean,
17 | borderless?: boolean,
18 | delayPressIn?: number,
19 | onPress?: () => void,
20 | children: $tsflower_subst$React$ReactNode,
21 | ...
22 | };
23 |
24 | declare export default function TouchableItem(Props): $tsflower_subst$React$JSX$Element;
25 |
--------------------------------------------------------------------------------
/types/react-native-tab-view/lib/typescript/src/index.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | export { default as TabBar } from './TabBar';
5 | export { Props as TabBarProps } from './TabBar';
6 | export { default as TabView } from './TabView';
7 | export { Props as TabViewProps } from './TabView';
8 | export { default as TabBarIndicator } from './TabBarIndicator';
9 | export { Props as TabBarIndicatorProps } from './TabBarIndicator';
10 | export { default as TabBarItem } from './TabBarItem';
11 | export { Props as TabBarItemProps } from './TabBarItem';
12 | export { default as TouchableItem } from './TouchableItem';
13 | export { default as SceneMap } from './SceneMap';
14 | export { default as ScrollPager } from './ScrollPager';
15 | export { Route, NavigationState, SceneRendererProps } from './types';
16 |
--------------------------------------------------------------------------------
/src/common/SectionSeparatorBetween.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import React from 'react';
3 | import type { Node } from 'react';
4 | import SectionSeparator from './SectionSeparator';
5 |
6 | /*
7 | * Upstream `SectionList` is full of `any`s. This type is incomplete,
8 | * and just captures what we use.
9 | */
10 | type Props = $ReadOnly<{|
11 | leadingItem: ?{ ... },
12 | leadingSection: ?{ data: { length: number, ... }, ... },
13 | |}>;
14 |
15 | /** Can be passed to RN's `SectionList` as `SectionSeparatorComponent`. */
16 | export default function SectionSeparatorBetween(props: Props): Node {
17 | const { leadingItem, leadingSection } = props;
18 |
19 | if (leadingItem || !leadingSection || leadingSection.data.length === 0) {
20 | return null;
21 | }
22 |
23 | return ;
24 | }
25 |
--------------------------------------------------------------------------------
/src/utils/__tests__/DefaultMap-test.js:
--------------------------------------------------------------------------------
1 | // @flow strict-local
2 |
3 | import DefaultMap from '../DefaultMap';
4 |
5 | describe('DefaultMap', () => {
6 | test('smoke', () => {
7 | const m = new DefaultMap(() => []);
8 | expect([...m.map.entries()].sort()).toEqual([]);
9 |
10 | // Create a value.
11 | m.getOrCreate('a').push(1);
12 | expect([...m.map.entries()].sort()).toEqual([['a', [1]]]);
13 |
14 | // Different key gets a fresh value.
15 | m.getOrCreate('b').push(2);
16 | // prettier-ignore
17 | expect([...m.map.entries()].sort()).toEqual([['a', [1]], ['b', [2]]]);
18 |
19 | // Existing key gets the existing value.
20 | m.getOrCreate('a').push(3);
21 | // prettier-ignore
22 | expect([...m.map.entries()].sort()).toEqual([['a', [1, 3]], ['b', [2]]]);
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/firebase.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 | 1:835904834568:android:19a01c6476449260
14 | 835904834568
15 |
16 |
--------------------------------------------------------------------------------
/src/api/subscriptions/subscriptionAdd.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponse, Auth } from '../transportTypes';
3 | import { apiPost } from '../apiFetch';
4 |
5 | type SubscriptionObj = {|
6 | // TODO(server-future): This should use a stream ID (#3918), not stream name.
7 | // Server issue: https://github.com/zulip/zulip/issues/10744
8 | name: string,
9 | |};
10 |
11 | /** See https://zulip.com/api/subscribe */
12 | export default (
13 | auth: Auth,
14 | subscriptions: $ReadOnlyArray,
15 | // TODO(server-3.0): Send numeric user IDs (#3764), not emails.
16 | principals?: $ReadOnlyArray,
17 | ): Promise =>
18 | apiPost(auth, 'users/me/subscriptions', {
19 | subscriptions: JSON.stringify(subscriptions),
20 | principals: JSON.stringify(principals),
21 | });
22 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/views/MaskedView.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import * as React from 'react';
5 |
6 | type Props = {
7 | maskElement: React$Element,
8 | children: React$Element,
9 | ...
10 | };
11 |
12 | declare export default function MaskedView(
13 | Props,
14 | ): React$Element<
15 | | string
16 | | ((
17 | props: any,
18 | ) =>
19 | | React$Element<
20 | | string
21 | | any
22 | | $FlowFixMe /* new (props: any) => React.Component */ /* tsflower-unimplemented: ConstructorType */,
23 | >
24 | | null
25 | | $FlowFixMe) /* new (props: any) => React.Component */ /* tsflower-unimplemented: ConstructorType */,
26 | >;
27 | export {};
28 |
--------------------------------------------------------------------------------
/src/alertWords/alertWordsReducer.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { AlertWordsState, PerAccountApplicableAction } from '../types';
3 | import { REGISTER_COMPLETE, EVENT_ALERT_WORDS, RESET_ACCOUNT_DATA } from '../actionConstants';
4 | import { NULL_ARRAY } from '../nullObjects';
5 |
6 | const initialState = NULL_ARRAY;
7 |
8 | export default (
9 | state: AlertWordsState = initialState, // eslint-disable-line default-param-last
10 | action: PerAccountApplicableAction,
11 | ): AlertWordsState => {
12 | switch (action.type) {
13 | case RESET_ACCOUNT_DATA:
14 | return initialState;
15 |
16 | case REGISTER_COMPLETE:
17 | return action.data.alert_words;
18 |
19 | case EVENT_ALERT_WORDS:
20 | return action.alert_words || initialState;
21 |
22 | default:
23 | return state;
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/src/nav/IconUnreadMentions.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | import React from 'react';
4 | import type { Node } from 'react';
5 | import { View } from 'react-native';
6 |
7 | import { useSelector } from '../react-redux';
8 | import { getUnreadMentionsTotal } from '../selectors';
9 | import { IconMention } from '../common/Icons';
10 | import CountOverlay from '../common/CountOverlay';
11 |
12 | type Props = $ReadOnly<{|
13 | color: string,
14 | |}>;
15 |
16 | export default function IconUnreadMentions(props: Props): Node {
17 | const { color } = props;
18 | const unreadMentionsTotal = useSelector(getUnreadMentionsTotal);
19 |
20 | return (
21 |
22 |
23 |
24 |
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | /**
3 | * These two were added to the RN template file in
4 | * `facebook/react-native@f4d5e8c23` (released in 0.60.5) because of
5 | * conflicts with `eslint-config-react-native-community`. We haven't
6 | * activated that config (we might, it's #4119), and we haven't
7 | * otherwise found a use for these rules; we don't follow them.
8 | */
9 | // bracketSpacing: false,
10 | // jsxBracketSameLine: true,
11 |
12 | printWidth: 100,
13 |
14 | // Changed to "flow" just because Flow claims it's necessary for
15 | // formatting Flow enums. Unconfirmed…but it doesn't seem to hurt. Doc:
16 | // https://flow.org/en/docs/enums/enabling-enums/#toc-upgrade-tooling
17 | parser: 'flow',
18 |
19 | singleQuote: true,
20 | trailingComma: 'all',
21 | arrowParens: 'avoid',
22 | };
23 |
--------------------------------------------------------------------------------
/src/__tests__/isAppOwnDomain-test.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | import isAppOwnDomain from '../isAppOwnDomain';
4 |
5 | describe('isAppOwnDomain', () => {
6 | test.each([
7 | ['https://chat.zulip.org', true],
8 | ['https://zulipchat.com', true],
9 | ['https://zulip.com', true],
10 | ['https://example.zulipchat.com', true],
11 | ['https://example.zulip.com', true],
12 | ['https://example.zulip.com/api/v1/server_settings', true],
13 | ['https://example.zulip.com/avatar/1234', true],
14 |
15 | ['https://zulipchat.org', false],
16 | ['https://www.google.com', false],
17 | ['https://zulipchat.co.uk', false],
18 | ['https://chat.zulip.io', false],
19 | ])('%s should be %p', (urlStr: string, expected: boolean) => {
20 | expect(isAppOwnDomain(new URL(urlStr))).toBe(expected);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/api/subscriptions/setTopicMute.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponse, Auth } from '../transportTypes';
3 | import { apiPatch } from '../apiFetch';
4 |
5 | /** See https://zulip.com/api/mute-topic */
6 | export default async (
7 | auth: Auth,
8 | // TODO(server-2.0): Switch to stream ID (#3918), instead of name.
9 | // (The version that was introduced in isn't documented:
10 | // https://github.com/zulip/zulip/issues/11136#issuecomment-1033046851
11 | // but see:
12 | // https://github.com/zulip/zulip-mobile/issues/3244#issuecomment-840200325
13 | // )
14 | stream: string,
15 | topic: string,
16 | value: boolean,
17 | ): Promise =>
18 | apiPatch(auth, 'users/me/subscriptions/muted_topics', {
19 | stream,
20 | topic,
21 | op: value ? 'add' : 'remove',
22 | });
23 |
--------------------------------------------------------------------------------
/src/common/SearchEmptyState.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import React from 'react';
3 | import type { Node } from 'react';
4 | import { View } from 'react-native';
5 |
6 | import ZulipTextIntl from './ZulipTextIntl';
7 | import { createStyleSheet } from '../styles';
8 |
9 | const styles = createStyleSheet({
10 | container: {
11 | flex: 1,
12 | padding: 16,
13 | marginTop: 8,
14 | justifyContent: 'center',
15 | },
16 | text: {
17 | fontSize: 18,
18 | textAlign: 'center',
19 | },
20 | });
21 |
22 | type Props = $ReadOnly<{|
23 | text: string,
24 | |}>;
25 |
26 | export default function SearchEmptyState(props: Props): Node {
27 | const { text } = props;
28 |
29 | return (
30 |
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/zulipmobile/SentryUtils.kt:
--------------------------------------------------------------------------------
1 | package com.zulipmobile
2 |
3 | import io.sentry.Sentry
4 | import io.sentry.SentryLevel
5 |
6 | /**
7 | * A home for things that ought to be static extensions of `Sentry`.
8 | *
9 | * Extending Java classes with static members isn't currently a feature
10 | * available in Kotlin:
11 | * https://youtrack.jetbrains.com/issue/KT-11968
12 | * so this is our substitute.
13 | */
14 | class SentryX {
15 | companion object {
16 | /**
17 | * Like `Sentry.captureException`, but at level `SentryLevel.WARNING`.
18 | */
19 | public fun warnException(e: Throwable) {
20 | Sentry.withScope { scope ->
21 | scope.level = SentryLevel.WARNING
22 | Sentry.captureException(e)
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/caughtup/caughtUpSelectors.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { CaughtUp, CaughtUpState, PerAccountState, Narrow } from '../types';
3 | import { NULL_OBJECT } from '../nullObjects';
4 | import { keyFromNarrow } from '../utils/narrow';
5 |
6 | /** The value implicitly represented by a missing entry in CaughtUpState. */
7 | export const DEFAULT_CAUGHTUP: CaughtUp = {
8 | older: false,
9 | newer: false,
10 | };
11 |
12 | export const getCaughtUp = (state: PerAccountState): CaughtUpState => state.caughtUp || NULL_OBJECT;
13 |
14 | export const getCaughtUpForNarrowInner = (state: CaughtUpState, narrow: Narrow): CaughtUp =>
15 | state[keyFromNarrow(narrow)] || DEFAULT_CAUGHTUP;
16 |
17 | export const getCaughtUpForNarrow = (state: PerAccountState, narrow: Narrow): CaughtUp =>
18 | getCaughtUpForNarrowInner(getCaughtUp(state), narrow);
19 |
--------------------------------------------------------------------------------
/src/webview/html/__tests__/template-test.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | // $FlowFixMe[untyped-import]
3 | import escape from 'lodash.escape';
4 | import template from '../template';
5 |
6 | describe('template', () => {
7 | const evil = '&';
8 | const escaped = escape(evil);
9 |
10 | test('interpolates', () => {
11 | expect(template``).toEqual('');
12 | expect(template`a`).toEqual('a');
13 | expect(template`a${'b'}c`).toEqual('abc');
14 | });
15 |
16 | test('escapes HTML', () => {
17 | expect(template`a${evil}c`).toEqual(`a${escaped}c`);
18 | });
19 |
20 | test('optionally preserves HTML', () => {
21 | expect(template`a$!${evil}c`).toEqual(`a${evil}c`);
22 | });
23 |
24 | test('has an escape for the option', () => {
25 | expect(template`a$\!${evil}c`).toEqual(`a$!${escaped}c`);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/types/@react-navigation/bottom-tabs/lib/typescript/src/views/SafeAreaProviderCompat.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type {
5 | ReactNode as $tsflower_subst$React$ReactNode,
6 | JSX$Element as $tsflower_subst$React$JSX$Element,
7 | } from 'tsflower/subst/react';
8 |
9 | import * as React from 'react';
10 |
11 | declare export var initialSafeAreaInsets:
12 | | {
13 | top: number,
14 | bottom: number,
15 | right: number,
16 | left: number,
17 | ...
18 | }
19 | | {
20 | top: number,
21 | right: number,
22 | bottom: number,
23 | left: number,
24 | ...
25 | };
26 |
27 | type Props = { children: $tsflower_subst$React$ReactNode, ... };
28 | declare export default function SafeAreaProviderCompat(Props): $tsflower_subst$React$JSX$Element;
29 | export {};
30 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/views/BorderlessButton.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type {
5 | ComponentProps as $tsflower_subst$React$ComponentProps,
6 | JSX$Element as $tsflower_subst$React$JSX$Element,
7 | } from 'tsflower/subst/react';
8 |
9 | import * as React from 'react';
10 | import { BaseButton } from 'react-native-gesture-handler';
11 | type Props = $tsflower_subst$React$ComponentProps & {
12 | pressOpacity: number,
13 | ...
14 | };
15 |
16 | declare export default class BorderlessButton extends React.Component {
17 | defaultProps: {
18 | activeOpacity: number,
19 | borderless: boolean,
20 | ...
21 | };
22 | opacity: any;
23 | handleActiveStateChange: any;
24 | render(): $tsflower_subst$React$JSX$Element;
25 | }
26 |
27 | export {};
28 |
--------------------------------------------------------------------------------
/types/expo-modules-core/build/Platform.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | // TODO[tsflower]: From @types/react-native; add to subst/react-native.
5 | type PlatformOSType = 'ios' | 'android' | 'macos' | 'windows' | 'web' | 'native';
6 |
7 | export type PlatformSelectOSType = PlatformOSType | 'native' | 'electron' | 'default';
8 |
9 | export type PlatformSelect = (
10 | specifics: $FlowFixMe /* {
11 | [platform in PlatformSelectOSType]?: T;
12 | } */ /* tsflower-unimplemented: MappedType */,
13 | ) => T;
14 |
15 | declare var Platform: {
16 | OS: 'ios' | 'android' | 'windows' | 'macos' | 'web',
17 | select: PlatformSelect,
18 | isDOMAvailable: boolean,
19 | canUseEventListeners: boolean,
20 | canUseViewport: boolean,
21 | isAsyncDebugging: boolean,
22 | ...
23 | };
24 |
25 | export default Platform;
26 |
--------------------------------------------------------------------------------
/types/react-native-safe-area-context/lib/typescript/src/specs/NativeSafeAreaContext.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type TurboModule } from 'react-native/Libraries/TurboModule/RCTExport';
5 | import { type Double } from 'react-native/Libraries/Types/CodegenTypes';
6 |
7 | export interface Spec extends TurboModule {
8 | getConstants: () => {
9 | initialWindowMetrics?: {
10 | insets: {
11 | top: Double,
12 | right: Double,
13 | bottom: Double,
14 | left: Double,
15 | ...
16 | },
17 | frame: {
18 | x: Double,
19 | y: Double,
20 | width: Double,
21 | height: Double,
22 | ...
23 | },
24 | ...
25 | },
26 | ...
27 | };
28 | }
29 |
30 | declare var _default: Spec | null;
31 | export default _default;
32 |
--------------------------------------------------------------------------------
/src/api/pollForEvents.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponseSuccess, Auth } from './transportTypes';
3 | import type { GeneralEvent } from './eventTypes';
4 | import { apiGet } from './apiFetch';
5 |
6 | type ApiResponsePollEvents = {|
7 | ...$Exact,
8 | events: $ReadOnlyArray,
9 | |};
10 |
11 | /** See https://zulip.com/api/get-events */
12 | // TODO: Handle downgrading server across kThresholdVersion, which we'd hear
13 | // about in `restart` events, by throwing a ServerTooOldError. This case
14 | // seems pretty rare but is possible.
15 | export default (auth: Auth, queueId: string, lastEventId: number): Promise =>
16 | apiGet(
17 | auth,
18 | 'events',
19 | {
20 | queue_id: queueId,
21 | last_event_id: lastEventId,
22 | },
23 | true,
24 | );
25 |
--------------------------------------------------------------------------------
/src/chat/InvalidNarrow.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | import React from 'react';
4 | import type { Node } from 'react';
5 | import { StyleSheet, View } from 'react-native';
6 |
7 | import type { Narrow } from '../types';
8 | import ZulipTextIntl from '../common/ZulipTextIntl';
9 |
10 | const styles = StyleSheet.create({
11 | container: {
12 | flex: 1,
13 | alignItems: 'center',
14 | justifyContent: 'center',
15 | },
16 | text: {
17 | fontSize: 20,
18 | paddingLeft: 10,
19 | padding: 8,
20 | },
21 | });
22 |
23 | type Props = $ReadOnly<{|
24 | narrow: Narrow,
25 | |}>;
26 |
27 | export default function InvalidNarrow(props: Props): Node {
28 | return (
29 |
30 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/types/@react-navigation/bottom-tabs/lib/typescript/src/views/Badge.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type {
5 | WithAnimatedValue as $tsflower_subst$RN$Animated$WithAnimatedValue,
6 | StyleProp as $tsflower_subst$RN$StyleProp,
7 | TextStyle as $tsflower_subst$RN$TextStyle,
8 | } from 'tsflower/subst/react-native';
9 |
10 | import type { JSX$Element as $tsflower_subst$React$JSX$Element } from 'tsflower/subst/react';
11 | import { Animated } from 'react-native';
12 |
13 | type Props = {
14 | visible: boolean,
15 | children?: string | number,
16 | size?: number,
17 | style?: $tsflower_subst$RN$Animated$WithAnimatedValue<
18 | $tsflower_subst$RN$StyleProp<$tsflower_subst$RN$TextStyle>,
19 | >,
20 | ...
21 | };
22 |
23 | declare export default function Badge(Props): $tsflower_subst$React$JSX$Element | null;
24 | export {};
25 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/views/TouchableItem.ios.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type {
5 | ComponentProps as $tsflower_subst$React$ComponentProps,
6 | JSX$Element as $tsflower_subst$React$JSX$Element,
7 | } from 'tsflower/subst/react';
8 |
9 | import * as React from 'react';
10 | import { BaseButton } from 'react-native-gesture-handler';
11 | type Props = $tsflower_subst$React$ComponentProps & {
12 | pressOpacity: number,
13 | ...
14 | };
15 |
16 | declare export default class TouchableItem extends React.Component {
17 | defaultProps: {
18 | pressOpacity: number,
19 | borderless: boolean,
20 | enabled: boolean,
21 | ...
22 | };
23 | opacity: any;
24 | handleActiveStateChange: any;
25 | render(): $tsflower_subst$React$JSX$Element;
26 | }
27 |
28 | export {};
29 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | mavenLocal()
5 | google()
6 | }
7 | }
8 |
9 | rootProject.name = 'ZulipMobile'
10 |
11 | apply from: '../node_modules/expo/scripts/autolinking.gradle'
12 | useExpoModules()
13 |
14 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle");
15 | applyNativeModulesSettingsGradle(settings)
16 |
17 | include ':app'
18 |
19 | include ':ReactAndroid'
20 | project(':ReactAndroid').projectDir = new File(
21 | rootProject.projectDir, '../node_modules/react-native/ReactAndroid')
22 |
23 | // The RN Gradle Plugin is needed to build RN from source.
24 | // (We do that to make some changes to RN, with our zulip/react-native fork.)
25 | includeBuild('../node_modules/react-native/packages/react-native-gradle-plugin')
26 |
--------------------------------------------------------------------------------
/src/mute/muteModelTypes.js:
--------------------------------------------------------------------------------
1 | // @flow strict-local
2 | import Immutable from 'immutable';
3 |
4 | import { type UserTopicVisibilityPolicy } from '../api/modelTypes';
5 |
6 | /**
7 | * The "visibility policy" our user has chosen for each topic.
8 | *
9 | * See jsdoc of UserTopicVisibilityPolicy for background.
10 | *
11 | * In this data structure, the keys are stream ID and then topic name.
12 | * Values of `UserTopicVisibilityPolicy.None` are represented by absence,
13 | * and streams where the map would be empty are also omitted.
14 | */
15 | // TODO(#5381): Ideally we'd call this UserTopicState and `state.userTopic`.
16 | // But it's currently a pain to actually rename a state subtree: #5381.
17 | export type MuteState = Immutable.Map<
18 | number, // stream ID
19 | Immutable.Map<
20 | string, // topic name
21 | UserTopicVisibilityPolicy,
22 | >,
23 | >;
24 |
--------------------------------------------------------------------------------
/types/@react-navigation/material-top-tabs/lib/typescript/src/views/MaterialTopTabView.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { JSX$Element as $tsflower_subst$React$JSX$Element } from 'tsflower/subst/react';
5 | import { type TabNavigationState, type ParamListBase } from '@react-navigation/native';
6 |
7 | import {
8 | type MaterialTopTabDescriptorMap,
9 | type MaterialTopTabNavigationConfig,
10 | type MaterialTopTabNavigationHelpers,
11 | } from '../types';
12 |
13 | type Props = MaterialTopTabNavigationConfig & {
14 | state: TabNavigationState,
15 | navigation: MaterialTopTabNavigationHelpers,
16 | descriptors: MaterialTopTabDescriptorMap,
17 | tabBarPosition?: 'top' | 'bottom',
18 | ...
19 | };
20 |
21 | declare export default function MaterialTopTabView(Props): $tsflower_subst$React$JSX$Element;
22 | export {};
23 |
--------------------------------------------------------------------------------
/types/@react-navigation/stack/lib/typescript/src/views/MaskedViewNative.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import typeof * as $tsflower_import_typeof$_$_40_react_2d_native_2d_community_2f_masked_2d_view from '@react-native-community/masked-view';
5 |
6 | import type {
7 | ComponentProps as $tsflower_subst$React$ComponentProps,
8 | JSX$Element as $tsflower_subst$React$JSX$Element,
9 | } from 'tsflower/subst/react';
10 |
11 | import * as React from 'react';
12 | type MaskedViewType = $ElementType<
13 | $tsflower_import_typeof$_$_40_react_2d_native_2d_community_2f_masked_2d_view,
14 | 'default',
15 | >;
16 | type Props = $tsflower_subst$React$ComponentProps & {
17 | children: React$Element,
18 | ...
19 | };
20 | declare export default function MaskedView(Props): $tsflower_subst$React$JSX$Element;
21 | export {};
22 |
--------------------------------------------------------------------------------
/src/styles/miscStyles.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import { CONTROL_SIZE } from './constants';
3 |
4 | export const statics = {
5 | largerText: {
6 | fontSize: 20,
7 | },
8 | row: {
9 | flexDirection: 'row',
10 | alignItems: 'center',
11 | },
12 | listItem: {
13 | flexDirection: 'row',
14 | alignItems: 'center',
15 | paddingVertical: 8,
16 | paddingHorizontal: 16,
17 | },
18 | flexed: {
19 | flex: 1,
20 | },
21 | rightItem: {
22 | marginLeft: 'auto',
23 | },
24 | center: {
25 | flex: 1,
26 | justifyContent: 'center',
27 | alignItems: 'center',
28 | },
29 | field: {
30 | flex: 1,
31 | flexDirection: 'row',
32 | height: CONTROL_SIZE,
33 | marginTop: 5,
34 | marginBottom: 5,
35 | },
36 | alignBottom: {
37 | flexDirection: 'column',
38 | justifyContent: 'flex-end',
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/zulipmobile/ShareToZulipActivity.kt:
--------------------------------------------------------------------------------
1 | package com.zulipmobile;
2 |
3 | import android.content.ComponentName
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import androidx.appcompat.app.AppCompatActivity
7 |
8 | /// The activity for when a user shares to Zulip from another app.
9 | ///
10 | /// This is a tiny shim activity, which forwards the user on to our
11 | /// [MainActivity] to get the actual UI for sharing to Zulip.
12 | class ShareToZulipActivity : AppCompatActivity() {
13 | override fun onCreate(savedInstanceState: Bundle?) {
14 | super.onCreate(savedInstanceState)
15 | intent.component =
16 | ComponentName(applicationContext.packageName, "com.zulipmobile.MainActivity")
17 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
18 | startActivity(intent)
19 | finish()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/api/messages/updateMessageFlags.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponseSuccess, Auth } from '../transportTypes';
3 | import type { UserMessageFlag } from '../modelTypes';
4 | import { apiPost } from '../apiFetch';
5 |
6 | export type ApiResponseUpdateMessageFlags = {|
7 | ...$Exact,
8 |
9 | // The `messages` property is deprecated. See discussion:
10 | // https://chat.zulip.org/#narrow/stream/378-api-design/topic/mark-as-unread.20request/near/1463920
11 | -messages: $ReadOnlyArray,
12 | |};
13 |
14 | /** https://zulip.com/api/update-message-flags */
15 | export default (
16 | auth: Auth,
17 | messageIds: $ReadOnlyArray,
18 | op: 'add' | 'remove',
19 | flag: UserMessageFlag,
20 | ): Promise =>
21 | apiPost(auth, 'messages/flags', { messages: JSON.stringify(messageIds), flag, op });
22 |
--------------------------------------------------------------------------------
/src/api/settings/toggleMobilePushSettings.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ApiResponse, Auth } from '../transportTypes';
3 | import { apiPatch } from '../apiFetch';
4 |
5 | const getRequestBody = (opp, value) => {
6 | const data = {};
7 | if (opp === 'offline_notification_change') {
8 | data.enable_offline_push_notifications = value;
9 | } else if (opp === 'online_notification_change') {
10 | data.enable_online_push_notifications = value;
11 | } else if (opp === 'stream_notification_change') {
12 | data.enable_stream_push_notifications = value;
13 | }
14 | return data;
15 | };
16 |
17 | export default async ({
18 | auth,
19 | opp,
20 | value,
21 | }: {|
22 | auth: Auth,
23 | opp: string,
24 | value: boolean,
25 | |}): Promise =>
26 | apiPatch(auth, 'settings/notifications', {
27 | ...getRequestBody(opp, value),
28 | });
29 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | const isDevelopment = process.env.NODE_ENV === 'development';
3 |
4 | type Config = {|
5 | requestLongTimeoutMs: number,
6 | messagesPerRequest: number,
7 | messageListThreshold: number,
8 | enableReduxLogging: boolean,
9 | enableErrorConsoleLogging: boolean,
10 | appOwnDomains: $ReadOnlyArray,
11 | |};
12 |
13 | const config: Config = {
14 | // A completely unreasonable amount of time for a request, or
15 | // several retries of a request, to take. If this elapses, we're
16 | // better off giving up.
17 | requestLongTimeoutMs: 60 * 1000,
18 |
19 | messagesPerRequest: 100,
20 | messageListThreshold: 4000,
21 | enableReduxLogging: isDevelopment && !!global.btoa,
22 | enableErrorConsoleLogging: true,
23 | appOwnDomains: ['zulip.com', 'zulipchat.com', 'chat.zulip.org'],
24 | };
25 |
26 | export default config;
27 |
--------------------------------------------------------------------------------
/src/api/notifications/savePushToken.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { Auth } from '../transportTypes';
3 | import { apiPost } from '../apiFetch';
4 |
5 | /**
6 | * Tell the server our device token for push notifications.
7 | *
8 | * @param mobileOS - Choose the server-side API intended for iOS or Android clients.
9 | */
10 | export default async (auth: Auth, mobileOS: 'ios' | 'android', token: string): Promise => {
11 | const routeName = mobileOS === 'android' ? 'android_gcm_reg_id' : 'apns_device_token';
12 | const extraParams =
13 | // The `Object.freeze` is to work around a Flow issue:
14 | // https://github.com/facebook/flow/issues/2386#issuecomment-695064325
15 | mobileOS === 'android' ? Object.freeze({}) : { appid: 'org.zulip.Zulip' };
16 | return apiPost(auth, `users/me/${routeName}`, {
17 | token,
18 | ...extraParams,
19 | });
20 | };
21 |
--------------------------------------------------------------------------------
/types/@react-navigation/routers/lib/typescript/src/index.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import * as CommonActions from './CommonActions';
5 | export { CommonActions };
6 | export { default as BaseRouter } from './BaseRouter';
7 | export { default as StackRouter, StackActions } from './StackRouter';
8 | export {
9 | StackActionHelpers,
10 | StackActionType,
11 | StackRouterOptions,
12 | StackNavigationState,
13 | } from './StackRouter';
14 | export { default as TabRouter, TabActions } from './TabRouter';
15 | export { TabActionHelpers, TabActionType, TabRouterOptions, TabNavigationState } from './TabRouter';
16 | export { default as DrawerRouter, DrawerActions } from './DrawerRouter';
17 | export {
18 | DrawerActionHelpers,
19 | DrawerActionType,
20 | DrawerRouterOptions,
21 | DrawerNavigationState,
22 | } from './DrawerRouter';
23 | export * from './types';
24 |
--------------------------------------------------------------------------------
/docs/howto/forked-rn.md:
--------------------------------------------------------------------------------
1 | # Using a `react-native` with cherry-picked or custom changes
2 |
3 | Since 2024-09, we use a fork of `react-native` to make changes
4 | atop 0.68.7. We prefer to avoid upgrading to later `react-native`
5 | releases because it's laborious and we're eager to retire this
6 | codebase and transition to `zulip-flutter`.
7 |
8 | When there's an issue in React Native that calls for changes in
9 | React Native:
10 |
11 | - Push those changes to our RN fork, `zulip/react-native`,
12 | on the `0.68.7-zulip` branch.
13 |
14 | - Update the `package.json`:
15 |
16 | ```json
17 | "react-native": "zulip/react-native#",
18 | ```
19 |
20 | - Run `yarn`.
21 |
22 | When building for Android, it will take longer the first time because
23 | React Native is built from source. (`react-native` releases on NPM,
24 | which we've been using until recently, come with a pre-built binary.)
25 |
--------------------------------------------------------------------------------
/src/autocomplete/getAutocompletedText.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { InputSelection } from '../types';
3 |
4 | export default (textWhole: string, autocompleteText: string, selection: InputSelection): string => {
5 | const { start, end } = selection;
6 | let remainder = '';
7 | let text = textWhole;
8 | if (start === end && start !== text.length) {
9 | // new letter is typed
10 | remainder = text.substring(start, text.length);
11 | text = text.substring(0, start);
12 | }
13 |
14 | const lastIndex: number = Math.max(
15 | text.lastIndexOf(':'),
16 | text.lastIndexOf('#'),
17 | text.lastIndexOf('@'),
18 | );
19 |
20 | const prefix = text[lastIndex] === ':' ? ':' : `${text[lastIndex]}`;
21 | const suffix = text[lastIndex] === ':' ? ':' : '';
22 |
23 | return `${text.substring(0, lastIndex)}${prefix}${autocompleteText}${suffix} ${remainder}`;
24 | };
25 |
--------------------------------------------------------------------------------
/src/user-picker/AvatarList.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import React from 'react';
3 | import type { Node } from 'react';
4 | import { FlatList } from 'react-native';
5 |
6 | import type { UserId, UserOrBot } from '../types';
7 | import AvatarItem from './AvatarItem';
8 |
9 | type Props = $ReadOnly<{|
10 | users: $ReadOnlyArray,
11 | listRef: React$Ref,
12 | onPress: UserId => void,
13 | |}>;
14 |
15 | export default function AvatarList(props: Props): Node {
16 | const { listRef, users, onPress } = props;
17 |
18 | return (
19 | String(user.user_id)}
26 | renderItem={({ item: user }) => }
27 | />
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/types/@react-navigation/native/lib/typescript/src/useLinkProps.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { MouseEvent as $tsflower_subst$React$MouseEvent } from 'tsflower/subst/react';
5 | import type { GestureResponderEvent as $tsflower_subst$RN$GestureResponderEvent } from 'tsflower/subst/react-native';
6 | import * as React from 'react';
7 | import 'react-native';
8 | import { type NavigationAction } from '@react-navigation/core';
9 |
10 | type Props = {
11 | to: string,
12 | action?: NavigationAction,
13 | ...
14 | };
15 |
16 | declare export default function useLinkProps(Props): {
17 | href: string,
18 | accessibilityRole: 'link',
19 | onPress: (
20 | e?:
21 | | $tsflower_subst$React$MouseEvent
22 | | $tsflower_subst$RN$GestureResponderEvent
23 | | void,
24 | ) => void,
25 | ...
26 | };
27 |
28 | export {};
29 |
--------------------------------------------------------------------------------
/src/common/SectionHeader.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import React, { useContext } from 'react';
3 | import type { Node } from 'react';
4 | import { View } from 'react-native';
5 |
6 | import { ThemeContext, createStyleSheet } from '../styles';
7 | import ZulipTextIntl from './ZulipTextIntl';
8 | import type { LocalizableReactText } from '../types';
9 |
10 | const styles = createStyleSheet({
11 | header: {
12 | padding: 10,
13 | backgroundColor: 'hsla(0, 0%, 50%, 0.75)',
14 | },
15 | });
16 |
17 | type Props = $ReadOnly<{|
18 | text: LocalizableReactText,
19 | |}>;
20 |
21 | export default function SectionHeader(props: Props): Node {
22 | const { text } = props;
23 | const themeData = useContext(ThemeContext);
24 |
25 | return (
26 |
27 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/src/react-native-action-sheet.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { ComponentType, ElementConfig } from 'react';
3 | // $FlowFixMe[untyped-import]
4 | import { connectActionSheet as connectActionSheetInner } from '@expo/react-native-action-sheet';
5 |
6 | import type { BoundedDiff } from './generics';
7 |
8 | export type ShowActionSheetWithOptions = (
9 | { options: string[], cancelButtonIndex: number, ... },
10 | (number) => void,
11 | ) => void;
12 |
13 | /**
14 | * Exactly like the `connectActionSheet` in
15 | * `react-native-action-sheet` upstream, but more typed.
16 | */
17 | export function connectActionSheet>(
18 | WrappedComponent: C,
19 | ): ComponentType<
20 | BoundedDiff<
21 | $Exact>,
22 | {| +showActionSheetWithOptions: ShowActionSheetWithOptions |},
23 | >,
24 | > {
25 | return connectActionSheetInner(WrappedComponent);
26 | }
27 |
--------------------------------------------------------------------------------
/src/utils/keyMirror.js:
--------------------------------------------------------------------------------
1 | // @flow strict-local
2 |
3 | import { objectFromEntries } from '../jsBackport';
4 |
5 | /**
6 | * Return an object where each property value equals the key.
7 | *
8 | * This is a handy idiom for making objects that function like enums.
9 | * For an example, see:
10 | * https://flow.org/en/docs/enums/migrating-legacy-patterns/#toc-keymirror
11 | *
12 | * The main reason to use this helper rather than just write out the result
13 | * directly is that it lets Flow infer a more specific type. For example:
14 | *
15 | * const Status1 = keyMirror({ on: null, off: null });
16 | * Status1.on; // type is 'on'
17 | *
18 | * const Status2 = { on: 'on', off: 'off' };
19 | * Status2.on; // type is string, which is less helpful
20 | */
21 | export function keyMirror(o: O): $ObjMapi(K) => K> {
22 | return objectFromEntries(Object.keys(o).map(k => [k, k]));
23 | }
24 |
--------------------------------------------------------------------------------
/types/@react-navigation/core/lib/typescript/src/getActionFromState.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import { type NavigationState, type PartialState } from '@react-navigation/routers';
5 | import * as CommonActions from '@react-navigation/routers/lib/typescript/src/CommonActions';
6 | import { type PathConfigMap, type NavigatorScreenParams } from './types';
7 |
8 | type Options = {
9 | initialRouteName?: string,
10 | screens: PathConfigMap,
11 | ...
12 | };
13 |
14 | type NavigateAction> = {
15 | type: 'NAVIGATE',
16 | payload: {
17 | name: string,
18 | params?: NavigatorScreenParams,
19 | ...
20 | },
21 | ...
22 | };
23 |
24 | declare export default function getActionFromState(
25 | state: PartialState>,
26 | options?: Options,
27 | ): NavigateAction> | CommonActions.Action | void;
28 | export {};
29 |
--------------------------------------------------------------------------------
/src/title/ActivityText.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 |
3 | import React from 'react';
4 | import type { Node } from 'react';
5 | import type { TextStyleProp } from 'react-native/Libraries/StyleSheet/StyleSheet';
6 |
7 | import type { UserOrBot } from '../types';
8 | import { useSelector } from '../react-redux';
9 | import { getUserLastActiveAsRelativeTimeString } from '../presence/presenceModel';
10 | import ZulipText from '../common/ZulipText';
11 |
12 | type Props = $ReadOnly<{|
13 | style: TextStyleProp,
14 | user: UserOrBot,
15 | |}>;
16 |
17 | export default function ActivityText(props: Props): Node {
18 | const { style, user } = props;
19 |
20 | const activeTime = useSelector(state =>
21 | getUserLastActiveAsRelativeTimeString(state, user, Date.now()),
22 | );
23 | if (activeTime == null) {
24 | return null;
25 | }
26 |
27 | return ;
28 | }
29 |
--------------------------------------------------------------------------------
/ios/ZulipMobile-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | // To use an Objective-C module in a Swift file, first just try importing it
2 | // at the top of your Swift file, like so:
3 | //
4 | // import React.RCTBridgeModule
5 | //
6 | // If you can't find an import line that Xcode understands, instead try
7 | // adding an import line in this file, like so:
8 | //
9 | // #import
10 | //
11 | // That should make the module (plus the modules *it* imports, actually)
12 | // available in all our project's Swift files, without the Swift files
13 | // needing an import line of their own.
14 | //
15 | // The first approach (an import line in the Swift file) is preferred
16 | // because it looks like how imports normally work in Swift. But sometimes
17 | // we can't find an import line that works; not sure why. Discussion:
18 | // https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/ios.2FZulipMobile-Bridging-Header.2Eh/near/1520435
19 |
--------------------------------------------------------------------------------
/react-native.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * See https://github.com/react-native-community/cli/blob/master/docs/configuration.md.
3 | *
4 | * To print the full config from the React Native CLI, run
5 | * `react-native config`.
6 | */
7 | module.exports = {
8 | /**
9 | * See https://github.com/react-native-community/cli/blob/master/docs/dependencies.md.
10 | *
11 | * Currently, we only use this to blacklist some native-code
12 | * libraries, per-platform, that we don't want to be linked with
13 | * "autolinking".
14 | *
15 | * For more about "autolinking", see
16 | * https://github.com/react-native-community/cli/blob/master/docs/autolinking.md.
17 | */
18 | dependencies: {
19 | 'react-native-vector-icons': {
20 | platforms: {
21 | // We're using a setup that doesn't involve linking
22 | // `VectorIconsPackage` on Android.
23 | android: null,
24 | },
25 | },
26 | },
27 | };
28 |
--------------------------------------------------------------------------------
/src/webview/html/messageTypingAsHtml.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import { PixelRatio } from 'react-native';
3 |
4 | import template from './template';
5 | import type { UserOrBot } from '../../types';
6 |
7 | const typingAvatar = (realm: URL, user: UserOrBot): string => template`
8 |
9 |
,
17 | )
18 | .toString()})
19 |
20 | `;
21 |
22 | export default (realm: URL, users: $ReadOnlyArray): string => template`
23 | $!${users.map(user => typingAvatar(realm, user)).join('')}
24 |
25 |
26 |
27 |
28 |
29 | `;
30 |
--------------------------------------------------------------------------------
/types/@react-native-clipboard/clipboard/dist/Clipboard.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { EmitterSubscription as $tsflower_subst$RN$EmitterSubscription } from 'tsflower/subst/react-native';
5 | import 'react-native';
6 |
7 | declare export var Clipboard: {
8 | getString(): Promise,
9 | getStrings(): Promise,
10 | getImagePNG(): Promise,
11 | getImageJPG(): Promise,
12 | setImage(content: string): void,
13 | getImage(): Promise,
14 | setString(content: string): void,
15 | setStrings(content: string[]): void,
16 | hasString(): Promise,
17 | hasImage(): Promise,
18 | hasURL(): Promise | void,
19 | hasNumber(): Promise | void,
20 | hasWebURL(): Promise | void,
21 | addListener(callback: () => void): $tsflower_subst$RN$EmitterSubscription,
22 | removeAllListeners(): void,
23 | ...
24 | };
25 |
--------------------------------------------------------------------------------
/src/webview/html/messageListElementHtml.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | import type { GetText, MessageListElement } from '../../types';
3 | import { ensureUnreachable } from '../../generics';
4 | import type { BackgroundData } from '../backgroundData';
5 |
6 | import message from './message';
7 | import header from './header';
8 | import time from './time';
9 |
10 | export default ({
11 | backgroundData,
12 | element,
13 | _,
14 | }: {|
15 | backgroundData: BackgroundData,
16 | element: MessageListElement,
17 | _: GetText,
18 | |}): string => {
19 | switch (element.type) {
20 | case 'time':
21 | return time(element);
22 | case 'header':
23 | return header(backgroundData, element, _);
24 | case 'message':
25 | return message(backgroundData, element, _);
26 | default:
27 | ensureUnreachable(element);
28 | throw new Error(`Unidentified element.type: '${element.type}'`);
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/types/react-native-safe-area-context/lib/typescript/src/SafeAreaView.js.flow:
--------------------------------------------------------------------------------
1 | /* @flow
2 | * @generated by TsFlower
3 | */
4 | import type { NativeMethods } from 'react-native/Libraries/Renderer/shims/ReactNativeTypes';
5 |
6 | import type {
7 | ForwardRefExoticComponent as $tsflower_subst$React$ForwardRefExoticComponent,
8 | RefAttributes as $tsflower_subst$React$RefAttributes,
9 | } from 'tsflower/subst/react';
10 |
11 | import type { Readonly } from 'tsflower/subst/lib';
12 | import * as React from 'react';
13 | import { type NativeSafeAreaViewProps } from './SafeArea.types';
14 | import { type NativeProps } from './specs/NativeSafeAreaView';
15 |
16 | export type SafeAreaViewProps = NativeSafeAreaViewProps;
17 | declare export var SafeAreaView: $tsflower_subst$React$ForwardRefExoticComponent<
18 | NativeSafeAreaViewProps &
19 | $tsflower_subst$React$RefAttributes & Readonly>,
20 | >;
21 |
--------------------------------------------------------------------------------
/src/styles/constants.js:
--------------------------------------------------------------------------------
1 | /* @flow strict-local */
2 | // $FlowFixMe[untyped-import]
3 | import Color from 'color';
4 |
5 | export const CONTROL_SIZE = 44;
6 | export const NAVBAR_SIZE = 58;
7 |
8 | // The value `hsl(222, 99%, 69%)` is chosen to match `rgb(100, 146, 253.5)`,
9 | // which is the sRGB midpoint of the Zulip logo's gradient.
10 | //
11 | // Note this color is also used directly in several other places:
12 | // * in our WebView's CSS;
13 | // * under `android/` (search for "BRAND_COLOR");
14 | // * in `ios/**/Brand.colorset/Contents.json`.
15 | export const BRAND_COLOR = 'hsl(222, 99%, 69%)';
16 | export const BORDER_COLOR = BRAND_COLOR;
17 | export const HIGHLIGHT_COLOR: string = Color(BRAND_COLOR).fade(0.5).toString();
18 |
19 | export const HALF_COLOR = 'hsla(0, 0%, 50%, 0.5)';
20 | export const QUARTER_COLOR = 'hsla(0, 0%, 50%, 0.25)';
21 |
22 | // Material warning color
23 | export const kWarningColor = 'hsl(40, 100%, 60%)';
24 |
--------------------------------------------------------------------------------