Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3abe24f
chore: migrate NewServerView to hooks
OtavioStasiak Oct 15, 2024
5580641
chore: updated types and migrate to useAppSelector
OtavioStasiak Oct 16, 2024
d658b28
chore: created hooks and separated components
OtavioStasiak Oct 17, 2024
f916332
chore: created useConnectServer hook
OtavioStasiak Oct 18, 2024
825b054
minor changes
OtavioStasiak Oct 18, 2024
1decef4
minor changes
OtavioStasiak Oct 23, 2024
b666a75
minor changes
OtavioStasiak Oct 24, 2024
7dd83df
Merge branch 'develop' into chore-migrate-to-hooks-new-server-view
OtavioStasiak Apr 4, 2025
7c25b9e
chore: adjusted code
OtavioStasiak Apr 4, 2025
1efe989
fix: selectServerRequest missing version
OtavioStasiak Apr 4, 2025
af7f0bd
chore: improved code
OtavioStasiak Apr 4, 2025
863af62
fix: removed empty spaces
OtavioStasiak Apr 4, 2025
89a7592
fix: test
OtavioStasiak Apr 4, 2025
8f59ca8
feat: testing accessibility focus change on error
OtavioStasiak Apr 7, 2025
7d52570
fix: comment alert
OtavioStasiak Apr 7, 2025
45e6c1b
Merge branch 'develop' into feat-a11y-inline-error-new-server-view
OtavioStasiak Apr 15, 2025
09114cd
chore: adjusted error logic
OtavioStasiak Apr 15, 2025
a0d4d62
fix: accessibility announcement
OtavioStasiak Apr 16, 2025
cea3574
fix: lint
OtavioStasiak Apr 16, 2025
333fea7
fix: error style
OtavioStasiak Apr 16, 2025
5a713f8
fix: props on ControlledFormTextInput
OtavioStasiak Apr 17, 2025
c0d56a6
fix: adjusted accessibility label
OtavioStasiak Apr 17, 2025
926f795
Merge branch 'develop' into feat-a11y-inline-error-new-server-view
OtavioStasiak May 6, 2025
e6967b3
fix: lint
OtavioStasiak May 6, 2025
9367d47
fix: code adjustments
OtavioStasiak May 6, 2025
df06322
Merge branch 'develop' into feat-a11y-inline-error-new-server-view
OtavioStasiak Jun 18, 2025
c5d7384
fix: error announcement
OtavioStasiak Jun 18, 2025
eb37f5d
chore: code improvements
OtavioStasiak Jun 18, 2025
0e2a500
fix: newServerView clear errors onChange
OtavioStasiak Jun 18, 2025
4d903b2
fix: error announcement
OtavioStasiak Jun 18, 2025
bc0d7c9
Merge branch 'develop' into feat-a11y-inline-error-new-server-view
OtavioStasiak Jun 24, 2025
b3105be
fix: remove input text color
OtavioStasiak Jun 24, 2025
7b196d8
Merge branch 'develop' into feat-a11y-inline-error-new-server-view
OtavioStasiak Jun 27, 2025
fefab6e
Merge branch 'develop' into feat-a11y-inline-error-new-server-view
OtavioStasiak Jul 2, 2025
0f1499d
Merge branch 'develop' into feat-a11y-inline-error-new-server-view
OtavioStasiak Jul 2, 2025
6882216
fix: remove duplicated a11y announcement error
OtavioStasiak Jul 2, 2025
2a13528
Merge branch 'develop' into feat-a11y-inline-error-new-server-view
OtavioStasiak Jul 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions app/actions/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ interface ISelectServerSuccess extends Action {
version: string;
name: string;
}

