You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Desktop, Mobile: Fixes #10645: Show notification in case Joplin Cloud credential is not valid anymore (#10649)
This commit is contained in:
		| @@ -420,6 +420,7 @@ class Application extends BaseApplication { | ||||
| 		AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId })); | ||||
| 		AlarmService.setLogger(reg.logger()); | ||||
|  | ||||
| 		reg.setDispatch(this.dispatch.bind(this)); | ||||
| 		reg.setShowErrorMessageBoxHandler((message: string) => { bridge().showErrorMessageBox(message); }); | ||||
|  | ||||
| 		if (Setting.value('flagOpenDevTools')) { | ||||
|   | ||||
| @@ -97,6 +97,7 @@ interface Props { | ||||
| 	notesSortOrderField: string; | ||||
| 	notesSortOrderReverse: boolean; | ||||
| 	notesColumns: NoteListColumns; | ||||
| 	showInvalidJoplinCloudCredential: boolean; | ||||
| } | ||||
|  | ||||
| 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 = () => { | ||||
| 			this.props.dispatch({ | ||||
| 				type: 'NAV_GO', | ||||
| @@ -684,6 +692,12 @@ class MainScreenComponent extends React.Component<Props, State> { | ||||
| 			); | ||||
| 		} else if (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 ( | ||||
| @@ -705,7 +719,8 @@ class MainScreenComponent extends React.Component<Props, State> { | ||||
| 			props.isSafeMode || | ||||
| 			this.showShareInvitationNotification(props) || | ||||
| 			this.props.needApiAuth || | ||||
| 			!!this.props.mustUpgradeAppMessage; | ||||
| 			!!this.props.mustUpgradeAppMessage || | ||||
| 			props.showInvalidJoplinCloudCredential; | ||||
| 	} | ||||
|  | ||||
| 	public registerCommands() { | ||||
| @@ -965,6 +980,7 @@ const mapStateToProps = (state: AppState) => { | ||||
| 		notesSortOrderField: state.settings['notes.sortOrder.field'], | ||||
| 		notesSortOrderReverse: state.settings['notes.sortOrder.reverse'], | ||||
| 		notesColumns: validateColumns(state.settings['notes.columns']), | ||||
| 		showInvalidJoplinCloudCredential: state.settings['sync.target'] === 10 && state.mustAuthenticate, | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,7 @@ interface WrapperProps { | ||||
| 	mustUpgradeAppMessage?: string; | ||||
| 	shareInvitations?: ShareInvitation[]; | ||||
| 	processingShareInvitationResponse?: boolean; | ||||
| 	showInvalidJoplinCloudCredential?: boolean; | ||||
| } | ||||
|  | ||||
| const WarningBannerWrapper: React.FC<WrapperProps> = props => { | ||||
| @@ -29,6 +30,7 @@ const WarningBannerWrapper: React.FC<WrapperProps> = props => { | ||||
| 		mustUpgradeAppMessage={props.mustUpgradeAppMessage ?? ''} | ||||
| 		shareInvitations={props.shareInvitations ?? []} | ||||
| 		processingShareInvitationResponse={props.processingShareInvitationResponse ?? false} | ||||
| 		showInvalidJoplinCloudCredential={props.showInvalidJoplinCloudCredential ?? false} | ||||
| 	/>; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -19,6 +19,7 @@ interface Props { | ||||
| 	mustUpgradeAppMessage: string; | ||||
| 	shareInvitations: ShareInvitation[]; | ||||
| 	processingShareInvitationResponse: boolean; | ||||
| 	showInvalidJoplinCloudCredential: boolean; | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -50,6 +51,9 @@ export const WarningBannerComponent: React.FC<Props> = props => { | ||||
| 	if (props.hasDisabledEncryptionItems) { | ||||
| 		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); | ||||
| 	if ( | ||||
| @@ -85,5 +89,6 @@ export default connect((state: AppState) => { | ||||
| 		mustUpgradeAppMessage: state.mustUpgradeAppMessage, | ||||
| 		shareInvitations: state.shareService.shareInvitations, | ||||
| 		processingShareInvitationResponse: state.shareService.processingShareInvitationResponse, | ||||
| 		showInvalidJoplinCloudCredential: state.settings['sync.target'] === 10 && state.mustAuthenticate, | ||||
| 	}; | ||||
| })(WarningBannerComponent); | ||||
|   | ||||
| @@ -39,7 +39,7 @@ const { connect, Provider } = require('react-redux'); | ||||
| import { Provider as PaperProvider, MD3DarkTheme, MD3LightTheme } from 'react-native-paper'; | ||||
| const { BackButtonService } = require('./services/back-button.js'); | ||||
| 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'; | ||||
| const { shimInit } = require('./utils/shim-init-react.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 | ||||
| async function initialize(dispatch: Function) { | ||||
| async function initialize(dispatch: Dispatch) { | ||||
| 	shimInit(); | ||||
|  | ||||
| 	setDispatch(dispatch); | ||||
| @@ -531,6 +531,7 @@ async function initialize(dispatch: Function) { | ||||
|  | ||||
| 	reg.setLogger(mainLogger); | ||||
| 	reg.setShowErrorMessageBoxHandler((message: string) => { alert(message); }); | ||||
| 	reg.setDispatch(dispatch); | ||||
|  | ||||
| 	BaseService.logger_ = mainLogger; | ||||
| 	// require('@joplin/lib/ntpDate').setLogger(reg.logger()); | ||||
|   | ||||
| @@ -56,7 +56,9 @@ export default class SyncTargetJoplinCloud extends BaseSyncTarget { | ||||
| 			const sessionId = await api.sessionId(); | ||||
| 			return !!sessionId; | ||||
| 		} catch (error) { | ||||
| 			if (error.code === 403) return false; | ||||
| 			if (error.code === 403) { | ||||
| 				return false; | ||||
| 			} | ||||
| 			throw error; | ||||
| 		} | ||||
| 	} | ||||
| @@ -65,8 +67,10 @@ export default class SyncTargetJoplinCloud extends BaseSyncTarget { | ||||
| 		return 'JoplinCloudLogin'; | ||||
| 	} | ||||
|  | ||||
| 	// While Joplin Cloud requires password, the new login method makes this | ||||
| 	// information useless | ||||
| 	public static requiresPassword() { | ||||
| 		return true; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	public async fileApi(): Promise<FileApi> { | ||||
|   | ||||
| @@ -524,6 +524,9 @@ export default class Synchronizer { | ||||
| 					// await uploadSyncInfo(this.api(), remoteInfo); | ||||
| 				} | ||||
| 			} catch (error) { | ||||
| 				if (error.code === 403) { | ||||
| 					this.dispatch({ type: 'MUST_AUTHENTICATE', value: true }); | ||||
| 				} | ||||
| 				if (error.code === 'outdatedSyncTarget') { | ||||
| 					Setting.setValue('sync.upgradeState', Setting.SYNC_UPGRADE_STATE_SHOULD_DO); | ||||
| 				} | ||||
|   | ||||
| @@ -8,7 +8,7 @@ const targetToRequiresPassword: Record<string, boolean> = { | ||||
| 	'webdav': true, | ||||
| 	'amazon_s3': true, | ||||
| 	'joplinServer': true, | ||||
| 	'joplinCloud': true, | ||||
| 	'joplinCloud': false, | ||||
| 	'onedrive': false, | ||||
| 	'dropbox': false, | ||||
| }; | ||||
|   | ||||
| @@ -133,6 +133,7 @@ export interface State { | ||||
| 	lastDeletion: StateLastDeletion; | ||||
| 	lastDeletionNotificationTime: number; | ||||
| 	mustUpgradeAppMessage: string; | ||||
| 	mustAuthenticate: boolean; | ||||
|  | ||||
| 	// Extra reducer keys go here: | ||||
| 	pluginService: PluginServiceState; | ||||
| @@ -215,6 +216,7 @@ export const defaultState: State = { | ||||
| 	}, | ||||
| 	lastDeletionNotificationTime: 0, | ||||
| 	mustUpgradeAppMessage: '', | ||||
| 	mustAuthenticate: false, | ||||
|  | ||||
| 	pluginService: pluginServiceDefaultState, | ||||
| 	shareService: shareServiceDefaultState, | ||||
| @@ -1324,6 +1326,10 @@ const reducer = produce((draft: Draft<State> = defaultState, action: any) => { | ||||
| 			draft.mustUpgradeAppMessage = action.message; | ||||
| 			break; | ||||
|  | ||||
| 		case 'MUST_AUTHENTICATE': | ||||
| 			draft.mustAuthenticate = action.value; | ||||
| 			break; | ||||
|  | ||||
| 		case 'NOTE_LIST_RENDERER_ADD': | ||||
| 			{ | ||||
| 				const noteListRendererIds = draft.noteListRendererIds.slice(); | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import Logger from '@joplin/utils/Logger'; | ||||
| import Setting from './models/Setting'; | ||||
| import shim from './shim'; | ||||
| import SyncTargetRegistry from './SyncTargetRegistry'; | ||||
| import { AnyAction, Dispatch } from 'redux'; | ||||
|  | ||||
| class Registry { | ||||
|  | ||||
| @@ -21,6 +22,7 @@ class Registry { | ||||
| 	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied | ||||
| 	private db_: any; | ||||
| 	private isOnMobileData_ = false; | ||||
| 	private dispatch_: Dispatch = (() => {}) as Dispatch; | ||||
|  | ||||
| 	public logger() { | ||||
| 		if (!this.logger_) { | ||||
| @@ -45,6 +47,14 @@ class Registry { | ||||
| 		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 | ||||
| 	// and the sync.mobileWifiOnly setting is true it will cancel the sync. | ||||
| 	public setIsOnMobileData(isOnMobileData: boolean) { | ||||
| @@ -139,10 +149,18 @@ class Registry { | ||||
| 					} | ||||
|  | ||||
| 					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.'); | ||||
| 						promiseResolve(); | ||||
| 						return; | ||||
| 					} | ||||
| 					this.dispatch({ | ||||
| 						type: 'MUST_AUTHENTICATE', | ||||
| 						value: false, | ||||
| 					}); | ||||
|  | ||||
| 					try { | ||||
| 						const sync = await this.syncTarget(syncTargetId).synchronizer(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user