This commit is contained in:
Wayne Rocha 2023-05-07 01:48:01 -03:00
commit 83006cd37d
7 changed files with 147 additions and 8 deletions

View file

@ -1,6 +1,6 @@
{
"name": "ResponsivelyApp",
"version": "1.1.0",
"version": "1.2.0",
"description": "A developer-friendly browser for developing responsive web apps",
"license": "MIT",
"author": {

View file

@ -0,0 +1,7 @@
export function updateWebViewHeightAndScale(
webView: HTMLElement | Electron.WebviewTag,
pageHeight: number
) {
webView.style.height = `${pageHeight}px`;
webView.style.transform = `scale(0.1)`;
}

View file

@ -1,8 +1,9 @@
/* eslint-disable promise/always-return */
import { Device } from 'common/deviceList';
import { ipcMain, shell, webContents } from 'electron';
import { writeFile, ensureDir } from 'fs-extra';
import path from 'path';
import os from 'os';
import { homedir } from 'os';
export interface ScreenshotArgs {
webContentsId: number;
@ -10,17 +11,35 @@ export interface ScreenshotArgs {
device: Device;
}
export interface ScreenshotAllArgs {
webContentsId: number;
device: Device;
previousHeight: string;
previousTransform: string;
pageHeight: number;
}
export interface ScreenshotResult {
done: boolean;
}
const captureImage = async (
webContentsId: number
): Promise<Electron.NativeImage> => {
const Image = await webContents.fromId(webContentsId).capturePage();
return Image;
};
const quickScreenshot = async (
arg: ScreenshotArgs
): Promise<ScreenshotResult> => {
const { webContentsId } = arg;
const image = await webContents.fromId(webContentsId).capturePage();
const dir = path.join(os.homedir(), `Desktop/Responsively-Screenshots`);
const filePath = path.join(dir, `/screenshot-${Date.now()}.jpeg`);
const {
webContentsId,
device: { name },
} = arg;
const image = await captureImage(webContentsId);
const dir = path.join(homedir(), `Desktop/Responsively-Screenshots`);
const filePath = path.join(dir, `/${name}-${Date.now()}.jpeg`);
await ensureDir(dir);
await writeFile(filePath, image.toJPEG(100));
setTimeout(() => shell.showItemInFolder(filePath), 100);
@ -28,6 +47,19 @@ const quickScreenshot = async (
return { done: true };
};
const captureAllDecies = async (
args: Array<ScreenshotAllArgs>
): Promise<ScreenshotResult> => {
const screenShots = args.map((arg) => {
const { device, webContentsId } = arg;
const screenShotArg: ScreenshotArgs = { device, webContentsId };
return quickScreenshot(screenShotArg);
});
await Promise.all(screenShots);
return { done: true };
};
export const initScreenshotHandlers = () => {
ipcMain.handle(
'screenshot',
@ -35,4 +67,11 @@ export const initScreenshotHandlers = () => {
return quickScreenshot(arg);
}
);
ipcMain.handle(
'screenshot:All',
async (event, args: Array<ScreenshotAllArgs>) => {
return captureAllDecies(args);
}
);
};

View file

@ -0,0 +1,19 @@
import Modal from '../Modal';
interface Props {
isOpen: boolean;
onClose: () => void;
title: JSX.Element | string;
}
const ModalLoader = ({ isOpen, onClose, title }: Props) => {
return (
<Modal isOpen={isOpen} onClose={onClose} title={title}>
<div className="flex flex-col items-center justify-center gap-4">
Capturing screen...
</div>
</Modal>
);
};
export default ModalLoader;

View file

@ -89,8 +89,7 @@ const Toolbar = ({
const previousHeight = webviewTag.style.height;
const previousTransform = webviewTag.style.transform;
webviewTag.style.height = `${pageHeight}px`;
webviewTag.style.transform = `scale(0.1)`;
updateWebViewHeightAndScale(webviewTag, pageHeight);
await new Promise((resolve) => setTimeout(resolve, 1000));

View file

@ -1,16 +1,24 @@
import { useDispatch, useSelector } from 'react-redux';
import {
selectIsCapturingScreenshot,
selectIsInspecting,
selectRotate,
setIsCapturingScreenshot,
setIsInspecting,
setRotate,
} from 'renderer/store/features/renderer';
import { Icon } from '@iconify/react';
import { ScreenshotAllArgs } from 'main/screenshot';
import { selectActiveSuite } from 'renderer/store/features/device-manager';
import WebPage from 'main/screenshot/webpage';
import { getDevicesMap } from 'common/deviceList';
import { updateWebViewHeightAndScale } from 'common/webViewUtils';
import NavigationControls from './NavigationControls';
import Menu from './Menu';
import Button from '../Button';
import AddressBar from './AddressBar';
import ColorSchemeToggle from './ColorSchemeToggle';
import ModalLoader from '../ModalLoader';
import { PreviewSuiteSelector } from './PreviewSuiteSelector';
const Divider = () => <div className="h-6 w-px bg-gray-300 dark:bg-gray-700" />;
@ -18,8 +26,55 @@ const Divider = () => <div className="h-6 w-px bg-gray-300 dark:bg-gray-700" />;
const ToolBar = () => {
const rotateDevices = useSelector(selectRotate);
const isInspecting = useSelector(selectIsInspecting);
const isCapturingScreenshot = useSelector(selectIsCapturingScreenshot);
const activeSuite = useSelector(selectActiveSuite);
const dispatch = useDispatch();
const screenshotCaptureHandler = async () => {
dispatch(setIsCapturingScreenshot(true));
const webViews: NodeListOf<Electron.WebviewTag> =
document.querySelectorAll('webView');
const screens: Array<ScreenshotAllArgs> = [];
const devices = activeSuite.devices.map((d) => getDevicesMap()[d]);
webViews.forEach(async (webview) => {
const device = devices.find((d) => d.name === webview.id);
const webPage = new WebPage(webview as unknown as Electron.WebContents);
const pageHeight = await webPage.getPageHeight();
const previousHeight = webview.style.height;
const previousTransform = webview.style.transform;
updateWebViewHeightAndScale(webview, pageHeight);
if (device != null) {
screens.push({
webContentsId: webview.getWebContentsId(),
device,
previousHeight,
previousTransform,
pageHeight,
});
}
});
await new Promise((resolve) => setTimeout(resolve, 1000));
await window.electron.ipcRenderer.invoke<Array<ScreenshotAllArgs>, any>(
'screenshot:All',
screens
);
// reset webviews to original size
webViews.forEach((webview) => {
const screent = screens.find((s) => s.device.name === webview.id);
if (screent != null) {
webview.style.height = screent.previousHeight;
webview.style.transform = screent.previousTransform;
}
});
dispatch(setIsCapturingScreenshot(false));
};
const handleClose = () => {
// Do nothing. Prevent Dialog from closing.
};
return (
<div className="flex items-center justify-between gap-2">
<NavigationControls />
@ -44,10 +99,22 @@ const ToolBar = () => {
>
<Icon icon="lucide:inspect" />
</Button>
<Button
onClick={screenshotCaptureHandler}
isActive={isCapturingScreenshot}
title="Screenshot All WebViews"
>
<Icon icon="lucide:camera" />
</Button>
<ColorSchemeToggle />
<Divider />
<PreviewSuiteSelector />
<Menu />
<ModalLoader
isOpen={isCapturingScreenshot}
onClose={handleClose}
title="Screenshot"
/>
</div>
);
};

View file

@ -9,6 +9,7 @@ export interface RendererState {
rotate: boolean;
isInspecting: boolean | undefined;
layout: PreviewLayout;
isCapturingScreenshot: boolean;
}
const zoomSteps = [
@ -30,6 +31,7 @@ const initialState: RendererState = {
rotate: false,
isInspecting: undefined,
layout: window.electron.store.get('ui.previewLayout'),
isCapturingScreenshot: false,
};
export const rendererSlice = createSlice({
@ -67,6 +69,9 @@ export const rendererSlice = createSlice({
state.layout = action.payload;
window.electron.store.set('ui.previewLayout', action.payload);
},
setIsCapturingScreenshot: (state, action: PayloadAction<boolean>) => {
state.isCapturingScreenshot = action.payload;
},
},
});
@ -78,6 +83,7 @@ export const {
setRotate,
setIsInspecting,
setLayout,
setIsCapturingScreenshot,
} = rendererSlice.actions;
export const selectZoomFactor = (state: RootState) => state.renderer.zoomFactor;
@ -86,5 +92,7 @@ export const selectRotate = (state: RootState) => state.renderer.rotate;
export const selectIsInspecting = (state: RootState) =>
state.renderer.isInspecting;
export const selectLayout = (state: RootState) => state.renderer.layout;
export const selectIsCapturingScreenshot = (state: RootState) =>
state.renderer.isCapturingScreenshot;
export default rendererSlice.reducer;