mirror of
https://github.com/chinchang/web-maker.git
synced 2025-10-13 20:44:23 +02:00
13
gulpfile.js
13
gulpfile.js
@@ -195,6 +195,8 @@ gulp.task('packageExtension', function () {
|
||||
child_process.execSync('cp src/options.html extension');
|
||||
child_process.execSync('cp src/eventPage.js extension');
|
||||
child_process.execSync('cp src/icon-16.png extension');
|
||||
child_process.execSync('cp offscreen.html extension');
|
||||
child_process.execSync('cp offscreen.js extension');
|
||||
child_process.execSync('rm -rf extension/service-worker.js');
|
||||
return merge(
|
||||
gulp
|
||||
@@ -292,10 +294,13 @@ const buildExtension = series(
|
||||
);
|
||||
|
||||
function runWatcher(cb) {
|
||||
return watch(['src/**/*.js', 'src/**/*.jsx'], function (cbb) {
|
||||
buildExtension();
|
||||
cbb();
|
||||
});
|
||||
return watch(
|
||||
['src/**/*.js', 'src/**/*.jsx', 'src/**/*.json'],
|
||||
function (cbb) {
|
||||
buildExtension();
|
||||
cbb();
|
||||
}
|
||||
);
|
||||
cb();
|
||||
}
|
||||
|
||||
|
2
offscreen.html
Normal file
2
offscreen.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<!doctype html>
|
||||
<script src="./offscreen.js"></script>
|
44
offscreen.js
Normal file
44
offscreen.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// This URL must point to the public site
|
||||
const _URL = 'https://webmaker.app/signup';
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.src = _URL;
|
||||
document.documentElement.appendChild(iframe);
|
||||
chrome.runtime.onMessage.addListener(handleChromeMessages);
|
||||
|
||||
function handleChromeMessages(message, sender, sendResponse) {
|
||||
// Extensions may have an number of other reasons to send messages, so you
|
||||
// should filter out any that are not meant for the offscreen document.
|
||||
if (message.target !== 'offscreen') {
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleIframeMessage({ data }) {
|
||||
console.log('got postmessage in offscreen doc from iframe');
|
||||
|
||||
try {
|
||||
if (data.startsWith('!_{')) {
|
||||
// Other parts of the Firebase library send messages using postMessage.
|
||||
// You don't care about them in this context, so return early.
|
||||
return;
|
||||
}
|
||||
data = JSON.parse(data);
|
||||
self.removeEventListener('message', handleIframeMessage);
|
||||
|
||||
// being sent back to worker
|
||||
sendResponse(data);
|
||||
} catch (e) {
|
||||
console.log(`json parse failed - ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
globalThis.addEventListener('message', handleIframeMessage, false);
|
||||
|
||||
// Initialize the authentication flow in the iframed document. You must set the
|
||||
// second argument (targetOrigin) of the message in order for it to be successfully
|
||||
// delivered.
|
||||
iframe.contentWindow.postMessage(
|
||||
{ initAuth: true, providerName: message.providerName },
|
||||
new URL(_URL).origin
|
||||
);
|
||||
return true;
|
||||
}
|
1697
package-lock.json
generated
1697
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "web-maker",
|
||||
"version": "6.2.0",
|
||||
"version": "6.3.0",
|
||||
"description": "A blazing fast & offline web playground",
|
||||
"scripts": {
|
||||
"start": "concurrently --kill-others \"gulp start-preview-server\" \"npm run -s dev\"",
|
||||
@@ -75,8 +75,8 @@
|
||||
"code-blast-codemirror": "chinchang/code-blast-codemirror#web-maker",
|
||||
"codemirror": "^5.65.16",
|
||||
"copy-webpack-plugin": "^4.5.1",
|
||||
"firebase": "^10.8.0",
|
||||
"esprima-next": "^6.0.3",
|
||||
"firebase": "^8.10.0",
|
||||
"jszip": "^3.1.5",
|
||||
"preact": "^10.17.0",
|
||||
"preact-portal": "^1.1.3",
|
||||
|
12
packages/signup/dist/index.html
vendored
12
packages/signup/dist/index.html
vendored
@@ -1 +1,11 @@
|
||||
<!doctype html><html><head><title>signInWithPopup</title></head><body> <h1>signInWithPopup</h1> <script type="module" src="/signup/index.1903c1b7.js"></script> </body></html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>signInWithPopup</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>signInWithPopup</h1>
|
||||
|
||||
<script src="/index.a3cfa626.js" defer=""></script>
|
||||
</body>
|
||||
</html>
|
||||
|
93
src/auth.js
93
src/auth.js
@@ -1,35 +1,78 @@
|
||||
import { trackEvent } from './analytics';
|
||||
import firebase from 'firebase/app';
|
||||
import { auth } from './firebaseInit';
|
||||
import { log } from './utils';
|
||||
import {
|
||||
GithubAuthProvider as ExtensionGithubAuthProvider,
|
||||
GoogleAuthProvider as ExtensionGoogleAuthProvider,
|
||||
signOut,
|
||||
signInWithCredential
|
||||
} from 'firebase/auth/web-extension';
|
||||
import {
|
||||
signInWithPopup,
|
||||
GithubAuthProvider,
|
||||
GoogleAuthProvider
|
||||
} from 'firebase/auth';
|
||||
|
||||
export const auth = {
|
||||
export const authh = {
|
||||
logout() {
|
||||
firebase.auth().signOut();
|
||||
signOut(auth);
|
||||
},
|
||||
login(providerName) {
|
||||
var provider;
|
||||
if (providerName === 'facebook') {
|
||||
provider = new firebase.auth.FacebookAuthProvider();
|
||||
} else if (providerName === 'twitter') {
|
||||
provider = new firebase.auth.TwitterAuthProvider();
|
||||
} else if (providerName === 'google') {
|
||||
provider = new firebase.auth.GoogleAuthProvider();
|
||||
provider.addScope('https://www.googleapis.com/auth/userinfo.profile');
|
||||
} else {
|
||||
provider = new firebase.auth.GithubAuthProvider();
|
||||
async login(providerName) {
|
||||
const onSuccess = () => {
|
||||
trackEvent(
|
||||
'fn',
|
||||
'loggedIn',
|
||||
providerName,
|
||||
window.IS_EXTENSION ? 'extension' : 'web'
|
||||
);
|
||||
|
||||
// Save to recommend next time
|
||||
window.db.local.set({
|
||||
lastAuthProvider: providerName
|
||||
});
|
||||
};
|
||||
|
||||
if (window.IS_EXTENSION) {
|
||||
const result = await chrome.runtime.sendMessage({
|
||||
type: 'firebase-auth',
|
||||
providerName
|
||||
});
|
||||
|
||||
if (
|
||||
result.name === 'FirebaseError' &&
|
||||
result.code === 'auth/account-exists-with-different-credential'
|
||||
) {
|
||||
alert(
|
||||
'You have already signed up with the same email using different social login'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let credential;
|
||||
switch (providerName) {
|
||||
case 'google':
|
||||
credential = ExtensionGoogleAuthProvider.credentialFromResult(result);
|
||||
break;
|
||||
case 'github':
|
||||
credential = ExtensionGithubAuthProvider.credentialFromResult(result);
|
||||
break;
|
||||
}
|
||||
|
||||
// authenticationObject is of the type UserCredentialImpl. Use it to authenticate here
|
||||
return signInWithCredential(auth, credential).then(onSuccess);
|
||||
}
|
||||
|
||||
return firebase
|
||||
.auth()
|
||||
.signInWithPopup(provider)
|
||||
.then(function() {
|
||||
trackEvent('fn', 'loggedIn', providerName);
|
||||
// Save to recommend next time
|
||||
window.db.local.set({
|
||||
lastAuthProvider: providerName
|
||||
});
|
||||
})
|
||||
.catch(function(error) {
|
||||
var provider;
|
||||
if (providerName === 'google') {
|
||||
provider = new GoogleAuthProvider();
|
||||
provider.addScope('https://www.googleapis.com/auth/userinfo.profile');
|
||||
} else {
|
||||
provider = new GithubAuthProvider();
|
||||
}
|
||||
|
||||
return signInWithPopup(auth, provider)
|
||||
.then(onSuccess)
|
||||
.catch(function (error) {
|
||||
log(error);
|
||||
if (error.code === 'auth/account-exists-with-different-credential') {
|
||||
alert(
|
||||
|
@@ -134,7 +134,8 @@ export default class AddLibrary extends Component {
|
||||
https://cdnjs.cloudflare.com, https://unpkg.com,
|
||||
https://maxcdn.com, https://cdn77.com,
|
||||
https://maxcdn.bootstrapcdn.com, https://cdn.jsdelivr.net/,
|
||||
https://rawgit.com, https://wzrd.in, https://cdn.tailwindcss.com
|
||||
https://rawgit.com, https://wzrd.in, https://cdn.tailwindcss.com,
|
||||
https://assets.webmaker.app
|
||||
</p>
|
||||
|
||||
<textarea
|
||||
|
@@ -1,6 +1,12 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import firebase from 'firebase/app';
|
||||
import 'firebase/storage';
|
||||
import {
|
||||
deleteObject,
|
||||
uploadBytesResumable,
|
||||
ref,
|
||||
listAll,
|
||||
getDownloadURL
|
||||
} from 'firebase/storage';
|
||||
import { storage } from '../firebaseInit';
|
||||
import { HStack, Stack, VStack } from './Stack';
|
||||
import { copyToClipboard } from '../utils';
|
||||
import { Trans } from '@lingui/macro';
|
||||
@@ -17,6 +23,7 @@ function getFileType(url) {
|
||||
}
|
||||
return ext;
|
||||
}
|
||||
|
||||
const Assets = ({ onProBtnClick, onLoginBtnClick }) => {
|
||||
const [files, setFiles] = useState([]);
|
||||
const [isFetchingFiles, setIsFetchingFiles] = useState(false);
|
||||
@@ -26,8 +33,6 @@ const Assets = ({ onProBtnClick, onLoginBtnClick }) => {
|
||||
const [uploadProgress, setUploadProgress] = useState();
|
||||
const [listType, setListType] = useState('grid');
|
||||
|
||||
const storageRef = firebase.storage().ref(`assets/${window.user?.uid}`);
|
||||
|
||||
const uploadFile = file => {
|
||||
if (file.size > 1024 * 1024) {
|
||||
// 1MB limit
|
||||
@@ -39,9 +44,11 @@ const Assets = ({ onProBtnClick, onLoginBtnClick }) => {
|
||||
const metadata = {
|
||||
cacheControl: 'public, max-age=3600' // 1 hr
|
||||
};
|
||||
|
||||
const fileRef = storageRef.child(file.name);
|
||||
const task = fileRef.put(file, metadata);
|
||||
const task = uploadBytesResumable(
|
||||
ref(storage, `assets/${window.user?.uid}/${file.name}`),
|
||||
file,
|
||||
metadata
|
||||
);
|
||||
|
||||
task.on(
|
||||
'state_changed',
|
||||
@@ -49,12 +56,12 @@ const Assets = ({ onProBtnClick, onLoginBtnClick }) => {
|
||||
// Observe state change events such as progress, pause, and resume
|
||||
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
|
||||
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
|
||||
console.log('Upload is ' + progress + '% done');
|
||||
// console.log('Upload is ' + progress + '% done');
|
||||
},
|
||||
error => {
|
||||
// Handle unsuccessful uploads
|
||||
setIsUploading(false);
|
||||
console.error('File upload error:', error);
|
||||
// console.log('File upload error:', error);
|
||||
alertsService.add('⚠️ File upload failed');
|
||||
},
|
||||
() => {
|
||||
@@ -78,11 +85,11 @@ const Assets = ({ onProBtnClick, onLoginBtnClick }) => {
|
||||
// Function to fetch existing files
|
||||
const fetchFiles = () => {
|
||||
setIsFetchingFiles(true);
|
||||
storageRef
|
||||
.listAll()
|
||||
|
||||
listAll(ref(storage, `assets/${window.user?.uid}`))
|
||||
.then(result => {
|
||||
const filePromises = result.items.map(item => {
|
||||
return item.getDownloadURL().then(url => {
|
||||
return getDownloadURL(item).then(url => {
|
||||
return { name: item.name, url };
|
||||
});
|
||||
});
|
||||
@@ -190,9 +197,8 @@ const Assets = ({ onProBtnClick, onLoginBtnClick }) => {
|
||||
const file = files[index];
|
||||
const answer = confirm(`Are you sure you want to delete "${file.name}"?`);
|
||||
if (!answer) return;
|
||||
const fileRef = storageRef.child(file.name);
|
||||
fileRef
|
||||
.delete()
|
||||
const fileRef = ref(storage, file.url);
|
||||
deleteObject(fileRef)
|
||||
.then(() => {
|
||||
alertsService.add('File deleted successfully');
|
||||
setFiles(files.filter((_, i) => i !== index));
|
||||
@@ -259,6 +265,15 @@ const Assets = ({ onProBtnClick, onLoginBtnClick }) => {
|
||||
</div>
|
||||
</div>
|
||||
{isFetchingFiles && <LoaderWithText>Fetching files...</LoaderWithText>}
|
||||
|
||||
{!isFetchingFiles && !files.length ? (
|
||||
<Stack justify="center">
|
||||
<Text align="center" appearance="secondary">
|
||||
No files uploaded yet
|
||||
</Text>
|
||||
</Stack>
|
||||
) : null}
|
||||
|
||||
<VStack align="stretch" gap={1}>
|
||||
{files.length ? (
|
||||
<Stack gap={1}>
|
||||
|
@@ -106,6 +106,19 @@ export function Changelog(props) {
|
||||
return (
|
||||
<div>
|
||||
<h1>Whats new?</h1>
|
||||
|
||||
<Notification version="6.3.0" {...props} isLatest={true}>
|
||||
<NotificationItem type="bug">
|
||||
Extension Only: JavaScript is NOW WORKING, again! 🎉 The extension is
|
||||
now migrated to MV3 which means its much more secure, and of course,
|
||||
JavaScript now works again! 🥳
|
||||
</NotificationItem>
|
||||
<NotificationItem type="ui">
|
||||
Tailwind CSS 2 template has been removed since Extension now supports
|
||||
the lateat Tailwind CSS 3 template.
|
||||
</NotificationItem>
|
||||
</Notification>
|
||||
|
||||
<Notification version="6.2.0" {...props} isLatest={true}>
|
||||
<li>Preact template updated to latest version with hooks.</li>
|
||||
<li>Mail option added in help modal.</li>
|
||||
|
@@ -38,7 +38,7 @@ export default class ContentWrap extends Component {
|
||||
window.localStorage.getItem(LocalStorageKeys.WAS_CONSOLE_OPEN) ===
|
||||
'true',
|
||||
isCssSettingsModalOpen: false,
|
||||
isPreviewNotWorkingModalVisible: false,
|
||||
|
||||
logs: []
|
||||
};
|
||||
|
||||
@@ -63,15 +63,11 @@ export default class ContentWrap extends Component {
|
||||
this.clearConsoleBtnClickHandler.bind(this);
|
||||
this.toggleConsole = this.toggleConsole.bind(this);
|
||||
this.evalConsoleExpr = this.evalConsoleExpr.bind(this);
|
||||
this.dismissPreviewNotWorkingModal =
|
||||
this.dismissPreviewNotWorkingModal.bind(this);
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return (
|
||||
this.state.isConsoleOpen !== nextState.isConsoleOpen ||
|
||||
this.state.isCssSettingsModalOpen !== nextState.isCssSettingsModalOpen ||
|
||||
this.state.isPreviewNotWorkingModalVisible !==
|
||||
nextState.isPreviewNotWorkingModalVisible ||
|
||||
this.state.logs !== nextState.logs ||
|
||||
this.state.codeSplitSizes !== nextState.codeSplitSizes ||
|
||||
this.state.mainSplitSizes !== nextState.mainSplitSizes ||
|
||||
@@ -145,10 +141,7 @@ export default class ContentWrap extends Component {
|
||||
createPreviewFile(html, css, js) {
|
||||
const versionMatch = navigator.userAgent.match(/Chrome\/(\d+)/);
|
||||
|
||||
const shouldInlineJs =
|
||||
!window.webkitRequestFileSystem ||
|
||||
!window.IS_EXTENSION ||
|
||||
(versionMatch && +versionMatch[1] >= 104);
|
||||
const shouldInlineJs = true;
|
||||
var contents = getCompleteHtml(
|
||||
html,
|
||||
css,
|
||||
@@ -164,80 +157,52 @@ export default class ContentWrap extends Component {
|
||||
trackEvent.hasTrackedCode = true;
|
||||
}
|
||||
|
||||
if (shouldInlineJs) {
|
||||
if (this.detachedWindow) {
|
||||
log('✉️ Sending message to detached window');
|
||||
this.detachedWindow.postMessage({ contents }, '*');
|
||||
} else {
|
||||
// 1. we refresh the frame so that all JS is cleared in the frame. this will
|
||||
// break the iframe since sandboxed frame isn't served by SW (needed for offline support)
|
||||
// 2. we cache and remove the sandbox attribute and refresh again so that it gets served by SW
|
||||
// 3. we add back cached sandbox attr & write the contents to the iframe
|
||||
const refreshAndDo = fn => {
|
||||
frameRefreshPromise =
|
||||
frameRefreshPromise ||
|
||||
// Race earlier had a settimeout too as a fallback. It was removed because onload
|
||||
// was firing 100% times.
|
||||
// TODO: remove race
|
||||
Promise.race([
|
||||
new Promise(resolve => {
|
||||
this.frame.onload = () => {
|
||||
resolve('onload');
|
||||
};
|
||||
})
|
||||
]);
|
||||
|
||||
frameRefreshPromise.then(resolutionReason => {
|
||||
frameRefreshPromise = null;
|
||||
log('resolved with ', resolutionReason);
|
||||
fn();
|
||||
});
|
||||
// Setting to blank string cause frame to reload
|
||||
if (window.IS_EXTENSION) {
|
||||
this.frame.src = '';
|
||||
} else {
|
||||
this.frame.src = this.frame.src;
|
||||
}
|
||||
};
|
||||
const writeInsideIframe = () => {
|
||||
if (!cachedSandboxAttribute && window.DEBUG) {
|
||||
// alert('sandbox empty');
|
||||
}
|
||||
log('sending PM');
|
||||
|
||||
if (window.IS_EXTENSION) {
|
||||
this.frame.contentDocument.open();
|
||||
this.frame.contentDocument.write(contents);
|
||||
this.frame.contentDocument.close();
|
||||
} else {
|
||||
this.frame.contentWindow.postMessage({ contents }, '*');
|
||||
}
|
||||
};
|
||||
// refreshAndDo(() => {
|
||||
// cachedSandboxAttribute = this.frame.getAttribute('sandbox');
|
||||
// // console.log('removing sandbox', sandbox);
|
||||
// // this.frame.setAttribute('sweet', sandbox);
|
||||
// // this.frame.removeAttribute('sandbox');
|
||||
// refreshAndDo(writeInsideIframe);
|
||||
// });
|
||||
refreshAndDo(writeInsideIframe);
|
||||
}
|
||||
if (this.detachedWindow) {
|
||||
log('✉️ Sending message to detached window');
|
||||
this.detachedWindow.postMessage({ contents }, '*');
|
||||
} else {
|
||||
// we need to store user script in external JS file to prevent inline-script
|
||||
// CSP from affecting it.
|
||||
writeFile('script.js', blobjs, () => {
|
||||
writeFile('preview.html', blob, () => {
|
||||
var origin = chrome.i18n.getMessage()
|
||||
? `chrome-extension://${chrome.i18n.getMessage('@@extension_id')}`
|
||||
: `${location.origin}`;
|
||||
var src = `filesystem:${origin}/temporary/preview.html`;
|
||||
if (this.detachedWindow) {
|
||||
this.detachedWindow.postMessage({ url: src }, '*');
|
||||
} else {
|
||||
this.frame.src = src;
|
||||
}
|
||||
// 1. we refresh the frame so that all JS is cleared in the frame. this will
|
||||
// break the iframe since sandboxed frame isn't served by SW (needed for offline support)
|
||||
// 2. we cache and remove the sandbox attribute and refresh again so that it gets served by SW
|
||||
// 3. we add back cached sandbox attr & write the contents to the iframe
|
||||
const refreshAndDo = fn => {
|
||||
frameRefreshPromise =
|
||||
frameRefreshPromise ||
|
||||
// Race earlier had a settimeout too as a fallback. It was removed because onload
|
||||
// was firing 100% times.
|
||||
// TODO: remove race
|
||||
Promise.race([
|
||||
new Promise(resolve => {
|
||||
this.frame.onload = () => {
|
||||
resolve('onload');
|
||||
};
|
||||
})
|
||||
]);
|
||||
|
||||
frameRefreshPromise.then(resolutionReason => {
|
||||
frameRefreshPromise = null;
|
||||
log('resolved with ', resolutionReason);
|
||||
fn();
|
||||
});
|
||||
});
|
||||
|
||||
this.frame.src = this.frame.src;
|
||||
};
|
||||
const writeInsideIframe = () => {
|
||||
if (!cachedSandboxAttribute && window.DEBUG) {
|
||||
// alert('sandbox empty');
|
||||
}
|
||||
log('sending PM');
|
||||
|
||||
this.frame.contentWindow.postMessage({ contents }, '*');
|
||||
};
|
||||
// refreshAndDo(() => {
|
||||
// cachedSandboxAttribute = this.frame.getAttribute('sandbox');
|
||||
// // console.log('removing sandbox', sandbox);
|
||||
// // this.frame.setAttribute('sweet', sandbox);
|
||||
// // this.frame.removeAttribute('sandbox');
|
||||
// refreshAndDo(writeInsideIframe);
|
||||
// });
|
||||
refreshAndDo(writeInsideIframe);
|
||||
}
|
||||
}
|
||||
cleanupErrors(lang) {
|
||||
@@ -277,14 +242,13 @@ export default class ContentWrap extends Component {
|
||||
js: this.cmCodes.js
|
||||
};
|
||||
log('🔎 setPreviewContent', isForced);
|
||||
const targetFrame = this.detachedWindow
|
||||
? this.detachedWindow //this.detachedWindow.document.querySelector('iframe')
|
||||
: this.frame;
|
||||
const targetFrame = this.detachedWindow ? this.detachedWindow : this.frame;
|
||||
|
||||
const cssMode = this.props.currentItem.cssMode;
|
||||
// If just CSS was changed (and everything shudn't be empty),
|
||||
// change the styles inside the iframe.
|
||||
if (
|
||||
false &&
|
||||
!isForced &&
|
||||
currentCode.html === this.codeInPreview.html &&
|
||||
currentCode.js === this.codeInPreview.js &&
|
||||
@@ -335,19 +299,6 @@ export default class ContentWrap extends Component {
|
||||
this.showErrors(resultItem.errors.lang, resultItem.errors.data);
|
||||
}
|
||||
});
|
||||
|
||||
const versionMatch = navigator.userAgent.match(/Chrome\/(\d+)/);
|
||||
if (
|
||||
result[2].code &&
|
||||
versionMatch &&
|
||||
+versionMatch[1] >= 104 &&
|
||||
window.IS_EXTENSION &&
|
||||
!window.sessionStorage.getItem('previewErrorMessageDismissed')
|
||||
) {
|
||||
this.setState({
|
||||
isPreviewNotWorkingModalVisible: true
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -687,13 +638,6 @@ export default class ContentWrap extends Component {
|
||||
this.props.onPrettifyBtnClick(codeType);
|
||||
}
|
||||
|
||||
dismissPreviewNotWorkingModal() {
|
||||
this.setState({
|
||||
isPreviewNotWorkingModalVisible: false
|
||||
});
|
||||
window.sessionStorage.setItem('previewErrorMessageDismissed', true);
|
||||
}
|
||||
|
||||
render() {
|
||||
// log('contentwrap update');
|
||||
|
||||
@@ -960,7 +904,7 @@ export default class ContentWrap extends Component {
|
||||
ref={el => (this.frame = el)}
|
||||
frameborder="0"
|
||||
id="demo-frame"
|
||||
allowpaymentrequest="true"
|
||||
src="./indexpm.html"
|
||||
allowfullscreen="true"
|
||||
/>
|
||||
) : (
|
||||
@@ -996,34 +940,6 @@ export default class ContentWrap extends Component {
|
||||
settings={this.props.currentItem.cssSettings}
|
||||
editorTheme={this.props.prefs.editorTheme}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
show={this.state.isPreviewNotWorkingModalVisible}
|
||||
closeHandler={this.dismissPreviewNotWorkingModal}
|
||||
>
|
||||
<h1>🔴 JavaScript preview not working!</h1>
|
||||
<p>
|
||||
Latest version of Chrome has changed a few things because of which
|
||||
JavaScript preview has stopped working.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you want to work with just HTML & CSS, you can still continue
|
||||
here. Otherwise, it is recommended to switch to the Web Maker web
|
||||
app which is much more powerful and works offline too -{' '}
|
||||
<a href="https://webmaker.app/app" target="_blank">
|
||||
Go to web app
|
||||
</a>
|
||||
</p>
|
||||
<div class="flex flex-h-end">
|
||||
<button
|
||||
class="btn btn--primary"
|
||||
onClick={this.dismissPreviewNotWorkingModal}
|
||||
>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
</SplitPane>
|
||||
);
|
||||
|
@@ -257,7 +257,7 @@ export default class ContentWrapFiles extends Component {
|
||||
obj[file.path] =
|
||||
'<script src="' +
|
||||
(chrome.extension
|
||||
? chrome.extension.getURL('/lib/screenlog.js')
|
||||
? chrome.runtime.getURL('lib/screenlog.js')
|
||||
: `${location.origin}${
|
||||
window.DEBUG ? '' : BASE_PATH
|
||||
}/lib/screenlog.js`) +
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import { h, Component } from 'preact';
|
||||
import { trackEvent } from '../analytics';
|
||||
import { auth } from '../auth';
|
||||
import { authh } from '../auth';
|
||||
|
||||
export default class Login extends Component {
|
||||
login(e) {
|
||||
const provider = e.target.dataset.authProvider;
|
||||
trackEvent('ui', 'loginProviderClick', provider);
|
||||
auth.login(provider);
|
||||
authh.login(provider);
|
||||
}
|
||||
componentDidMount() {
|
||||
window.db.local.get(
|
||||
|
@@ -114,7 +114,7 @@ export function Profile({ user, logoutBtnHandler }) {
|
||||
? 'Never ever'
|
||||
: getHumanReadableDate(
|
||||
currentSubscription.attributes.renews_at
|
||||
)}
|
||||
)}
|
||||
</Text>
|
||||
</Text>
|
||||
|
||||
|
@@ -9,7 +9,9 @@ import { Icon } from './Icons';
|
||||
import { Text } from './Text';
|
||||
|
||||
const FREE_PUBLIC_ITEM_COUNT = 1;
|
||||
const BASE_URL = location.origin;
|
||||
const BASE_URL = location.origin.includes('chrome-extension://')
|
||||
? 'webmaker.app'
|
||||
: location.origin;
|
||||
const TOGGLE_VISIBILITY_API =
|
||||
/*!window.location.origin.includes('localhost')
|
||||
? 'http://127.0.0.1:5001/web-maker-app/us-central1/toggleVisibility'
|
||||
|
@@ -43,10 +43,10 @@ import { modes, HtmlModes, CssModes, JsModes } from '../codeModes';
|
||||
import { trackEvent } from '../analytics';
|
||||
import { deferred } from '../deferred';
|
||||
import { alertsService } from '../notifications';
|
||||
import firebase from 'firebase/app';
|
||||
import 'firebase/auth';
|
||||
import { auth } from '../firebaseInit';
|
||||
import { onAuthStateChanged } from 'firebase/auth';
|
||||
import { Profile } from './Profile';
|
||||
import { auth } from '../auth';
|
||||
import { authh } from '../auth';
|
||||
import { SupportDeveloperModal } from './SupportDeveloperModal';
|
||||
import { KeyboardShortcutsModal } from './KeyboardShortcutsModal';
|
||||
import { takeScreenshot } from '../takeScreenshot';
|
||||
@@ -84,7 +84,7 @@ if (module.hot) {
|
||||
}
|
||||
|
||||
const UNSAVED_WARNING_COUNT = 15;
|
||||
const version = '6.2.0';
|
||||
const version = '6.3.0';
|
||||
|
||||
// Read forced settings as query parameters
|
||||
window.forcedSettings = {};
|
||||
@@ -197,7 +197,7 @@ export default class App extends Component {
|
||||
window.user = savedUser;
|
||||
}
|
||||
|
||||
firebase.auth().onAuthStateChanged(authUser => {
|
||||
onAuthStateChanged(auth, authUser => {
|
||||
this.setState({ isLoginModalOpen: false });
|
||||
if (authUser) {
|
||||
log('You are -> ', authUser);
|
||||
@@ -220,7 +220,6 @@ export default class App extends Component {
|
||||
this.setState({ user: newUser });
|
||||
window.user = newUser;
|
||||
// window.localStorage.setItem('user', authUser);
|
||||
trackEvent('fn', 'loggedIn', window.IS_EXTENSION ? 'extension' : 'web');
|
||||
|
||||
if (!window.localStorage[LocalStorageKeys.ASKED_TO_IMPORT_CREATIONS]) {
|
||||
this.fetchItems(false, true).then(items => {
|
||||
@@ -641,7 +640,6 @@ export default class App extends Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
console.log('itemId', this.props.itemId);
|
||||
function setBodySize() {
|
||||
document.body.style.height = `${window.innerHeight}px`;
|
||||
}
|
||||
@@ -1081,18 +1079,15 @@ export default class App extends Component {
|
||||
alertsService.add('Setting saved');
|
||||
});
|
||||
if (window.user) {
|
||||
window.db.getDb().then(remoteDb => {
|
||||
remoteDb
|
||||
.collection('users')
|
||||
.doc(window.user.uid)
|
||||
.update({
|
||||
[`settings.${settingName}`]: this.state.prefs[settingName]
|
||||
})
|
||||
.then(arg => {
|
||||
log(`Setting "${settingName}" for user`, arg);
|
||||
})
|
||||
.catch(error => log(error));
|
||||
});
|
||||
db.updateUserSetting(
|
||||
window.user.uid,
|
||||
settingName,
|
||||
this.state.prefs[settingName]
|
||||
)
|
||||
.then(arg => {
|
||||
log(`Setting "${settingName}" saved`, arg);
|
||||
})
|
||||
.catch(error => log(error));
|
||||
}
|
||||
trackEvent('ui', 'updatePref-' + settingName, prefs[settingName]);
|
||||
}
|
||||
@@ -1140,7 +1135,7 @@ export default class App extends Component {
|
||||
}
|
||||
}
|
||||
trackEvent('fn', 'loggedOut');
|
||||
auth.logout();
|
||||
authh.logout();
|
||||
this.setState({ isProfileModalOpen: false });
|
||||
this.createNewItem();
|
||||
alertsService.add('Log out successfull');
|
||||
@@ -1943,7 +1938,6 @@ export default class App extends Component {
|
||||
this.loginBtnClickHandler();
|
||||
}}
|
||||
onBuyFromExtensionClick={() => {
|
||||
console.log('open modal');
|
||||
this.closeAllOverlays();
|
||||
this.setState({ isProOnAppModalOpen: true });
|
||||
}}
|
||||
|
137
src/db.js
137
src/db.js
@@ -1,6 +1,17 @@
|
||||
import './firebaseInit';
|
||||
import firebase from 'firebase/app';
|
||||
// import './firebaseInit';
|
||||
import 'firebase/firestore';
|
||||
import { db } from './firebaseInit';
|
||||
import {
|
||||
getDoc,
|
||||
getDocs,
|
||||
doc,
|
||||
updateDoc,
|
||||
setDoc,
|
||||
where,
|
||||
collection,
|
||||
getCountFromServer,
|
||||
query
|
||||
} from 'firebase/firestore';
|
||||
import { deferred } from './deferred';
|
||||
import { trackEvent } from './analytics';
|
||||
import { log } from './utils';
|
||||
@@ -26,9 +37,6 @@ function getArrayFromQuerySnapshot(querySnapshot) {
|
||||
(() => {
|
||||
const FAUX_DELAY = 1;
|
||||
|
||||
var db;
|
||||
var dbPromise;
|
||||
|
||||
var local = {
|
||||
get: (obj, cb) => {
|
||||
const retVal = {};
|
||||
@@ -64,46 +72,9 @@ function getArrayFromQuerySnapshot(querySnapshot) {
|
||||
const dbLocalAlias = chrome && chrome.storage ? chrome.storage.local : local;
|
||||
const dbSyncAlias = chrome && chrome.storage ? chrome.storage.sync : local;
|
||||
|
||||
async function getDb() {
|
||||
if (dbPromise) {
|
||||
return dbPromise;
|
||||
}
|
||||
function getDb() {
|
||||
log('Initializing firestore');
|
||||
dbPromise = new Promise((resolve, reject) => {
|
||||
if (db) {
|
||||
return resolve(db);
|
||||
}
|
||||
const firestoreInstance = firebase.firestore();
|
||||
|
||||
return firestoreInstance
|
||||
.enablePersistence({ experimentalTabSynchronization: true })
|
||||
.then(function () {
|
||||
// Initialize Cloud Firestore through firebase
|
||||
db = firebase.firestore();
|
||||
// const settings = {
|
||||
// timestampsInSnapshots: true
|
||||
// };
|
||||
// db.settings(settings);
|
||||
log('firebase db ready', db);
|
||||
resolve(db);
|
||||
})
|
||||
.catch(function (err) {
|
||||
reject(err.code);
|
||||
if (err.code === 'failed-precondition') {
|
||||
// Multiple tabs open, persistence can only be enabled
|
||||
// in one tab at a a time.
|
||||
alert(
|
||||
"Opening Web Maker web app in multiple tabs isn't supported at present and it seems like you already have it opened in another tab. Please use in one tab."
|
||||
);
|
||||
trackEvent('fn', 'multiTabError');
|
||||
} else if (err.code === 'unimplemented') {
|
||||
// The current browser does not support all of the
|
||||
// features required to enable persistence
|
||||
// ...
|
||||
}
|
||||
});
|
||||
});
|
||||
return dbPromise;
|
||||
return db;
|
||||
}
|
||||
|
||||
async function getUserLastSeenVersion() {
|
||||
@@ -134,44 +105,31 @@ function getArrayFromQuerySnapshot(querySnapshot) {
|
||||
function () {}
|
||||
);
|
||||
if (window.user) {
|
||||
const remoteDb = await getDb();
|
||||
remoteDb.doc(`users/${window.user.uid}`).update({
|
||||
updateDoc(doc(db, `users/${window.user.uid}`), {
|
||||
lastSeenVersion: version
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function getUser(userId) {
|
||||
const remoteDb = await getDb();
|
||||
return remoteDb
|
||||
.doc(`users/${userId}`)
|
||||
.get()
|
||||
.then(doc => {
|
||||
if (!doc.exists) {
|
||||
// return remoteDb.doc(`users/${userId}`).set(
|
||||
// {},
|
||||
// {
|
||||
// merge: true
|
||||
// }
|
||||
// );
|
||||
return {};
|
||||
}
|
||||
const user = doc.data();
|
||||
return getDoc(doc(db, `users/${userId}`)).then(doc => {
|
||||
if (!doc.exists()) {
|
||||
// return setDoc(doc(db, `users/${userId}`), {}, { merge: true });
|
||||
return {};
|
||||
}
|
||||
|
||||
return user;
|
||||
});
|
||||
const user = doc.data();
|
||||
// Object.assign(window.user, user);
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchItem(itemId) {
|
||||
const remoteDb = await getDb();
|
||||
return remoteDb
|
||||
.doc(`items/${itemId}`)
|
||||
.get()
|
||||
.then(doc => {
|
||||
if (!doc.exists) return {};
|
||||
const data = doc.data();
|
||||
return data;
|
||||
});
|
||||
getDoc(doc(db, `items/${itemId}`)).then(doc => {
|
||||
if (!doc.exists) return {};
|
||||
const data = doc.data();
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch user settings.
|
||||
@@ -188,24 +146,28 @@ function getArrayFromQuerySnapshot(querySnapshot) {
|
||||
}
|
||||
|
||||
async function getPublicItemCount(userId) {
|
||||
const remoteDb = await getDb();
|
||||
return remoteDb
|
||||
.collection('items')
|
||||
.where('createdBy', '==', userId)
|
||||
.where('isPublic', '==', true)
|
||||
.get()
|
||||
.then(snapShot => {
|
||||
return snapShot.size;
|
||||
});
|
||||
const q = query(
|
||||
collection(db, 'items'),
|
||||
where('createdBy', '==', userId),
|
||||
where('isPublic', '==', true)
|
||||
);
|
||||
const snapshot = await getCountFromServer(q);
|
||||
return snapshot.data().count;
|
||||
}
|
||||
|
||||
async function getUserSubscriptionEvents(userId) {
|
||||
const remoteDb = await getDb();
|
||||
return remoteDb
|
||||
.collection('subscriptions')
|
||||
.where('userId', '==', userId)
|
||||
.get()
|
||||
.then(getArrayFromQuerySnapshot);
|
||||
const q = query(
|
||||
collection(db, 'subscriptions'),
|
||||
where('userId', '==', userId)
|
||||
);
|
||||
|
||||
return getDocs(q).then(getArrayFromQuerySnapshot);
|
||||
}
|
||||
|
||||
async function updateUserSetting(userId, settingName, settingValue) {
|
||||
return updateDoc(doc(db, `users/${userId}`), {
|
||||
[`settings.${settingName}`]: settingValue
|
||||
});
|
||||
}
|
||||
|
||||
window.db = {
|
||||
@@ -217,6 +179,7 @@ function getArrayFromQuerySnapshot(querySnapshot) {
|
||||
fetchItem,
|
||||
getPublicItemCount,
|
||||
getUserSubscriptionEvents,
|
||||
updateUserSetting,
|
||||
local: dbLocalAlias,
|
||||
sync: dbSyncAlias
|
||||
};
|
||||
|
@@ -4,9 +4,7 @@ window.addEventListener('message', e => {
|
||||
const frame = document.querySelector('iframe');
|
||||
frame.src = frame.src;
|
||||
setTimeout(() => {
|
||||
frame.contentDocument.open();
|
||||
frame.contentDocument.write(e.data.contents);
|
||||
frame.contentDocument.close();
|
||||
frame.contentWindow.postMessage(e.data, '*');
|
||||
}, 10);
|
||||
}
|
||||
if (e.data && e.data.url && e.data.url.match(/index\.html/)) {
|
||||
|
@@ -1,16 +1,16 @@
|
||||
function openApp() {
|
||||
chrome.tabs.create({
|
||||
url: chrome.extension.getURL('index.html'),
|
||||
url: chrome.runtime.getURL('index.html'),
|
||||
selected: true
|
||||
});
|
||||
}
|
||||
|
||||
chrome.browserAction.onClicked.addListener(function() {
|
||||
chrome.action.onClicked.addListener(function () {
|
||||
openApp();
|
||||
});
|
||||
|
||||
// Listen for tabs getting created.
|
||||
chrome.tabs.onCreated.addListener(function(tab) {
|
||||
chrome.tabs.onCreated.addListener(function (tab) {
|
||||
// If a new tab is opened (without any URL), check user's
|
||||
// replace Tab setting and act accordingly. Default is false.
|
||||
if (tab.url === 'chrome://newtab/') {
|
||||
@@ -18,12 +18,12 @@ chrome.tabs.onCreated.addListener(function(tab) {
|
||||
{
|
||||
replaceNewTab: false
|
||||
},
|
||||
function(items) {
|
||||
function (items) {
|
||||
if (items.replaceNewTab) {
|
||||
chrome.tabs.update(
|
||||
tab.id,
|
||||
{
|
||||
url: chrome.extension.getURL('index.html')
|
||||
url: chrome.runtime.getURL('index.html')
|
||||
},
|
||||
function callback() {
|
||||
console.log('ho gaya');
|
||||
@@ -45,3 +45,78 @@ chrome.runtime.onInstalled.addListener(function callback(details) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const OFFSCREEN_DOCUMENT_PATH = '/offscreen.html';
|
||||
|
||||
// Chrome only allows for a single offscreenDocument. This is a helper function
|
||||
// that returns a boolean indicating if a document is already active.
|
||||
async function hasDocument() {
|
||||
// Check all windows controlled by the service worker to see if one
|
||||
// of them is the offscreen document with the given path
|
||||
const matchedClients = await clients.matchAll();
|
||||
return matchedClients.some(
|
||||
c => c.url === chrome.runtime.getURL(OFFSCREEN_DOCUMENT_PATH)
|
||||
);
|
||||
}
|
||||
|
||||
async function setupOffscreenDocument(path) {
|
||||
// If we do not have a document, we are already setup and can skip
|
||||
if (await chrome.offscreen.hasDocument()) return;
|
||||
await chrome.offscreen.createDocument({
|
||||
url: path,
|
||||
reasons: [chrome.offscreen.Reason.DOM_SCRAPING],
|
||||
justification: 'authentication'
|
||||
});
|
||||
}
|
||||
|
||||
async function closeOffscreenDocument() {
|
||||
if (!(await hasDocument())) {
|
||||
return;
|
||||
}
|
||||
await chrome.offscreen.closeDocument();
|
||||
}
|
||||
|
||||
function getAuth(providerName) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
// sending to offscreen document
|
||||
const auth = await chrome.runtime.sendMessage({
|
||||
type: 'firebase-auth',
|
||||
target: 'offscreen',
|
||||
providerName
|
||||
});
|
||||
auth?.name !== 'FirebaseError' ? resolve(auth) : reject(auth);
|
||||
});
|
||||
}
|
||||
|
||||
async function firebaseAuth(providerName) {
|
||||
await setupOffscreenDocument(OFFSCREEN_DOCUMENT_PATH);
|
||||
|
||||
const auth = await getAuth(providerName)
|
||||
.then(auth => {
|
||||
console.log('User Authenticated', auth);
|
||||
return auth;
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.code === 'auth/operation-not-allowed') {
|
||||
console.error(
|
||||
'You must enable an OAuth provider in the Firebase' +
|
||||
' console in order to use signInWithPopup. This sample' +
|
||||
' uses Google by default.'
|
||||
);
|
||||
} else {
|
||||
console.error(err);
|
||||
return err;
|
||||
}
|
||||
})
|
||||
.finally(closeOffscreenDocument);
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
|
||||
// received from the app
|
||||
if (message.type === 'firebase-auth') {
|
||||
firebaseAuth(message.providerName).then(sendResponse);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
@@ -1,5 +1,12 @@
|
||||
import firebase from 'firebase/app';
|
||||
// import 'firebase/firestore';
|
||||
import { initializeApp } from 'firebase/app';
|
||||
import { getAuth } from 'firebase/auth';
|
||||
import {
|
||||
initializeFirestore,
|
||||
persistentLocalCache,
|
||||
persistentMultipleTabManager
|
||||
} from 'firebase/firestore';
|
||||
import { getStorage } from 'firebase/storage';
|
||||
|
||||
const config = {
|
||||
apiKey: 'AIzaSyBl8Dz7ZOE7aP75mipYl2zKdLSRzBU2fFc',
|
||||
authDomain: 'web-maker-app.firebaseapp.com',
|
||||
@@ -8,4 +15,14 @@ const config = {
|
||||
storageBucket: 'web-maker-app.appspot.com',
|
||||
messagingSenderId: '560473480645'
|
||||
};
|
||||
firebase.initializeApp(config);
|
||||
|
||||
const app = initializeApp(config);
|
||||
export { app };
|
||||
|
||||
export const auth = getAuth(app);
|
||||
export const storage = getStorage(app);
|
||||
export const db = initializeFirestore(app, {
|
||||
localCache: persistentLocalCache({
|
||||
tabManager: persistentMultipleTabManager()
|
||||
})
|
||||
});
|
||||
|
@@ -1,16 +1,27 @@
|
||||
import { deferred } from './deferred';
|
||||
import { log } from './utils';
|
||||
import firebase from 'firebase/app';
|
||||
import {
|
||||
collection,
|
||||
deleteField,
|
||||
where,
|
||||
getDoc,
|
||||
query,
|
||||
setDoc,
|
||||
writeBatch,
|
||||
doc,
|
||||
deleteDoc,
|
||||
updateDoc,
|
||||
getDocs
|
||||
} from 'firebase/firestore';
|
||||
|
||||
export const itemService = {
|
||||
async getItem(id) {
|
||||
var remoteDb = await window.db.getDb();
|
||||
return remoteDb
|
||||
.doc(`items/${id}`)
|
||||
.get()
|
||||
var remoteDb = window.db.getDb();
|
||||
return getDoc(doc(remoteDb, `items/${id}`))
|
||||
.then(doc => {
|
||||
return doc.data();
|
||||
});
|
||||
})
|
||||
.catch(error => log(error));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -41,23 +52,23 @@ export const itemService = {
|
||||
const items = [];
|
||||
|
||||
if (window.user && !shouldFetchLocally) {
|
||||
var remoteDb = await window.db.getDb();
|
||||
remoteDb
|
||||
.collection('items')
|
||||
.where('createdBy', '==', window.user.uid)
|
||||
.onSnapshot(
|
||||
function (querySnapshot) {
|
||||
querySnapshot.forEach(function (doc) {
|
||||
items.push(doc.data());
|
||||
});
|
||||
log('Items fetched in ', Date.now() - t, 'ms');
|
||||
var remoteDb = window.db.getDb();
|
||||
|
||||
d.resolve(items);
|
||||
},
|
||||
function () {
|
||||
d.resolve([]);
|
||||
}
|
||||
);
|
||||
const q = query(
|
||||
collection(remoteDb, 'items'),
|
||||
where('createdBy', '==', window.user.uid)
|
||||
);
|
||||
getDocs(q)
|
||||
.then(querySnapshot => {
|
||||
querySnapshot.forEach(doc => {
|
||||
items.push(doc.data());
|
||||
});
|
||||
log(`${items.length} items fetched in `, Date.now() - t, 'ms');
|
||||
d.resolve(items);
|
||||
})
|
||||
.catch(() => {
|
||||
d.resolve([]);
|
||||
});
|
||||
} else {
|
||||
let itemIds = await this.getUserItemIds(shouldFetchLocally);
|
||||
itemIds = Object.getOwnPropertyNames(itemIds || {});
|
||||
@@ -83,8 +94,8 @@ export const itemService = {
|
||||
},
|
||||
|
||||
async setUser() {
|
||||
const remoteDb = await window.db.getDb();
|
||||
return remoteDb.doc(`users/${window.user.uid}`).set({
|
||||
const remoteDb = window.db.getDb();
|
||||
return setDoc(doc(remoteDb, `users/${window.user.uid}`), {
|
||||
items: {}
|
||||
});
|
||||
},
|
||||
@@ -112,16 +123,13 @@ export const itemService = {
|
||||
}
|
||||
|
||||
// LOGGED IN
|
||||
var remoteDb = await window.db.getDb();
|
||||
var remoteDb = window.db.getDb();
|
||||
item.createdBy = window.user.uid;
|
||||
remoteDb
|
||||
.collection('items')
|
||||
.doc(id)
|
||||
.set(item, {
|
||||
merge: true
|
||||
})
|
||||
.then(arg => {
|
||||
log('Document written', arg);
|
||||
setDoc(doc(remoteDb, `items/${id}`), item, {
|
||||
merge: true
|
||||
})
|
||||
.then(() => {
|
||||
log(`Document written`);
|
||||
d.resolve();
|
||||
})
|
||||
.catch(d.reject);
|
||||
@@ -161,14 +169,15 @@ export const itemService = {
|
||||
);
|
||||
} else {
|
||||
window.db.getDb().then(remoteDb => {
|
||||
const batch = remoteDb.batch();
|
||||
const batch = writeBatch(remoteDb);
|
||||
/* eslint-disable guard-for-in */
|
||||
for (var id in items) {
|
||||
items[id].createdBy = window.user.uid;
|
||||
batch.set(remoteDb.doc(`items/${id}`), items[id]);
|
||||
batch.update(remoteDb.doc(`users/${window.user.uid}`), {
|
||||
batch.set(doc(remoteDb, `items/${id}`), items[id]);
|
||||
batch.update(doc(remoteDb, `users/${window.user.uid}`), {
|
||||
[`items.${id}`]: true
|
||||
});
|
||||
|
||||
// Set these items on our cached user object too
|
||||
window.user.items = window.user.items || {};
|
||||
window.user.items[id] = true;
|
||||
@@ -187,14 +196,11 @@ export const itemService = {
|
||||
window.db.local.remove(id, d.resolve);
|
||||
return d.promise;
|
||||
}
|
||||
const remoteDb = await window.db.getDb();
|
||||
const remoteDb = window.db.getDb();
|
||||
log(`Starting to save item ${id}`);
|
||||
return remoteDb
|
||||
.collection('items')
|
||||
.doc(id)
|
||||
.delete()
|
||||
.then(arg => {
|
||||
log('Document removed', arg);
|
||||
return deleteDoc(doc(remoteDb, `items/${id}`))
|
||||
.then(() => {
|
||||
log('Document removed');
|
||||
})
|
||||
.catch(error => log(error));
|
||||
},
|
||||
@@ -214,13 +220,10 @@ export const itemService = {
|
||||
}
|
||||
);
|
||||
}
|
||||
const remoteDb = await window.db.getDb();
|
||||
return remoteDb
|
||||
.collection('users')
|
||||
.doc(window.user.uid)
|
||||
.update({
|
||||
[`items.${itemId}`]: true
|
||||
})
|
||||
const remoteDb = window.db.getDb();
|
||||
return updateDoc(doc(remoteDb, `users/${window.user.uid}`), {
|
||||
[`items.${itemId}`]: true
|
||||
})
|
||||
.then(arg => {
|
||||
log(`Item ${itemId} set for user`, arg);
|
||||
window.user.items = window.user.items || {};
|
||||
@@ -244,16 +247,13 @@ export const itemService = {
|
||||
}
|
||||
);
|
||||
}
|
||||
const remoteDb = await window.db.getDb();
|
||||
return remoteDb
|
||||
.collection('users')
|
||||
.doc(window.user.uid)
|
||||
.update({
|
||||
[`items.${itemId}`]: firebase.firestore.FieldValue.delete()
|
||||
})
|
||||
const remoteDb = window.db.getDb();
|
||||
return updateDoc(doc(remoteDb, `users/${window.user.uid}`), {
|
||||
[`items.${itemId}`]: deleteField()
|
||||
})
|
||||
.then(arg => {
|
||||
delete window.user.items[itemId];
|
||||
log(`Item ${itemId} unset for user`, arg);
|
||||
delete window.user.items[itemId];
|
||||
})
|
||||
.catch(error => log(error));
|
||||
},
|
||||
|
@@ -1,4 +1,4 @@
|
||||
let mainWindow = window.parent;
|
||||
let mainWindow = window.parent === window ? window.opener : window.parent;
|
||||
|
||||
function sanitizeDomNode(node) {
|
||||
const fakeNode = {
|
||||
|
@@ -1,24 +1,33 @@
|
||||
{
|
||||
"name": "Web Maker",
|
||||
"version": "6.0.0",
|
||||
"manifest_version": 2,
|
||||
"version": "6.3.0",
|
||||
"manifest_version": 3,
|
||||
"description": "Blazing fast & offline playground for your web experiments",
|
||||
"homepage_url": "https://webmaker.app",
|
||||
"permissions": ["storage", "tabs", "<all_urls>"],
|
||||
"permissions": ["storage", "tabs", "offscreen"],
|
||||
"optional_permissions": ["downloads"],
|
||||
"content_security_policy": "script-src 'self' filesystem: http://localhost:* https://localhost:* https://apis.google.com https://ajax.googleapis.com https://code.jquery.com https://cdnjs.cloudflare.com https://unpkg.com https://maxcdn.com https://cdn77.com https://maxcdn.bootstrapcdn.com https://cdn.jsdelivr.net/ https://*.stripe.com/ https://builds.framerjs.com/ https://rawgit.com https://wzrd.in https://www.gstatic.com https://semantic-ui.com https://www.google-analytics.com https://cdn.tailwindcss.com https://www.googletagmanager.com 'unsafe-eval'; object-src 'self'",
|
||||
"host_permissions": ["<all_urls>"],
|
||||
"content_security_policy": {
|
||||
"extension_pages": "script-src 'self'; object-src 'self'",
|
||||
"sandbox": "sandbox allow-scripts allow-forms allow-popups allow-modals; script-src 'self' filesystem: http://localhost:* https://localhost:* https://apis.google.com https://ajax.googleapis.com https://code.jquery.com https://cdnjs.cloudflare.com https://unpkg.com https://maxcdn.com https://cdn77.com https://maxcdn.bootstrapcdn.com https://cdn.jsdelivr.net/ https://*.stripe.com/ https://builds.framerjs.com/ https://rawgit.com https://wzrd.in https://www.gstatic.com https://semantic-ui.com https://www.google-analytics.com https://cdn.tailwindcss.com https://www.googletagmanager.com https://firebasestorage.googleapis.com https://assets.webmaker.app 'unsafe-inline' 'unsafe-eval'; child-src 'self';"
|
||||
},
|
||||
"options_ui": {
|
||||
"page": "options.html",
|
||||
"chrome_style": true
|
||||
"open_in_tab": false
|
||||
},
|
||||
"browser_action": {
|
||||
"action": {
|
||||
"default_title": "Start Web Maker",
|
||||
"default_icon": "icon-16.png"
|
||||
},
|
||||
"background": {
|
||||
"scripts": ["eventPage.js"],
|
||||
"persistent": false
|
||||
"sandbox": {
|
||||
"pages": ["indexpm.html"]
|
||||
},
|
||||
"background": {
|
||||
"service_worker": "eventPage.js",
|
||||
"type": "module"
|
||||
},
|
||||
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp4s9naKH2y92qItJCcj3deRGjjqhDfgUR2WDe34IEYEB4PGtmx/9IO4PaO49gMr82DBRuwxCg/vr9SyiVlG1/j0TApJlkaHVsRZE4EME2rbL1vzIQRE8gNB+b5L6rPl4GPX/eFqIuUe/UgQPZAadLBVxdwBdOv5ou8OY0jrLx/h0wcLVvk9d8/ELpk28Hb2LArCBd+vIngHK55Db1GMEGAyNUVzFCTg/MZ7w5fKLpA94mF2X0/Bv9sCjj7vDir24Mp5Z/Y3obTHvpzddppAE6ZEQ3fmpRkOJ3MbuaKYm9hNHs4az6XLW6Sdlv2YcIcCfvsev/WEKUZeD8KIRtRyyPQIDAQAB",
|
||||
|
||||
"icons": {
|
||||
"16": "icon-16.png",
|
||||
"48": "icon-48.png"
|
||||
|
@@ -35,7 +35,7 @@
|
||||
<body>
|
||||
<h3>
|
||||
Settings
|
||||
<span style="opacity: 0.6; font-size: 0.7em"> v6.0.0</span>
|
||||
<span style="opacity: 0.6; font-size: 0.7em"> v6.3.0</span>
|
||||
</h3>
|
||||
<form name="optionsForm">
|
||||
<label>
|
||||
|
@@ -12,7 +12,7 @@
|
||||
|
||||
<body>
|
||||
<iframe
|
||||
src="about://blank"
|
||||
src="indexpm.html"
|
||||
frameborder="0"
|
||||
id="demo-frame"
|
||||
allowfullscreen
|
||||
|
@@ -15,11 +15,6 @@ export default [
|
||||
title: 'Preact',
|
||||
img: 'assets/preact-logo.svg'
|
||||
},
|
||||
{
|
||||
id: 'tailwind2',
|
||||
title: 'Tailwind CSS 2',
|
||||
img: 'assets/tailwind-logo.svg'
|
||||
},
|
||||
{
|
||||
id: 'tailwind',
|
||||
title: 'Tailwind CSS 3',
|
||||
|
10
src/utils.js
10
src/utils.js
@@ -406,26 +406,26 @@ export function getCompleteHtml(html, css, js, item, isForExport) {
|
||||
'</head>\n' +
|
||||
'<body>\n' +
|
||||
html +
|
||||
'\n' +
|
||||
externalJs +
|
||||
'\n';
|
||||
|
||||
if (!isForExport) {
|
||||
contents +=
|
||||
'<script src="' +
|
||||
(chrome.extension
|
||||
? chrome.extension.getURL('lib/screenlog.js')
|
||||
? chrome.runtime.getURL('lib/screenlog.js')
|
||||
: `${location.origin}${
|
||||
window.DEBUG ? '' : BASE_PATH
|
||||
}/lib/screenlog.js`) +
|
||||
'"></script>';
|
||||
}
|
||||
|
||||
contents += '\n' + externalJs;
|
||||
|
||||
if (item.jsMode === JsModes.ES6) {
|
||||
contents +=
|
||||
'<script src="' +
|
||||
(chrome.extension
|
||||
? chrome.extension.getURL('lib/transpilers/babel-polyfill.min.js')
|
||||
? chrome.runtime.getURL('lib/transpilers/babel-polyfill.min.js')
|
||||
: `${location.origin}${BASE_PATH}/lib/transpilers/babel-polyfill.min.js`) +
|
||||
'"></script>';
|
||||
}
|
||||
@@ -532,7 +532,7 @@ export function prettify({ file, content, type }) {
|
||||
}
|
||||
const worker = new Worker(
|
||||
chrome.extension
|
||||
? chrome.extension.getURL('lib/prettier-worker.js')
|
||||
? chrome.runtime.getURL('lib/prettier-worker.js')
|
||||
: `${BASE_PATH !== '/' ? BASE_PATH : ''}/lib/prettier-worker.js`
|
||||
);
|
||||
worker.postMessage({ content, type });
|
||||
|
Reference in New Issue
Block a user