Skip to content

Commit 9b32cec

Browse files
authored
Add wsl left panel (#160)
* ADDED: WSL Linux distribution list on left panel (win only) * FIXED: infinite loop in favoriteState * FIXED: leftpanel selection on Linux distributions fixes #149 * FIXED: isRoot special case for WSL paths fixes #151 * FIXED: incorrect error message when renaming, fixes #156 * ADDED: new FsWsl filesystem for WSL on Windows * ADDED: new util to decode WSL files with special chars * FIXED: circular dep Fs <-> FsLocal, fixes tests
1 parent 6f4f086 commit 9b32cec

File tree

12 files changed

+13497
-13995
lines changed

12 files changed

+13497
-13995
lines changed

package-lock.json

Lines changed: 13188 additions & 13953 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/LeftPanel.tsx

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ import classNames from "classnames";
66
import { IReactionDisposer, reaction, toJS } from "mobx";
77
import i18next from 'i18next';
88
import { USERNAME, isMac } from "../utils/platform";
9+
import { hasWSL } from "../utils/wsl";
910
import Icons from "../constants/icons";
1011
import { FavoritesState, Favorite } from "../state/favoritesState";
1112
import { AppState } from "../state/appState";
1213
import { AppAlert } from "./AppAlert";
1314
import CONFIG from '../config/appConfig';
1415

16+
declare var ENV: any;
17+
1518
require("../css/favoritesPanel.css");
1619

1720
interface LeftPanelState {
@@ -32,10 +35,13 @@ interface InjectedProps extends IProps {
3235
export class LeftPanelClass extends React.Component<IProps, LeftPanelState> {
3336
favoritesState: FavoritesState;
3437
disposers:Array<IReactionDisposer> = new Array();
38+
// we have to make an async call to check for WSL
39+
// so we first set it to false
40+
showDistributions: boolean = false;
3541

3642
constructor(props:IProps) {
3743
super(props);
38-
44+
3945
const { t } = props;
4046

4147
this.state = {
@@ -60,8 +66,32 @@ export class LeftPanelClass extends React.Component<IProps, LeftPanelState> {
6066

6167
this.favoritesState = this.injected.appState.favoritesState;
6268

63-
this.installReaction();
69+
this.installReactions();
6470
this.bindLanguageChange();
71+
if (!ENV.CY) {
72+
this.checkForWSL();
73+
}
74+
}
75+
76+
private checkForWSL = async (): Promise<boolean> => {
77+
console.log('checking for WSL');
78+
this.showDistributions = await hasWSL();
79+
if (this.showDistributions) {
80+
console.log('WSL detected');
81+
const { t } = this.props;
82+
const { nodes } = this.state;
83+
84+
nodes.push({
85+
id: 2,
86+
hasCaret: true,
87+
isExpanded: true,
88+
label: t('FAVORITES_PANEL.LINUX'),
89+
childNodes: []
90+
});
91+
92+
this.setState({ nodes });
93+
}
94+
return this.showDistributions;
6595
}
6696

6797
private bindLanguageChange = () => {
@@ -76,7 +106,7 @@ export class LeftPanelClass extends React.Component<IProps, LeftPanelState> {
76106
public onLanguageChanged = (lang: string) => {
77107
console.log('building nodes', lang);
78108
this.buildNodes(this.favoritesState);
79-
}
109+
}
80110

81111
private get injected() {
82112
return this.props as InjectedProps;
@@ -87,7 +117,7 @@ export class LeftPanelClass extends React.Component<IProps, LeftPanelState> {
87117
this.unbindLanguageChange();
88118
}
89119

90-
private installReaction() {
120+
private installReactions() {
91121
this.disposers.push(reaction(
92122
() => toJS(this.favoritesState.places),
93123
(_: Favorite[]) => {
@@ -97,6 +127,16 @@ export class LeftPanelClass extends React.Component<IProps, LeftPanelState> {
97127
}
98128
})
99129
);
130+
131+
this.disposers.push(reaction(
132+
() => toJS(this.favoritesState.distributions),
133+
(_: Favorite[]) => {
134+
if (!this.props.hide) {
135+
console.log('distributions updated: need to rebuild nodes');
136+
this.buildNodes(this.favoritesState);
137+
}
138+
})
139+
);
100140
}
101141

102142
/**
@@ -108,14 +148,16 @@ export class LeftPanelClass extends React.Component<IProps, LeftPanelState> {
108148
getNodeFromPath(path:string):ITreeNode<string> {
109149
const { nodes } = this.state;
110150
const shortcuts = nodes[0].childNodes;
151+
const places = nodes[1].childNodes;
111152

112-
const found = shortcuts.find(node => node.nodeData === path);
153+
const found = shortcuts.find(node => node.nodeData === path) || places.find(node => node.nodeData === path);
113154

114-
if (found) {
155+
if (found || !this.showDistributions) {
115156
return found;
116157
} else {
117-
const places = nodes[1].childNodes;
118-
return places.find(node => node.nodeData === path);
158+
159+
const distribs = nodes[2].childNodes;
160+
return distribs.find(node => node.nodeData === path);
119161
}
120162
}
121163

@@ -148,7 +190,7 @@ export class LeftPanelClass extends React.Component<IProps, LeftPanelState> {
148190
if (sameView) {
149191
const activeCache = appState.getActiveCache();
150192
if (activeCache && activeCache.status === 'ok') {
151-
activeCache.cd(path)
193+
activeCache.cd(path);
152194
}
153195
} else {
154196
const winState = appState.winStates[0];
@@ -186,6 +228,7 @@ export class LeftPanelClass extends React.Component<IProps, LeftPanelState> {
186228
const { nodes } = this.state;
187229
const shortcuts = nodes[0];
188230
const places = nodes[1];
231+
const distributions = nodes[2];
189232

190233
shortcuts.childNodes = favorites.shortcuts.map((shortcut, i) => ({
191234
id: `s_${shortcut.path}`,
@@ -205,15 +248,27 @@ export class LeftPanelClass extends React.Component<IProps, LeftPanelState> {
205248
nodeData: place.path
206249
}));
207250

251+
if (this.showDistributions && favorites.distributions) {
252+
distributions.childNodes = favorites.distributions.map((distrib) => ({
253+
id: `p_${distrib.path}`,
254+
key: `p_${distrib.path}`,
255+
label: <span title={distrib.path}>{distrib.label}</span>,
256+
icon: distrib.icon,
257+
nodeData: distrib.path
258+
}));
259+
}
260+
208261
// update root nodes label too
209262
places.label = t('FAVORITES_PANEL.PLACES');
210263
shortcuts.label = t('FAVORITES_PANEL.SHORTCUTS');
264+
if (distributions) {
265+
distributions.label = t('FAVORITES_PANEL.LINUX');
266+
}
211267

212268
this.setState(this.state);
213269
}
214270

215271
render() {
216-
console.log('LeftPanel.render');
217272
const path = this.getActiveCachePath();
218273
this.setActiveNode(path);
219274
const { nodes } = this.state;

src/electron/main.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,15 @@ const ElectronApp = {
8080

8181
settings.manage(this.mainWindow);
8282

83-
if (!settings.getCustom()) {
84-
settings.setCustom({
83+
if (!settings.custom) {
84+
settings.custom = {
8585
splitView: false
86-
});
86+
};
8787
}
8888

89-
console.log(settings.getCustom());
89+
console.log(settings.custom);
9090

91-
this.mainWindow.initialSettings = settings.getCustom();
91+
this.mainWindow.initialSettings = settings.custom;
9292

9393
// this.mainWindow.loadURL(HTML_PATH);
9494
this.mainWindow.loadFile(HTML_PATH);
@@ -202,7 +202,7 @@ const ElectronApp = {
202202
const { id, settings } = data;
203203
const state = WindowSettings.getSettings(id);
204204
console.log('got state', state);
205-
state.setCustom(settings);
205+
state.custom = settings;
206206
});
207207
},
208208

src/gui/index.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,35 @@ import { Provider } from "mobx-react";
99
import { DndProvider } from "react-dnd";
1010
import HTML5Backend from "react-dnd-html5-backend";
1111
import { remote } from "electron";
12+
// register Fs that will be available in React-Explorer
13+
// I guess there is a better place to do that
14+
import { FsGeneric } from '../services/plugins/FsGeneric';
15+
import { FsWsl } from '../services/plugins/FsWsl';
16+
import { FsLocal } from '../services/plugins/FsLocal';
17+
import { registerFs } from '../services/Fs';
1218

1319
declare var ENV: any;
1420

21+
function initFS() {
22+
if ((process && process.env && process.env.NODE_ENV === 'test') || ENV.CY) {
23+
// console.log('**register generic', FsGeneric);
24+
registerFs(FsGeneric);
25+
} else {
26+
registerFs(FsWsl);
27+
registerFs(FsLocal);
28+
}
29+
}
30+
1531
class App {
1632
settingsState: SettingsState;
1733

1834
constructor() {
1935
this.settingsState = new SettingsState(ENV.VERSION);
2036
if (ENV.NODE_ENV !== "production") {
21-
this.createTestFolder().then(this.renderApp);
37+
this.createTestFolder()
38+
.then(this.init);
2239
} else {
23-
this.renderApp();
40+
this.init();
2441
}
2542
}
2643

@@ -44,6 +61,11 @@ class App {
4461
return window && window.initialSettings || {};
4562
}
4663

64+
init = () => {
65+
initFS();
66+
this.renderApp();
67+
}
68+
4769
renderApp = () => {
4870
const initialSettings = this.getInitialSettings();
4971
document.body.classList.add("loaded");

src/locale/lang/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@
190190
"UNKNOWN": "An error has occured",
191191
"GENERIC": "{{error.message}} (code {{error.code}})",
192192
"BAD_FILENAME": "File or directory name not valid: '{{entry}}'",
193-
"WIN_VALID_FILENAME": "All characters except *:<>\\?| can be used",
193+
"WIN_VALID_FILENAME": "All characters except *:<>\\?|\" can be used",
194194
"UNIX_VALID_FILENAME": "All characters except / can be used",
195195
"DELETE": "Error deleting files: {{message}}",
196196
"DELETE_WARN": "Some files could not be deleted",
@@ -218,6 +218,7 @@
218218
"FAVORITES_PANEL": {
219219
"SHORTCUTS": "Shortcuts",
220220
"PLACES": "Places",
221+
"LINUX": "Linux",
221222
"DOWNLOADS_DIR": "Downloads",
222223
"MUSIC_DIR": "Music",
223224
"PICTURES_DIR": "Images",

src/locale/lang/fr.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@
190190
"UNKNOWN": "Une erreur est survenue",
191191
"GENERIC": "{{error.message}} (code {{error.code}})",
192192
"BAD_FILENAME": "Nom de fichier ou dossier non valide: '{{entry}}'",
193-
"WIN_VALID_FILENAME": "Tous les caractères sauf *:<>\\?| sont acceptés",
193+
"WIN_VALID_FILENAME": "Tous les caractères sauf *:<>\\?|\" sont acceptés",
194194
"UNIX_VALID_FILENAME": "Tous les caractères sauf / sont acceptés",
195195
"DELETE": "Erreur lors de la suppression des fichiers: {{message}}",
196196
"DELETE_WARN": "Certains fichiers n'ont pu être supprimés",
@@ -218,6 +218,7 @@
218218
"FAVORITES_PANEL": {
219219
"SHORTCUTS": "Raccourcis",
220220
"PLACES": "Emplacements",
221+
"LINUX": "Linux",
221222
"DOWNLOADS_DIR": "Téléchargements",
222223
"MUSIC_DIR": "Musique",
223224
"PICTURES_DIR": "Images",

src/services/Fs.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { FsGeneric } from './plugins/FsGeneric';
2-
import { FsLocal } from './plugins/FsLocal';
31
import { Readable } from 'stream';
42
import { isWin } from '../utils/platform';
53

@@ -18,13 +16,6 @@ export function registerFs(fs: Fs): void {
1816
interfaces.push(fs);
1917
};
2018

21-
if ((process && process.env && process.env.NODE_ENV === 'test') || ENV.CY) {
22-
// console.log('**register generic', FsGeneric);
23-
registerFs(FsGeneric);
24-
} else {
25-
registerFs(FsLocal);
26-
}
27-
2819
declare var ENV: any;
2920

3021
export interface FileID {
@@ -152,7 +143,7 @@ export interface FsApi {
152143

153144
export function getFS(path: string): Fs {
154145
let newfs = interfaces.find((filesystem) => filesystem.canread(path));
155-
146+
console.log('got FS', newfs);
156147
// if (!newfs) {
157148
// newfs = FsGeneric;
158149
// }

src/services/plugins/FsLocal.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,22 @@ export class LocalApi implements FsApi {
8585

8686
async makedir(source: string, dirName: string, transferId = -1): Promise<string> {
8787
return new Promise((resolve, reject) => {
88+
console.log('makedir, source:', source, 'dirName:', dirName);
8889
const unixPath = path.join(source, dirName).replace(/\\/g, "/");
90+
console.log('unixPath', unixPath);
8991
try {
92+
console.log('calling mkdir');
9093
mkdir(unixPath, (err: any) => {
9194
if (err) {
95+
console.log('error creating dir', err);
9296
reject(err);
9397
} else {
98+
console.log('successfully created dir', err);
9499
resolve(path.join(source, dirName));
95100
}
96101
});
97102
} catch (err) {
103+
console.log('error execing mkdir()', err);
98104
reject(err);
99105
}
100106
});

0 commit comments

Comments
 (0)