interface IServerFailure extends Action {
failureMessage?: string;
}
export interface IServerRequestAction extends Action {
server: string;
username: string | null;
Expand All @@ -25,7 +27,7 @@ interface IServerInit extends Action {
previousServer: string;
}

export type TActionServer = ISelectServerAction & ISelectServerSuccess & IServerRequestAction & IServerInit;
export type TActionServer = ISelectServerAction & ISelectServerSuccess & IServerRequestAction & IServerInit & IServerFailure;

export function selectServerRequest(
server: string,
Expand Down Expand Up @@ -92,9 +94,10 @@ export function serverSuccess(): Action {
};
}

export function serverFailure(): Action {
export function serverFailure(failureMessage?: string): IServerFailure {
return {
type: SERVER.FAILURE
type: SERVER.FAILURE,
failureMessage
};
}

Expand Down
10 changes: 7 additions & 3 deletions app/reducers/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface IServer {
connecting: boolean;
connected: boolean;
failure: boolean;
failureMessage?: string;
server: string;
version: string;
name: string | null;
Expand All @@ -31,14 +32,16 @@ export default function server(state = initialState, action: TActionServer): ISe
return {
...state,
connecting: true,
failure: false
failure: false,
failureMessage: undefined
};
case SERVER.FAILURE:
return {
...state,
connecting: false,
connected: false,
failure: true
failure: true,
failureMessage: action.failureMessage
};
case SERVER.SELECT_CANCEL:
return {
Expand Down Expand Up @@ -79,7 +82,8 @@ export default function server(state = initialState, action: TActionServer): ISe
connecting: false,
connected: true,
loading: false,
changingServer: false
changingServer: false,
failureMessage: undefined
};
case SERVER.SELECT_FAILURE:
return {
Expand Down
7 changes: 2 additions & 5 deletions app/sagas/selectServer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { put, takeLatest } from 'redux-saga/effects';
import { Alert } from 'react-native';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { Q } from '@nozbe/watermelondb';
import valid from 'semver/functions/valid';
Expand Down Expand Up @@ -104,14 +103,12 @@ const getServerInfoSaga = function* getServerInfoSaga({ server, raiseError = tru
const serverInfoResult = yield* call(getServerInfo, server);
if (raiseError) {
if (!serverInfoResult.success) {
Alert.alert(I18n.t('Invalid_workspace_URL'), serverInfoResult.message);
yield put(serverFailure());
yield put(serverFailure(I18n.t('Invalid_URL')));
return;
}
const websocketInfo = yield* call(Services.getWebsocketInfo, { server });
if (!websocketInfo.success) {
Alert.alert(I18n.t('Invalid_workspace_URL'), websocketInfo.message);
yield put(serverFailure());
yield put(serverFailure(I18n.t('Invalid_URL')));
return;
}
}
Expand Down
23 changes: 23 additions & 0 deletions app/views/NewServerView/components/CertificatePicker/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { StyleSheet } from 'react-native';

import sharedStyles from '../../../Styles';

const styles = StyleSheet.create({
certificatePicker: {
alignItems: 'center',
justifyContent: 'flex-end'
},
chooseCertificateTitle: {
...sharedStyles.textRegular
},
chooseCertificate: {
...sharedStyles.textSemibold
},
buttonPrompt: {
...sharedStyles.textRegular,
textAlign: 'center',
lineHeight: 20
}
});

export default styles;
44 changes: 26 additions & 18 deletions app/views/NewServerView/components/ServerInput/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import React, { useState } from 'react';
import { StyleSheet, TextInputProps, View } from 'react-native';
import { Control } from 'react-hook-form';

import { useTheme } from '../../../../theme';
import { FormTextInput } from '../../../../containers/TextInput';
import { ControlledFormTextInput } from '../../../../containers/TextInput';
import { TServerHistoryModel } from '../../../../definitions';
import I18n from '../../../../i18n';
import { CustomIcon } from '../../../../containers/CustomIcon';
Expand All @@ -13,39 +14,42 @@ import { ServersHistoryActionSheetContent } from '../ServersHistoryActionSheetCo
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'flex-end',
alignItems: 'center',
gap: 4
},
inputContainer: {
flex: 1
},
input: {
marginTop: 0,
marginBottom: 0
},
serversHistoryButton: {
paddingVertical: 9
}
});

interface IServerInput extends TextInputProps {
text: string;
error?: string;
control: Control<
{
workspaceUrl: string;
},
any
>;
serversHistory: TServerHistoryModel[];
onSubmit(): void;
onDelete(item: TServerHistoryModel): void;
onPressServerHistory(serverHistory: TServerHistoryModel): void;
}

const ServerInput = ({
text,
error,
control,
serversHistory,
onChangeText,
onSubmit,
onDelete,
onPressServerHistory
}: IServerInput): JSX.Element => {
const [focused, setFocused] = useState(false);
const { colors } = useTheme();

const historyButtonMarginBottom = error ? 15 : -15;

const handleDeleteServerHistory = (item: TServerHistoryModel) => {
onDelete(item);
hideActionSheetRef();
Expand All @@ -71,23 +75,27 @@ const ServerInput = ({
return (
<View style={styles.container}>
<View style={styles.inputContainer}>
<FormTextInput
<ControlledFormTextInput
name='workspaceUrl'
control={control}
label={I18n.t('Workspace_URL')}
containerStyle={styles.input}
value={text}
containerStyle={styles.inputContainer}
inputStyle={error && !focused ? { borderColor: colors.fontDanger } : {}}
returnKeyType='send'
onChangeText={onChangeText}
testID='new-server-view-input'
onSubmitEditing={onSubmit}
clearButtonMode='while-editing'
keyboardType='url'
textContentType='URL'
required
onChangeText={onChangeText}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
error={error}
/>
</View>

{serversHistory?.length > 0 ? (
<View style={styles.serversHistoryButton}>
<View style={{ marginBottom: historyButtonMarginBottom }}>
<Touch
accessible
accessibilityLabel={I18n.t('Open_servers_history')}
Expand Down
10 changes: 5 additions & 5 deletions app/views/NewServerView/hooks/useConnectServer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import { ISubmitParams } from '../definitions';
import basicAuth from '../methods/basicAuth';

type TUseNewServerProps = {
text: string;
workspaceUrl: string;
certificate: string | null;
previousServer: string | null;
};

const useConnectServer = ({ text, certificate, previousServer }: TUseNewServerProps) => {
const useConnectServer = ({ workspaceUrl, certificate, previousServer }: TUseNewServerProps) => {
const dispatch = useDispatch();

const submit = ({ fromServerHistory = false, username, serverUrl }: ISubmitParams = {}) => {
Expand All @@ -27,17 +27,17 @@ const useConnectServer = ({ text, certificate, previousServer }: TUseNewServerPr
sdk.disconnect();
dispatch(selectServerClear());
}
if (text || serverUrl) {
if (workspaceUrl || serverUrl) {
Keyboard.dismiss();
const server = completeUrl(serverUrl ?? text);
const server = completeUrl(serverUrl ?? workspaceUrl);

// Save info - SSL Pinning
if (certificate) {
UserPreferences.setString(`${CERTIFICATE_KEY}-${server}`, certificate);
}

// Save info - HTTP Basic Authentication
basicAuth(server, serverUrl ?? text);
basicAuth(server, serverUrl ?? workspaceUrl);

if (fromServerHistory) {
dispatch(serverRequest(server, username, true));
Expand Down
40 changes: 30 additions & 10 deletions app/views/NewServerView/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useEffect, useState } from 'react';
import { BackHandler, Keyboard, Text } from 'react-native';
import { AccessibilityInfo, BackHandler, Keyboard, Text } from 'react-native';
import { useDispatch } from 'react-redux';
import { Image } from 'expo-image';
import { useForm } from 'react-hook-form';

import { inviteLinksClear } from '../../actions/inviteLinks';
import { selectServerRequest, serverFinishAdd, serverRequest } from '../../actions/server';
Expand All @@ -27,27 +28,38 @@ import styles from './styles';
const NewServerView = ({ navigation }: INewServerViewProps) => {
const dispatch = useDispatch();
const { colors } = useTheme();
const { previousServer, connecting } = useAppSelector(state => ({
const { previousServer, connecting, failureMessage } = useAppSelector(state => ({
previousServer: state.server.previousServer,
connecting: state.server.connecting
connecting: state.server.connecting,
failureMessage: state.server.failureMessage
}));

const [text, setText] = useState<string>('');
const {
control,
watch,
formState: { errors },
setValue,
setError,
clearErrors
} = useForm({ mode: 'onChange', defaultValues: { workspaceUrl: '' } });

const workspaceUrl = watch('workspaceUrl');
const [showBottomInfo, setShowBottomInfo] = useState<boolean>(true);
const { deleteServerHistory, queryServerHistory, serversHistory } = useServersHistory();
const { certificate, chooseCertificate, removeCertificate } = useCertificate();
const { submit } = useConnectServer({ text, certificate, previousServer });
const { submit } = useConnectServer({ workspaceUrl, certificate, previousServer });
const phoneMarginTop = previousServer ? 32 : 84;
const marginTop = isTablet ? 0 : phoneMarginTop;
const formContainerStyle = previousServer ? { paddingBottom: 100 } : {};

const onChangeText = (text: string) => {
setText(text);
setValue('workspaceUrl', text);
queryServerHistory(text);
clearErrors();
};

const onPressServerHistory = (serverHistory: TServerHistoryModel) => {
setText(serverHistory.url);
setValue('workspaceUrl', serverHistory.url);
submit({ fromServerHistory: true, username: serverHistory?.username, serverUrl: serverHistory?.url });
};

Expand All @@ -64,7 +76,7 @@ const NewServerView = ({ navigation }: INewServerViewProps) => {
if (!server) {
return;
}
setText(server);
setValue('workspaceUrl', server);
server = completeUrl(server);
dispatch(serverRequest(server));
};
Expand Down Expand Up @@ -92,6 +104,13 @@ const NewServerView = ({ navigation }: INewServerViewProps) => {
});
};

useEffect(() => {
if (failureMessage && !errors.workspaceUrl) {
AccessibilityInfo.announceForAccessibility(failureMessage);
setError('workspaceUrl', { message: failureMessage });
}
}, [failureMessage]);

useEffect(() => {
EventEmitter.addEventListener('NewServer', handleNewServerEvent);
const backHandler = BackHandler.addEventListener('hardwareBackPress', handleBackPress);
Expand Down Expand Up @@ -146,7 +165,8 @@ const NewServerView = ({ navigation }: INewServerViewProps) => {
{I18n.t('Add_server')}
</Text>
<ServerInput
text={text}
error={errors.workspaceUrl?.message}
control={control}
serversHistory={serversHistory}
onChangeText={onChangeText}
onSubmit={submit}
Expand All @@ -157,7 +177,7 @@ const NewServerView = ({ navigation }: INewServerViewProps) => {
title={I18n.t('Connect')}
type='primary'
onPress={submit}
disabled={!text || connecting}
disabled={!workspaceUrl || connecting}
loading={connecting}
style={styles.connectButton}
testID='new-server-view-button'
Expand Down
Loading