Merge pull request #500 from ente-io/handle-blocked-canvas

Handle blocked canvas
This commit is contained in:
Abhinav Kumar 2022-04-27 02:24:31 +05:30 committed by GitHub
commit a581a4a3cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 147 additions and 30 deletions

View file

@ -49,7 +49,7 @@ export default function MessageDialog({
{(children || attributes?.content) && (
<Modal.Body style={{ borderTop: '1px solid #444' }}>
{children || (
<p style={{ fontSize: '1.25rem', marginBottom: 0 }}>
<p style={{ fontSize: '19px', marginBottom: 0 }}>
{attributes.content}
</p>
)}

View file

@ -24,6 +24,8 @@ import { FileUploadResults, UPLOAD_STAGES } from 'constants/upload';
import { ElectronFile, FileWithCollection } from 'types/upload';
import UploadTypeChoiceModal from './UploadTypeChoiceModal';
import Router from 'next/router';
import { isCanvasBlocked } from 'utils/upload/isCanvasBlocked';
import { downloadApp } from 'utils/common';
const FIRST_ALBUM_NAME = 'My First Album';
@ -47,6 +49,7 @@ interface Props {
setElectronFiles: (files: ElectronFile[]) => void;
showUploadTypeChoiceModal: boolean;
setShowUploadTypeChoiceModal: (open: boolean) => void;
SetDialogMessage: SetDialogMessage;
}
enum UPLOAD_STRATEGY {
@ -121,32 +124,48 @@ export default function Upload(props: Props) {
useEffect(() => {
if (
!props.uploadInProgress &&
(props.electronFiles?.length > 0 ||
props.droppedFiles?.length > 0 ||
appContext.sharedFiles?.length > 0)
props.electronFiles?.length > 0 ||
props.droppedFiles?.length > 0 ||
appContext.sharedFiles?.length > 0
) {
props.setLoading(true);
if (props.droppedFiles?.length > 0) {
// File selection by drag and drop or selection of file.
toUploadFiles.current = props.droppedFiles;
props.clearDroppedFiles();
} else if (appContext.sharedFiles?.length > 0) {
toUploadFiles.current = appContext.sharedFiles;
appContext.resetSharedFiles();
} else if (props.electronFiles?.length > 0) {
// File selection from desktop app
toUploadFiles.current = props.electronFiles;
props.setElectronFiles([]);
}
const analysisResult = analyseUploadFiles();
setAnalysisResult(analysisResult);
if (props.uploadInProgress) {
// no-op
// a upload is already in progress
} else if (isCanvasBlocked()) {
props.setDialogMessage({
title: constants.CANVAS_BLOCKED_TITLE,
staticBackdrop: true,
content: constants.CANVAS_BLOCKED_MESSAGE(),
close: { text: constants.CLOSE },
proceed: {
text: constants.DOWNLOAD_APP,
action: downloadApp,
variant: 'success',
},
});
} else {
props.setLoading(true);
if (props.droppedFiles?.length > 0) {
// File selection by drag and drop or selection of file.
toUploadFiles.current = props.droppedFiles;
props.clearDroppedFiles();
} else if (appContext.sharedFiles?.length > 0) {
toUploadFiles.current = appContext.sharedFiles;
appContext.resetSharedFiles();
} else if (props.electronFiles?.length > 0) {
// File selection from desktop app
toUploadFiles.current = props.electronFiles;
props.setElectronFiles([]);
}
const analysisResult = analyseUploadFiles();
setAnalysisResult(analysisResult);
handleCollectionCreationAndUpload(
analysisResult,
props.isFirstUpload
);
props.setLoading(false);
handleCollectionCreationAndUpload(
analysisResult,
props.isFirstUpload
);
props.setLoading(false);
}
}
}, [props.droppedFiles, appContext.sharedFiles, props.electronFiles]);

View file

@ -5,7 +5,7 @@ import { Accordion, Button, Modal, ProgressBar } from 'react-bootstrap';
import { FileRejection } from 'react-dropzone';
import styled from 'styled-components';
import { DESKTOP_APP_DOWNLOAD_URL } from 'utils/common';
import { getOSSpecificDesktopAppDownloadLink } from 'utils/common';
import constants from 'utils/strings/constants';
import { ButtonVariant, getVariantColor } from './LinkButton';
import { FileUploadResults, UPLOAD_STAGES } from 'constants/upload';
@ -292,7 +292,7 @@ export default function UploadProgress(props: Props) {
fileUploadResult={FileUploadResults.BLOCKED}
sectionTitle={constants.BLOCKED_UPLOADS}
sectionInfo={constants.ETAGS_BLOCKED(
DESKTOP_APP_DOWNLOAD_URL
getOSSpecificDesktopAppDownloadLink()
)}
/>
<ResultSection

View file

@ -668,6 +668,7 @@ export default function Gallery() {
setElectronFiles={setElectronFiles}
showUploadTypeChoiceModal={showUploadTypeChoiceModal}
setShowUploadTypeChoiceModal={setShowUploadTypeChoiceModal}
SetDialogMessage={setDialogMessage}
/>
<Sidebar
collections={collections}

View file

@ -3,6 +3,9 @@ export enum OS {
ANDROID = 'android',
IOS = 'ios',
UNKNOWN = 'unknown',
WINDOWS = 'windows',
MAC = 'mac',
LINUX = 'linux',
}
declare global {
@ -30,10 +33,22 @@ const GetDeviceOS = () => {
}
// iOS detection from: http://stackoverflow.com/a/9039885/177710
if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
if (/(iPad|iPhone|iPod)/g.test(userAgent) && !window.MSStream) {
return OS.IOS;
}
// credit: https://github.com/MikeKovarik/platform-detect/blob/master/os.mjs
if (userAgent.includes('Windows')) {
return OS.WINDOWS;
}
if (userAgent.includes('Macintosh')) {
return OS.MAC;
}
// Linux must be last
if (userAgent.includes('Linux')) {
return OS.LINUX;
}
return OS.UNKNOWN;
};

View file

@ -1,9 +1,12 @@
import constants from 'utils/strings/constants';
import { CustomError } from 'utils/error';
import GetDeviceOS, { OS } from './deviceDetection';
export const DESKTOP_APP_DOWNLOAD_URL =
const DESKTOP_APP_GITHUB_DOWNLOAD_URL =
'https://github.com/ente-io/bhari-frame/releases/latest';
const APP_DOWNLOAD_ENTE_URL_PREFIX = 'https://ente.io/download';
export function checkConnectivity() {
if (navigator.onLine) {
return true;
@ -21,8 +24,21 @@ export async function sleep(time: number) {
});
}
export function getOSSpecificDesktopAppDownloadLink() {
const os = GetDeviceOS();
let url = '';
if (os === OS.WINDOWS) {
url = `${APP_DOWNLOAD_ENTE_URL_PREFIX}/exe`;
} else if (os === OS.MAC) {
url = `${APP_DOWNLOAD_ENTE_URL_PREFIX}/dmg`;
} else {
url = DESKTOP_APP_GITHUB_DOWNLOAD_URL;
}
return url;
}
export function downloadApp() {
const win = window.open(DESKTOP_APP_DOWNLOAD_URL, '_blank');
const link = getOSSpecificDesktopAppDownloadLink();
const win = window.open(link, '_blank');
win.focus();
}

View file

@ -713,6 +713,19 @@ const englishConstants = {
'are you sure that you want to stop all the uploads in progress?',
STOP_UPLOADS_HEADER: 'stop uploads?',
YES_STOP_UPLOADS: 'yes, stop uploads',
CANVAS_BLOCKED_TITLE: 'unable to generate thumbnail',
CANVAS_BLOCKED_MESSAGE: () => (
<>
<p>
it looks like your browser has disabled access to canvas, which
is necessary to generate thumbnails for your photos
</p>
<p>
please enable access to your browser's canvas, or check out our
desktop app
</p>
</>
),
};
export default englishConstants;

View file

@ -0,0 +1,53 @@
//
// Canvas Blocker &
// Firefox privacy.resistFingerprinting Detector.
// (c) 2018 // JOHN OZBAY // CRYPT.EE
// MIT License
// Credits: https://github.com/johnozbay/canvas-block-detector/blob/master/isCanvasBlocked.js
//
export function isCanvasBlocked() {
// create a 1px image data
let blocked = false;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// some blockers just return an undefined ctx. So let's check that first.
if (ctx) {
const imageData = ctx.createImageData(1, 1);
const originalImageData = imageData.data;
// set pixels to RGB 128
originalImageData[0] = 128;
originalImageData[1] = 128;
originalImageData[2] = 128;
originalImageData[3] = 255;
// set this to canvas
ctx.putImageData(imageData, 1, 1);
try {
// now get the data back from canvas.
const checkData = ctx.getImageData(1, 1, 1, 1).data;
// If this is firefox, and privacy.resistFingerprinting is enabled,
// OR a browser extension blocking the canvas,
// This will return RGB all white (255,255,255) instead of the (128,128,128) we put.
// so let's check the R and G to see if they're 255 or 128 (matching what we've initially set)
if (
originalImageData[0] !== checkData[0] &&
originalImageData[1] !== checkData[1]
) {
blocked = true;
}
} catch (error) {
// some extensions will return getImageData null. this is to account for that.
blocked = true;
}
} else {
blocked = true;
}
return blocked;
}