1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00

Desktop, Mobile: Fixes #10645: Show notification in case Joplin Cloud credential is not valid anymore (#10649)

This commit is contained in:
pedr 2024-07-01 12:21:17 -03:00 committed by Laurent Cozic
parent 3270122419
commit 78d9a7e636
10 changed files with 62 additions and 6 deletions

View File

@ -420,6 +420,7 @@ class Application extends BaseApplication {
AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId })); AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId }));
AlarmService.setLogger(reg.logger()); AlarmService.setLogger(reg.logger());
reg.setDispatch(this.dispatch.bind(this));
reg.setShowErrorMessageBoxHandler((message: string) => { bridge().showErrorMessageBox(message); }); reg.setShowErrorMessageBoxHandler((message: string) => { bridge().showErrorMessageBox(message); });
if (Setting.value('flagOpenDevTools')) { if (Setting.value('flagOpenDevTools')) {

View File

@ -97,6 +97,7 @@ interface Props {
notesSortOrderField: string; notesSortOrderField: string;
notesSortOrderReverse: boolean; notesSortOrderReverse: boolean;
notesColumns: NoteListColumns; notesColumns: NoteListColumns;
showInvalidJoplinCloudCredential: boolean;
} }
interface ShareFolderDialogOptions { interface ShareFolderDialogOptions {
@ -592,6 +593,13 @@ class MainScreenComponent extends React.Component<Props, State> {
}); });
}; };
const onViewJoplinCloudLoginScreen = () => {
this.props.dispatch({
type: 'NAV_GO',
routeName: 'JoplinCloudLogin',
});
};
const onViewSyncSettingsScreen = () => { const onViewSyncSettingsScreen = () => {
this.props.dispatch({ this.props.dispatch({
type: 'NAV_GO', type: 'NAV_GO',
@ -684,6 +692,12 @@ class MainScreenComponent extends React.Component<Props, State> {
); );
} else if (this.props.mustUpgradeAppMessage) { } else if (this.props.mustUpgradeAppMessage) {
msg = this.renderNotificationMessage(this.props.mustUpgradeAppMessage); msg = this.renderNotificationMessage(this.props.mustUpgradeAppMessage);
} else if (this.props.showInvalidJoplinCloudCredential) {
msg = this.renderNotificationMessage(
_('Your Joplin Cloud credentials are invalid, please login.'),
_('Login to Joplin Cloud.'),
onViewJoplinCloudLoginScreen,
);
} }
return ( return (
@ -705,7 +719,8 @@ class MainScreenComponent extends React.Component<Props, State> {
props.isSafeMode || props.isSafeMode ||
this.showShareInvitationNotification(props) || this.showShareInvitationNotification(props) ||
this.props.needApiAuth || this.props.needApiAuth ||
!!this.props.mustUpgradeAppMessage; !!this.props.mustUpgradeAppMessage ||
props.showInvalidJoplinCloudCredential;
} }
public registerCommands() { public registerCommands() {
@ -965,6 +980,7 @@ const mapStateToProps = (state: AppState) => {
notesSortOrderField: state.settings['notes.sortOrder.field'], notesSortOrderField: state.settings['notes.sortOrder.field'],
notesSortOrderReverse: state.settings['notes.sortOrder.reverse'], notesSortOrderReverse: state.settings['notes.sortOrder.reverse'],
notesColumns: validateColumns(state.settings['notes.columns']), notesColumns: validateColumns(state.settings['notes.columns']),
showInvalidJoplinCloudCredential: state.settings['sync.target'] === 10 && state.mustAuthenticate,
}; };
}; };

View File

@ -16,6 +16,7 @@ interface WrapperProps {
mustUpgradeAppMessage?: string; mustUpgradeAppMessage?: string;
shareInvitations?: ShareInvitation[]; shareInvitations?: ShareInvitation[];
processingShareInvitationResponse?: boolean; processingShareInvitationResponse?: boolean;
showInvalidJoplinCloudCredential?: boolean;
} }
const WarningBannerWrapper: React.FC<WrapperProps> = props => { const WarningBannerWrapper: React.FC<WrapperProps> = props => {
@ -29,6 +30,7 @@ const WarningBannerWrapper: React.FC<WrapperProps> = props => {
mustUpgradeAppMessage={props.mustUpgradeAppMessage ?? ''} mustUpgradeAppMessage={props.mustUpgradeAppMessage ?? ''}
shareInvitations={props.shareInvitations ?? []} shareInvitations={props.shareInvitations ?? []}
processingShareInvitationResponse={props.processingShareInvitationResponse ?? false} processingShareInvitationResponse={props.processingShareInvitationResponse ?? false}
showInvalidJoplinCloudCredential={props.showInvalidJoplinCloudCredential ?? false}
/>; />;
}; };

View File

@ -19,6 +19,7 @@ interface Props {
mustUpgradeAppMessage: string; mustUpgradeAppMessage: string;
shareInvitations: ShareInvitation[]; shareInvitations: ShareInvitation[];
processingShareInvitationResponse: boolean; processingShareInvitationResponse: boolean;
showInvalidJoplinCloudCredential: boolean;
} }
@ -50,6 +51,9 @@ export const WarningBannerComponent: React.FC<Props> = props => {
if (props.hasDisabledEncryptionItems) { if (props.hasDisabledEncryptionItems) {
warningComps.push(renderWarningBox('Status', _('Some items cannot be decrypted.'))); warningComps.push(renderWarningBox('Status', _('Some items cannot be decrypted.')));
} }
if (props.showInvalidJoplinCloudCredential) {
warningComps.push(renderWarningBox('JoplinCloudLogin', _('Your Joplin Cloud credentials are invalid, please login.')));
}
const shareInvitation = props.shareInvitations.find(inv => inv.status === ShareUserStatus.Waiting); const shareInvitation = props.shareInvitations.find(inv => inv.status === ShareUserStatus.Waiting);
if ( if (
@ -85,5 +89,6 @@ export default connect((state: AppState) => {
mustUpgradeAppMessage: state.mustUpgradeAppMessage, mustUpgradeAppMessage: state.mustUpgradeAppMessage,
shareInvitations: state.shareService.shareInvitations, shareInvitations: state.shareService.shareInvitations,
processingShareInvitationResponse: state.shareService.processingShareInvitationResponse, processingShareInvitationResponse: state.shareService.processingShareInvitationResponse,
showInvalidJoplinCloudCredential: state.settings['sync.target'] === 10 && state.mustAuthenticate,
}; };
})(WarningBannerComponent); })(WarningBannerComponent);

View File

@ -39,7 +39,7 @@ const { connect, Provider } = require('react-redux');
import { Provider as PaperProvider, MD3DarkTheme, MD3LightTheme } from 'react-native-paper'; import { Provider as PaperProvider, MD3DarkTheme, MD3LightTheme } from 'react-native-paper';
const { BackButtonService } = require('./services/back-button.js'); const { BackButtonService } = require('./services/back-button.js');
import NavService from '@joplin/lib/services/NavService'; import NavService from '@joplin/lib/services/NavService';
import { createStore, applyMiddleware } from 'redux'; import { createStore, applyMiddleware, Dispatch } from 'redux';
import reduxSharedMiddleware from '@joplin/lib/components/shared/reduxSharedMiddleware'; import reduxSharedMiddleware from '@joplin/lib/components/shared/reduxSharedMiddleware';
const { shimInit } = require('./utils/shim-init-react.js'); const { shimInit } = require('./utils/shim-init-react.js');
const { AppNav } = require('./components/app-nav.js'); const { AppNav } = require('./components/app-nav.js');
@ -489,7 +489,7 @@ const getInitialActiveFolder = async () => {
}; };
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
async function initialize(dispatch: Function) { async function initialize(dispatch: Dispatch) {
shimInit(); shimInit();
setDispatch(dispatch); setDispatch(dispatch);
@ -531,6 +531,7 @@ async function initialize(dispatch: Function) {
reg.setLogger(mainLogger); reg.setLogger(mainLogger);
reg.setShowErrorMessageBoxHandler((message: string) => { alert(message); }); reg.setShowErrorMessageBoxHandler((message: string) => { alert(message); });
reg.setDispatch(dispatch);
BaseService.logger_ = mainLogger; BaseService.logger_ = mainLogger;
// require('@joplin/lib/ntpDate').setLogger(reg.logger()); // require('@joplin/lib/ntpDate').setLogger(reg.logger());

View File

@ -56,7 +56,9 @@ export default class SyncTargetJoplinCloud extends BaseSyncTarget {
const sessionId = await api.sessionId(); const sessionId = await api.sessionId();
return !!sessionId; return !!sessionId;
} catch (error) { } catch (error) {
if (error.code === 403) return false; if (error.code === 403) {
return false;
}
throw error; throw error;
} }
} }
@ -65,8 +67,10 @@ export default class SyncTargetJoplinCloud extends BaseSyncTarget {
return 'JoplinCloudLogin'; return 'JoplinCloudLogin';
} }
// While Joplin Cloud requires password, the new login method makes this
// information useless
public static requiresPassword() { public static requiresPassword() {
return true; return false;
} }
public async fileApi(): Promise<FileApi> { public async fileApi(): Promise<FileApi> {

View File

@ -524,6 +524,9 @@ export default class Synchronizer {
// await uploadSyncInfo(this.api(), remoteInfo); // await uploadSyncInfo(this.api(), remoteInfo);
} }
} catch (error) { } catch (error) {
if (error.code === 403) {
this.dispatch({ type: 'MUST_AUTHENTICATE', value: true });
}
if (error.code === 'outdatedSyncTarget') { if (error.code === 'outdatedSyncTarget') {
Setting.setValue('sync.upgradeState', Setting.SYNC_UPGRADE_STATE_SHOULD_DO); Setting.setValue('sync.upgradeState', Setting.SYNC_UPGRADE_STATE_SHOULD_DO);
} }

View File

@ -8,7 +8,7 @@ const targetToRequiresPassword: Record<string, boolean> = {
'webdav': true, 'webdav': true,
'amazon_s3': true, 'amazon_s3': true,
'joplinServer': true, 'joplinServer': true,
'joplinCloud': true, 'joplinCloud': false,
'onedrive': false, 'onedrive': false,
'dropbox': false, 'dropbox': false,
}; };

