Skip to content

Commit 3d82ffe

Browse files
authored
Merge pull request #177 from entrylabs/develop
2.1.1 버전업
2 parents 4cc0b7a + 7e4fce4 commit 3d82ffe

File tree

17 files changed

+198
-165
lines changed

17 files changed

+198
-165
lines changed

build/entryx64.nsi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
!define MUI_UNICON "icon.ico"
1414
!define PRODUCT_NAME "Entry"
1515
!define APP_NAME "Entry.exe"
16-
!define PRODUCT_VERSION "2.1.0"
16+
!define PRODUCT_VERSION "2.1.1"
1717
!define PRODUCT_PUBLISHER "EntryLabs"
1818
!define PRODUCT_WEB_SITE "http://www.playentry.org/"
1919

build/entryx86.nsi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
!define MUI_UNICON "icon.ico"
1414
!define PRODUCT_NAME "Entry"
1515
!define APP_NAME "Entry.exe"
16-
!define PRODUCT_VERSION "2.1.0"
16+
!define PRODUCT_VERSION "2.1.1"
1717
!define PRODUCT_PUBLISHER "EntryLabs"
1818
!define PRODUCT_WEB_SITE "http://www.playentry.org/"
1919

package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"private": true,
33
"productName": "Entry",
44
"name": "entry",
5-
"version": "2.1.0",
5+
"version": "2.1.1",
66
"description": "Entry for offline",
77
"main": "src/main_build/main.bundle.js",
88
"scripts": {
@@ -35,10 +35,9 @@
3535
"@entrylabs/modal": "^1.2.7",
3636
"async-csv": "^2.1.3",
3737
"axios": "^0.19.2",
38-
"babel-loader": "^9.1.0",
3938
"cross-spawn": "^7.0.3",
40-
"entry-hw": "git+https://github.com/entrylabs/entry-hw.git#dist/v1.9.37",
41-
"entry-js": "git+https://github.com/entrylabs/entryjs.git#dist/offline_v2.0.54",
39+
"entry-hw": "git+https://github.com/entrylabs/entry-hw.git#dist/v1.9.38",
40+
"entry-js": "git+https://github.com/entrylabs/entryjs.git#dist/offline_v2.1.1",
4241
"entry-tool": "git+https://github.com/entrylabs/entry-tool.git#dist/20221024",
4342
"excel4node": "^1.7.0",
4443
"fs-extra": "^8.1.0",

src/main/ipcMainHelper.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Constants from './constants';
66
import CommonUtils from './commonUtils';
77
import checkUpdateRequest from './utils/network/checkUpdate';
88
import createLogger from './utils/functions/createLogger';
9+
import isValidAsarFile from './utils/functions/isValidAsarFile';
910
require('@electron/remote/main').initialize();
1011

1112
const logger = createLogger('main/ipcMainHelper.ts');
@@ -29,6 +30,7 @@ new (class {
2930
ipcMain.handle('importPictures', this.importPictures.bind(this));
3031
ipcMain.handle('importPicturesFromResource', this.importPicturesFromResource.bind(this));
3132
ipcMain.handle('importPictureFromCanvas', this.importPictureFromCanvas.bind(this));
33+
ipcMain.handle('captureBlockImage', this.captureBlockImage.bind(this));
3234
ipcMain.handle('importSounds', this.importSounds.bind(this));
3335
ipcMain.handle('importSoundsFromResource', this.importSoundsFromResource.bind(this));
3436
ipcMain.handle('createTableInfo', this.createTables.bind(this));
@@ -42,6 +44,7 @@ new (class {
4244
ipcMain.handle('quit', this.quitApplication.bind(this));
4345
ipcMain.handle('checkPermission', this.checkPermission.bind(this));
4446
ipcMain.handle('getOpenSourceText', () => ''); // 별다른 표기 필요없음
47+
ipcMain.handle('isValidAsarFile', this.checkIsValidAsarFile.bind(this));
4548
}
4649

4750
async saveProject(event: IpcMainInvokeEvent, project: ObjectLike, targetPath: string) {
@@ -106,6 +109,10 @@ new (class {
106109
return await MainUtils.importPictureFromCanvas(data);
107110
}
108111

112+
async captureBlockImage(event: IpcMainInvokeEvent, images: any, filePath: string) {
113+
return await MainUtils.captureBlockImage(images, filePath);
114+
}
115+
109116
async importSounds(event: IpcMainInvokeEvent, filePaths: string[]) {
110117
logger.verbose(`importSounds called ${filePaths}`);
111118
if (!filePaths || filePaths.length === 0) {
@@ -174,7 +181,7 @@ new (class {
174181
// 기본 이미지 및 사운드인 경우 상대경로이므로 기준 위치 수정
175182
if (typedPath.startsWith('renderer')) {
176183
typedPath = path.resolve(app.getAppPath(), 'src', typedPath);
177-
}else if(typedPath.startsWith('../../..')){
184+
} else if (typedPath.startsWith('../../..')) {
178185
typedPath = typedPath.replace('../../../', '');
179186
typedPath = path.resolve(app.getAppPath(), typedPath);
180187
}
@@ -225,6 +232,17 @@ new (class {
225232
}
226233
}
227234

235+
async checkIsValidAsarFile(event: IpcMainInvokeEvent) {
236+
try {
237+
const result = await isValidAsarFile();
238+
console.log('isValidAsarFile', result);
239+
return result;
240+
} catch (e) {
241+
console.log(e);
242+
return false;
243+
}
244+
}
245+
228246
async checkUpdate() {
229247
const data = await checkUpdateRequest();
230248
return [global.sharedObject.version, data];

src/main/main.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import AboutWindowManager from './views/aboutWindowManager';
55
import parseCommandLine from './utils/functions/parseCommandLine';
66
import configInitialize from './utils/functions/configInitialize';
77
import createLogger from './utils/functions/createLogger';
8-
import isValidAsarFile from './utils/functions/isValidAsarFile';
98

109
import('./ipcMainHelper');
1110
import('./utils/functions/globalShortCutRegister');
@@ -82,19 +81,6 @@ if (!app.requestSingleInstanceLock()) {
8281
ipcMain.on('closeAboutWindow', function() {
8382
aboutWindow.closeAboutWindow();
8483
});
85-
86-
setTimeout(async () => {
87-
try {
88-
const result = await isValidAsarFile();
89-
console.log('isValidAsarFile', result);
90-
if (!result) {
91-
mainWindow?.window?.webContents.send('invalidAsarFile');
92-
}
93-
} catch (e) {
94-
console.log(e);
95-
mainWindow?.window?.webContents.send('invalidAsarFile');
96-
}
97-
}, 2000);
9884
});
9985
}
10086

src/main/mainUtils.ts

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { app, ipcMain, WebContents } from 'electron';
1+
import { app, ipcMain, WebContents, BrowserWindow } from 'electron';
22
import fs from 'fs';
33
import path from 'path';
44
import xl from 'excel4node';
@@ -171,10 +171,10 @@ export default class MainUtils {
171171
});
172172

173173
Promise.all(copyObjectPromise)
174-
.then(function() {
174+
.then(function () {
175175
resolve('success');
176176
})
177-
.catch(function(err) {
177+
.catch(function (err) {
178178
reject(err);
179179
});
180180
} catch (e) {
@@ -545,6 +545,66 @@ export default class MainUtils {
545545
);
546546
}
547547

548+
/**
549+
* 코드블럭을 이미지로 저장한다. svg파일을 그리기위한 임시 브라우저윈도우를 띄우고 캡쳐해서 저장한다.
550+
* @param image 이미지의 가로세로크기(width, height)와 svg데이터(data)를 string값으로 가지고 있다.
551+
* @param filePath 캡쳐한 이미지를 저장할 경로
552+
*/
553+
static async captureBlockImage(images: any, filePath: string) {
554+
const FREE_SPACE = 25;
555+
const WAITING_TIME = 50;
556+
557+
try {
558+
images.forEach((image: any, index: number) => {
559+
const { width, height, data } = image;
560+
561+
// 캡쳐용 임시 브라우저 captureWindow 생성
562+
// 임시 브라우저이므로 별도 클래스로 관리하지 않음
563+
const remoteMain = require('@electron/remote/main');
564+
const captureWindow = new BrowserWindow({
565+
width: Math.ceil(width) + FREE_SPACE,
566+
height: Math.ceil(height) + FREE_SPACE,
567+
useContentSize: true,
568+
center: true,
569+
webPreferences: {
570+
nodeIntegration: true,
571+
contextIsolation: false,
572+
preload: path.resolve(
573+
app.getAppPath(),
574+
'src',
575+
'preload_build',
576+
'preload.bundle.js'
577+
)
578+
},
579+
})
580+
const windowId = captureWindow.id;
581+
remoteMain.enable(captureWindow.webContents);
582+
captureWindow.loadURL(
583+
`file:///${path.resolve(app.getAppPath(), 'src', 'main', 'views', 'capture.html')}`
584+
)
585+
586+
// captureWindow의 송수신 및 라이프사이클 관련 함수
587+
ipcMain.handle(`getImageString_${windowId}`, () => {
588+
return image;
589+
})
590+
ipcMain.on(`captureAndSave_${windowId}`, async () => {
591+
// 렌더러에서 svg를 그리는데 다소 시간이 걸리므로 대기 후 실행
592+
await setTimeout(async () => {
593+
const capturedImage = await captureWindow.webContents.capturePage({ x: 0, y: 0, width, height });
594+
await FileUtils.writeFile(capturedImage.toPNG(), `${filePath}${index}${'.png'}`);
595+
captureWindow.close();
596+
}, WAITING_TIME);
597+
})
598+
captureWindow.addListener('closed', () => {
599+
ipcMain.removeAllListeners(`captureAndSave_${windowId}`);
600+
ipcMain.removeHandler(`getImageString_${windowId}`);
601+
})
602+
})
603+
} catch (error) {
604+
console.error(error);
605+
}
606+
}
607+
548608
static importPictureFromCanvas(data: ObjectLike) {
549609
return new Promise(async (resolve, reject) => {
550610
const { file, image } = data;

src/main/views/capture.html

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<title>Capture</title>
7+
</head>
8+
9+
<body>
10+
<div class="container">
11+
12+
</div>
13+
14+
<script>
15+
const remote = require('@electron/remote');
16+
const windowId = remote.getCurrentWindow().id;
17+
const { ipcInvoke, ipcSend } = window;
18+
19+
ipcInvoke(`getImageString_${windowId}`).then((result) => {
20+
let svgString = result.data;
21+
const width = result.width;
22+
const height = result.height;
23+
24+
// image 태그 href 주소 수정
25+
svgString = svgString.replaceAll("file://../../..", window.getAppPathWithParams());
26+
27+
const containerDivDom = document.querySelector('.container');
28+
const blockImageDoc = new DOMParser().parseFromString(svgString, 'image/svg+xml').querySelector('svg');
29+
blockImageDoc.setAttribute('width', width);
30+
blockImageDoc.setAttribute('height', height);
31+
containerDivDom.append(blockImageDoc);
32+
33+
ipcSend(`captureAndSave_${windowId}`);
34+
});
35+
</script>
36+
</body>
37+
38+
</html>

src/preload/preload.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ ipcRenderer.on(
2222
const { x, y, width, height } = dimension || {};
2323
const imageElement = width && height ? new Image(width, height) : new Image();
2424

25-
imageElement.onload = function() {
25+
imageElement.onload = function () {
2626
canvas.width = imageElement.width;
2727
canvas.height = imageElement.height;
2828

@@ -64,6 +64,10 @@ window.ipcInvoke = (channel: string, ...args: any[]) => {
6464
return ipcRenderer.invoke(channel, ...args);
6565
};
6666

67+
window.ipcSend = (channel: string, ...args: any[]) => {
68+
ipcRenderer.send(channel, ...args);
69+
}
70+
6771
window.openEntryWebPage = () => {
6872
shell.openExternal('https://playentry.org/download/offline');
6973
};
@@ -79,6 +83,11 @@ window.weightsPath = () => {
7983
: path.resolve(remote.app.getAppPath(), 'node_modules', 'entry-js', 'weights');
8084
};
8185

86+
window.getAppPathWithParams = (...params: string[]) => {
87+
console.log(process.env.NODE_ENV);
88+
return path.resolve(remote.app.getAppPath(), ...params);
89+
}
90+
8291
/**
8392
* external file => loadProjectFromMain event => loadProject => callback(project)
8493
*/

src/renderer/components/workspace.tsx

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import _includes from 'lodash/includes';
1010
import _debounce from 'lodash/debounce';
1111
import entryPatch from '../helper/entry/entryPatcher';
1212
import { ModalProgress } from '@entrylabs/tool/component';
13+
import Constants from '../helper/constants';
1314
import ModalHelper from '../helper/entry/entryModalHelper';
1415
import RendererUtils from '../helper/rendererUtils';
1516
import IpcRendererHelper from '../helper/ipcRendererHelper';
@@ -21,8 +22,9 @@ import { IModalState, ModalActionCreators } from '../store/modules/modal';
2122
import { IMapDispatchToProps, IMapStateToProps } from '../store';
2223
import DragAndDropContainer from './DragAndDropContainer';
2324
import EntryModalHelper from '../helper/entry/entryModalHelper';
25+
import ipcRendererHelper from '../helper/ipcRendererHelper';
2426

25-
interface IProps extends IReduxDispatch, IReduxState {}
27+
interface IProps extends IReduxDispatch, IReduxState { }
2628

2729
class Workspace extends Component<IProps> {
2830
private lastHwName?: string;
@@ -159,6 +161,9 @@ class Workspace extends Component<IProps> {
159161
addEventListener('saveCanvasImage', (data: any) => {
160162
this.handleCanvasImageSave(data);
161163
});
164+
addEventListener('saveBlockImages', (data: any) => {
165+
this.handleBlockImageSave(data);
166+
});
162167
// exportObject
163168
addEventListener('exportObject', EntryUtils.exportObject);
164169
// 리스트 Import
@@ -225,6 +230,26 @@ class Workspace extends Component<IProps> {
225230
}
226231
}
227232

233+
async handleBlockImageSave(data: any) {
234+
const images = data.images;
235+
try {
236+
RendererUtils.showOpenDialog({
237+
properties: ['openDirectory'],
238+
filters: [{ name: 'Image', extensions: ['png'] }],
239+
}).then(async ({ filePaths }) => {
240+
const dirPath = filePaths[0];
241+
if (!dirPath) {
242+
throw 'invalid filePaths';
243+
}
244+
console.log(Constants.sep);
245+
const savePath = `${dirPath}${Constants.sep}`;
246+
await ipcRendererHelper.captureBlockImage(images, savePath);
247+
})
248+
} catch (err) {
249+
console.error(err);
250+
}
251+
}
252+
228253
handleChangeWorkspaceMode() {
229254
const workspace = Entry.getMainWS();
230255
const { mode } = workspace;
@@ -301,21 +326,21 @@ class Workspace extends Component<IProps> {
301326
if (hw.programConnected && hw.hwModule) {
302327
const hwName = hw.hwModule.name;
303328
if (_includes(EntryStatic.hwMiniSupportList, hwName)) {
304-
hwCategoryList.forEach(function(categoryName: string) {
329+
hwCategoryList.forEach(function (categoryName: string) {
305330
blockMenu.unbanCategory(categoryName);
306331
});
307332
blockMenu.banCategory('arduino');
308333
blockMenu.banCategory('hw_robot');
309334
} else {
310-
hwCategoryList.forEach(function(categoryName: string) {
335+
hwCategoryList.forEach(function (categoryName: string) {
311336
blockMenu.banCategory(categoryName);
312337
});
313338
blockMenu.banCategory('hw_robot');
314339
blockMenu.unbanCategory('arduino');
315340
}
316341
this.lastHwName = hwName;
317342
} else {
318-
hwCategoryList.forEach(function(categoryName: string) {
343+
hwCategoryList.forEach(function (categoryName: string) {
319344
blockMenu.banCategory(categoryName);
320345
});
321346
blockMenu.banCategory('arduino');

0 commit comments

Comments
 (0)