-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Desktop: Replace the edit profile config menu option with a gui to manage profiles #13771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
1a03202
Add a manage profiles screen
mrjo118 507b420
Update comment
mrjo118 92d6a4a
Removed edit profile config option
mrjo118 2bc005e
Remove any types
mrjo118 19af232
Rerun pipeline
mrjo118 b3400c2
Merge branch 'dev' into add-manage-profiles-screen
mrjo118 16d3744
Use dispatch type
mrjo118 997f691
Change to a function component and use scss WIP with height issue
mrjo118 d2f7227
Fix issues
mrjo118 a6e1343
Merge branch 'add-manage-profiles-screen' of https://github.com/mrjo1…
mrjo118 2e80d4e
Merge remote-tracking branch 'origin/dev' into add-manage-profiles-sc…
mrjo118 0fd78e0
PR feedback
mrjo118 e7f95b3
Remove remnants of old naming
mrjo118 6168d99
Remove search bar
mrjo118 642733a
Rerun pipeline
mrjo118 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService'; | ||
| import { _ } from '@joplin/lib/locale'; | ||
|
|
||
| export const declaration: CommandDeclaration = { | ||
| name: 'showProfileEditor', | ||
| label: () => _('Manage profiles'), | ||
| }; | ||
|
|
||
| export const runtime = (): CommandRuntime => { | ||
| return { | ||
| execute: async (context: CommandContext) => { | ||
| context.dispatch({ | ||
| type: 'NAV_GO', | ||
| routeName: 'ProfileEditor', | ||
| }); | ||
| }, | ||
| enabledCondition: 'hasMultiProfiles', | ||
| }; | ||
| }; | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| .profile-management { | ||
| font-family: var(--joplin-font-family); | ||
| display: flex; | ||
| flex-direction: column; | ||
|
|
||
| > .tableContainer { | ||
| overflow-y: scroll; | ||
| padding: 20px; | ||
| box-sizing: border-box; | ||
| flex: 1 1 0%; | ||
| color: var(--joplin-color); | ||
|
|
||
| > .notification { | ||
| margin-bottom: 10px; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| .profile-table { | ||
| width: 100%; | ||
|
|
||
| > thead > tr > .headerCell { | ||
| white-space: nowrap; | ||
| font-weight: bold; | ||
| width: 1px; | ||
| } | ||
|
|
||
| > tbody > tr { | ||
| > .nameCell { | ||
| text-overflow: ellipsis; | ||
| overflow-x: hidden; | ||
| max-width: 1px; | ||
| width: 100%; | ||
| white-space: nowrap; | ||
| } | ||
|
|
||
| > .dataCell { | ||
| white-space: nowrap; | ||
| width: 1px; | ||
| color: var(--joplin-color-faded); | ||
| } | ||
|
|
||
| > .profileActions > button { | ||
| margin-right: 10px; | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,193 @@ | ||
| import * as React from 'react'; | ||
| import { useState, useEffect, CSSProperties } from 'react'; | ||
| import ButtonBar from './ConfigScreen/ButtonBar'; | ||
| import { _ } from '@joplin/lib/locale'; | ||
| import { connect } from 'react-redux'; | ||
| import { themeStyle } from '@joplin/lib/theme'; | ||
| import bridge from '../services/bridge'; | ||
| import dialogs from './dialogs'; | ||
| import { Profile, ProfileConfig } from '@joplin/lib/services/profileConfig/types'; | ||
| import { deleteProfileById, saveProfileConfig } from '@joplin/lib/services/profileConfig'; | ||
| import Setting from '@joplin/lib/models/Setting'; | ||
| import shim from '@joplin/lib/shim'; | ||
| import Logger from '@joplin/utils/Logger'; | ||
| import { AppState } from '../app.reducer'; | ||
| import { Dispatch } from 'redux'; | ||
|
|
||
| const logger = Logger.create('ProfileEditor'); | ||
|
|
||
| interface Props { | ||
| themeId: number; | ||
| dispatch: Dispatch; | ||
| style: CSSProperties; | ||
| profileConfig: ProfileConfig; | ||
| } | ||
|
|
||
| interface ProfileTableProps { | ||
| profiles: Profile[]; | ||
| currentProfileId: string; | ||
| onProfileRename: (profile: Profile)=> void; | ||
| onProfileDelete: (profile: Profile)=> void; | ||
| themeId: number; | ||
| } | ||
|
|
||
| const ProfileTableComp: React.FC<ProfileTableProps> = props => { | ||
| const theme = themeStyle(props.themeId); | ||
|
|
||
| return ( | ||
| <table className="profile-table"> | ||
| <thead> | ||
| <tr> | ||
| <th className="headerCell">{_('Profile name')}</th> | ||
| <th className="headerCell">{_('ID')}</th> | ||
| <th className="headerCell">{_('Status')}</th> | ||
| <th className="headerCell">{_('Actions')}</th> | ||
| </tr> | ||
| </thead> | ||
| <tbody> | ||
| {props.profiles.map((profile: Profile, index: number) => { | ||
| const isCurrentProfile = profile.id === props.currentProfileId; | ||
| return ( | ||
| <tr key={index}> | ||
| <td id={`name-${profile.id}`} className="nameCell"> | ||
| <span style={{ fontWeight: isCurrentProfile ? 'bold' : 'normal' }}> | ||
| {profile.name || `(${_('Untitled')})`} | ||
| </span> | ||
| </td> | ||
| <td className="dataCell">{profile.id}</td> | ||
| <td className="dataCell"> | ||
| {isCurrentProfile ? _('Active') : ''} | ||
| </td> | ||
| <td className="dataCell profileActions"> | ||
| <button | ||
| id={`rename-${profile.id}`} | ||
| aria-labelledby={`rename-${profile.id} name-${profile.id}`} | ||
| style={theme.buttonStyle} | ||
| onClick={() => props.onProfileRename(profile)} | ||
| > | ||
| {_('Rename')} | ||
| </button> | ||
| {!isCurrentProfile && ( | ||
| <button | ||
| id={`delete-${profile.id}`} | ||
| aria-labelledby={`delete-${profile.id} name-${profile.id}`} | ||
| style={theme.buttonStyle} | ||
| onClick={() => props.onProfileDelete(profile)} | ||
| > | ||
| {_('Delete')} | ||
| </button> | ||
| )} | ||
| </td> | ||
| </tr> | ||
| ); | ||
| })} | ||
| </tbody> | ||
| </table> | ||
| ); | ||
| }; | ||
|
|
||
| const ProfileEditorComponent: React.FC<Props> = props => { | ||
| const { profileConfig, themeId, dispatch } = props; | ||
| const theme = themeStyle(themeId); | ||
| const style = props.style; | ||
| const containerHeight = style.height; | ||
|
|
||
| const [profiles, setProfiles] = useState<Profile[]>(profileConfig.profiles); | ||
|
|
||
| useEffect(() => { | ||
| setProfiles(profileConfig.profiles); | ||
| }, [profileConfig]); | ||
|
|
||
| const saveNewProfileConfig = async (makeNewProfileConfig: ()=> ProfileConfig) => { | ||
| try { | ||
| const newProfileConfig = makeNewProfileConfig(); | ||
| await saveProfileConfig(`${Setting.value('rootProfileDir')}/profiles.json`, newProfileConfig); | ||
| dispatch({ | ||
| type: 'PROFILE_CONFIG_SET', | ||
| value: newProfileConfig, | ||
| }); | ||
| } catch (error) { | ||
| logger.error(error); | ||
| bridge().showErrorMessageBox(error.message); | ||
| } | ||
| }; | ||
|
|
||
| const onProfileRename = async (profile: Profile) => { | ||
| const newName = await dialogs.prompt(_('Profile name:'), '', profile.name); | ||
| if (newName === null || newName === undefined || newName === profile.name) return; | ||
|
|
||
| if (!newName.trim()) { | ||
| bridge().showErrorMessageBox(_('Profile name cannot be empty')); | ||
| return; | ||
| } | ||
|
|
||
| const makeNewProfileConfig = () => { | ||
| const newProfiles = profileConfig.profiles.map(p => | ||
| p.id === profile.id ? { ...p, name: newName.trim() } : p, | ||
| ); | ||
|
|
||
| const newProfileConfig = { | ||
| ...profileConfig, | ||
| profiles: newProfiles, | ||
| }; | ||
|
|
||
| return newProfileConfig; | ||
| }; | ||
|
|
||
| await saveNewProfileConfig(makeNewProfileConfig); | ||
| }; | ||
|
|
||
| const onProfileDelete = async (profile: Profile) => { | ||
| const isCurrentProfile = profile.id === profileConfig.currentProfileId; | ||
| if (isCurrentProfile) { | ||
| bridge().showErrorMessageBox(_('The active profile cannot be deleted. Switch to a different profile and try again.')); | ||
| return; | ||
| } | ||
|
|
||
| const ok = bridge().showConfirmMessageBox(_('Delete profile "%s"?\n\nAll data, including notes, notebooks and tags will be permanently deleted.', profile.name), { | ||
| buttons: [_('Delete'), _('Cancel')], | ||
| defaultId: 1, | ||
| }); | ||
| if (!ok) return; | ||
|
|
||
| const rootDir = Setting.value('rootProfileDir'); | ||
| const profileDir = `${rootDir}/profile-${profile.id}`; | ||
|
|
||
| try { | ||
| await shim.fsDriver().remove(profileDir); | ||
| logger.info('Deleted profile directory: ', profileDir); | ||
| } catch (error) { | ||
| logger.error('Error deleting profile directory: ', error); | ||
| bridge().showErrorMessageBox(error.message); | ||
| } | ||
|
|
||
| await saveNewProfileConfig(() => deleteProfileById(profileConfig, profile.id)); | ||
| }; | ||
|
|
||
| return ( | ||
| <div className="profile-management" style={{ ...theme.containerStyle, height: containerHeight }}> | ||
| <div className="tableContainer"> | ||
| <div className="notification" style={theme.notificationBox}> | ||
| {_('Manage your profiles. You can rename or delete profiles. The active profile cannot be deleted.')} | ||
| </div> | ||
| <ProfileTableComp | ||
| themeId={themeId} | ||
| profiles={profiles} | ||
| currentProfileId={profileConfig.currentProfileId} | ||
| onProfileRename={onProfileRename} | ||
| onProfileDelete={onProfileDelete} | ||
| /> | ||
| </div> | ||
| <ButtonBar | ||
| onCancelClick={() => dispatch({ type: 'NAV_BACK' })} | ||
| /> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| const mapStateToProps = (state: AppState) => ({ | ||
| themeId: state.settings.theme, | ||
| profileConfig: state.profileConfig, | ||
| }); | ||
|
|
||
| export default connect(mapStateToProps)(ProfileEditorComponent); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be called
ProfileEditorlike on mobileThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done