View File

@ -133,6 +133,7 @@ export interface State {
lastDeletion: StateLastDeletion; lastDeletion: StateLastDeletion;
lastDeletionNotificationTime: number; lastDeletionNotificationTime: number;
mustUpgradeAppMessage: string; mustUpgradeAppMessage: string;
mustAuthenticate: boolean;
// Extra reducer keys go here: // Extra reducer keys go here:
pluginService: PluginServiceState; pluginService: PluginServiceState;
@ -215,6 +216,7 @@ export const defaultState: State = {
}, },
lastDeletionNotificationTime: 0, lastDeletionNotificationTime: 0,
mustUpgradeAppMessage: '', mustUpgradeAppMessage: '',
mustAuthenticate: false,
pluginService: pluginServiceDefaultState, pluginService: pluginServiceDefaultState,
shareService: shareServiceDefaultState, shareService: shareServiceDefaultState,
@ -1324,6 +1326,10 @@ const reducer = produce((draft: Draft<State> = defaultState, action: any) => {
draft.mustUpgradeAppMessage = action.message; draft.mustUpgradeAppMessage = action.message;
break; break;
case 'MUST_AUTHENTICATE':
draft.mustAuthenticate = action.value;
break;
case 'NOTE_LIST_RENDERER_ADD': case 'NOTE_LIST_RENDERER_ADD':
{ {
const noteListRendererIds = draft.noteListRendererIds.slice(); const noteListRendererIds = draft.noteListRendererIds.slice();

View File

@ -2,6 +2,7 @@ import Logger from '@joplin/utils/Logger';
import Setting from './models/Setting'; import Setting from './models/Setting';
import shim from './shim'; import shim from './shim';
import SyncTargetRegistry from './SyncTargetRegistry'; import SyncTargetRegistry from './SyncTargetRegistry';
import { AnyAction, Dispatch } from 'redux';
class Registry { class Registry {
@ -21,6 +22,7 @@ class Registry {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private db_: any; private db_: any;
private isOnMobileData_ = false; private isOnMobileData_ = false;
private dispatch_: Dispatch = (() => {}) as Dispatch;
public logger() { public logger() {
if (!this.logger_) { if (!this.logger_) {
@ -45,6 +47,14 @@ class Registry {
this.showErrorMessageBoxHandler_(message); this.showErrorMessageBoxHandler_(message);
} }
public setDispatch(dispatch: Dispatch) {
this.dispatch_ = dispatch;
}
private dispatch(action: AnyAction) {
return this.dispatch_(action);
}
// If isOnMobileData is true, the doWifiConnectionCheck is not set // If isOnMobileData is true, the doWifiConnectionCheck is not set
// and the sync.mobileWifiOnly setting is true it will cancel the sync. // and the sync.mobileWifiOnly setting is true it will cancel the sync.
public setIsOnMobileData(isOnMobileData: boolean) { public setIsOnMobileData(isOnMobileData: boolean) {
@ -139,10 +149,18 @@ class Registry {
} }
if (!(await this.syncTarget(syncTargetId).isAuthenticated())) { if (!(await this.syncTarget(syncTargetId).isAuthenticated())) {
this.dispatch({
type: 'MUST_AUTHENTICATE',
value: true,
});
this.logger().info('Synchroniser is missing credentials - manual sync required to authenticate.'); this.logger().info('Synchroniser is missing credentials - manual sync required to authenticate.');
promiseResolve(); promiseResolve();
return; return;
} }
this.dispatch({
type: 'MUST_AUTHENTICATE',
value: false,
});
try { try {
const sync = await this.syncTarget(syncTargetId).synchronizer(); const sync = await this.syncTarget(syncTargetId).synchronizer();