Skip to content

Commit edc54da

Browse files
authored
Merge branch 'develop' into feat/privatesketch
2 parents eafb1aa + 1b7766e commit edc54da

File tree

11 files changed

+89
-28
lines changed

11 files changed

+89
-28
lines changed

client/modules/IDE/hooks/useHandleMessageEvent.js

+55-21
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,64 @@ import { stopSketch, expandConsole } from '../actions/ide';
66
export default function useHandleMessageEvent() {
77
const dispatch = useDispatch();
88

9+
const safeStringify = (
10+
obj,
11+
depth = 0,
12+
maxDepth = 10,
13+
seen = new WeakMap()
14+
) => {
15+
if (typeof obj !== 'object' || obj === null) return obj;
16+
17+
if (depth >= maxDepth) {
18+
if (seen.has(obj)) return '[Circular Reference]';
19+
}
20+
21+
seen.set(obj, true);
22+
23+
return Array.isArray(obj)
24+
? obj.map((item) => safeStringify(item, depth + 1, maxDepth, seen))
25+
: Object.fromEntries(
26+
Object.entries(obj).map(([key, value]) => [
27+
key,
28+
safeStringify(value, depth + 1, maxDepth, seen)
29+
])
30+
);
31+
};
32+
933
const handleMessageEvent = (data) => {
34+
if (!data || typeof data !== 'object') return;
1035
const { source, messages } = data;
11-
if (source === 'sketch' && Array.isArray(messages)) {
12-
const decodedMessages = messages.map((message) => Decode(message.log));
13-
decodedMessages.every((message, index, arr) => {
14-
const { data: args } = message;
15-
let hasInfiniteLoop = false;
16-
Object.keys(args).forEach((key) => {
17-
if (
18-
typeof args[key] === 'string' &&
19-
args[key].includes('Exiting potential infinite loop')
20-
) {
21-
dispatch(stopSketch());
22-
dispatch(expandConsole());
23-
hasInfiniteLoop = true;
24-
}
25-
});
26-
if (hasInfiniteLoop) {
27-
return false;
28-
}
29-
return true;
30-
});
31-
dispatch(dispatchConsoleEvent(decodedMessages));
36+
if (source !== 'sketch' || !Array.isArray(messages)) return;
37+
38+
const decodedMessages = messages.map((message) => {
39+
try {
40+
const decoded = Decode(message.log) ?? '[Unknown Message]'; // Ensure decoding works
41+
return safeStringify(decoded);
42+
} catch (error) {
43+
console.error('Error decoding message:', error);
44+
return { error: 'Failed to decode message' };
45+
}
46+
});
47+
48+
// Detect infinite loop warnings
49+
const hasInfiniteLoop = decodedMessages.some(
50+
(message) =>
51+
message?.data &&
52+
Object.values(message.data).some(
53+
(arg) =>
54+
typeof arg === 'string' &&
55+
arg.includes('Exiting potential infinite loop')
56+
)
57+
);
58+
59+
if (hasInfiniteLoop) {
60+
dispatch(stopSketch());
61+
dispatch(expandConsole());
62+
return;
3263
}
64+
65+
dispatch(dispatchConsoleEvent(decodedMessages));
3366
};
67+
3468
return handleMessageEvent;
3569
}

client/modules/IDE/hooks/useP5Version.jsx

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import PropTypes from 'prop-types';
88
// JSON.stringify([...document.querySelectorAll('._132722c7')].map(n => n.innerText), null, 2)
99
// TODO: use their API for this to grab these at build time?
1010
export const p5Versions = [
11+
'2.0.1',
1112
'2.0.0',
1213
'1.11.5',
1314
'1.11.4',

client/utils/codemirror-search.js

+6
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,12 @@ function doSearch(cm, rev, persistent, immediate, ignoreQuery) {
515515
startSearch(cm, state, q);
516516
findNext(cm, rev);
517517
}
518+
cm.on('change', function () {
519+
var state = getSearchState(cm);
520+
if (state.query) {
521+
startSearch(cm, state, state.queryText);
522+
}
523+
});
518524
} else {
519525
dialog(cm, queryDialog, 'Search for:', q, function (query) {
520526
if (query && !state.query)

client/utils/reduxFormUtils.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ export function validateSettings(formProps) {
6262

6363
export function validateLogin(formProps) {
6464
const errors = {};
65-
if (!formProps.email) {
66-
errors.email = i18n.t('ReduxFormUtils.errorEmptyEmail');
65+
if (!formProps.email && !formProps.username) {
66+
errors.email = i18n.t('ReduxFormUtils.errorEmptyEmailorUserName');
6767
}
6868
if (!formProps.password) {
6969
errors.password = i18n.t('ReduxFormUtils.errorEmptyPassword');

contributor_docs/installation.md

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
Follow these instructions to set up your development environment, which you need to do before you start contributing code to this project.
44

5+
There are two ways to install the project locally:
6+
7+
Manual Installation is great if you're comfortable managing dependencies like Node.js and MongoDB directly on your machine. It's more hands-on and gives you finer control, which can be helpful for debugging or learning how each part works.
8+
9+
Docker Installation is ideal if you want a faster setup with all dependencies (Node, MongoDB, etc.) isolated in containers. This avoids version conflicts and works consistently across environments especially helpful if you're new to backend setup or don't want to alter your local setup.
10+
511
## Manual Installation
612

713
_Note_: The installation steps assume you are using a Unix-like shell. If you are using Windows, you will need to use `copy` instead of `cp`.

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "p5.js-web-editor",
3-
"version": "2.16.2",
3+
"version": "2.16.3",
44
"description": "The web editor for p5.js.",
55
"scripts": {
66
"clean": "rimraf dist",

server/config/passport.js

+11
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ passport.use(
5454
const isMatch = await user.comparePassword(password);
5555

5656
if (isMatch) {
57+
user.lastLoginTimestamp = Date.now();
58+
await user.save();
59+
5760
return done(null, user);
5861
} else { // eslint-disable-line
5962
return done(null, false, { msg: 'Invalid email or password' });
@@ -88,6 +91,8 @@ passport.use(
8891
}
8992

9093
keyDocument.lastUsedAt = Date.now();
94+
user.lastLoginTimestamp = Date.now();
95+
9196
await user.save();
9297
return done(null, user);
9398
} catch (err) {
@@ -140,6 +145,9 @@ passport.use(
140145
} else if (existingUser.banned) {
141146
return done(null, false, { msg: accountSuspensionMessage });
142147
}
148+
existingUser.lastLoginTimestamp = Date.now();
149+
await existingUser.save();
150+
143151
return done(null, existingUser);
144152
}
145153

@@ -239,6 +247,9 @@ passport.use(
239247
} else if (existingUser.banned) {
240248
return done(null, false, { msg: accountSuspensionMessage });
241249
}
250+
existingUser.lastLoginTimestamp = Date.now();
251+
await existingUser.save();
252+
242253
return done(null, existingUser);
243254
}
244255

server/models/user.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ const userSchema = new Schema(
8282
enum: ['none', 'essential', 'all'],
8383
default: 'none'
8484
},
85-
banned: { type: Boolean, default: false }
85+
banned: { type: Boolean, default: false },
86+
lastLoginTimestamp: { type: Date }
8687
},
8788
{ timestamps: true, usePushEach: true }
8889
);

server/server.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ app.use(
9494
name: 'sessionId',
9595
cookie: {
9696
httpOnly: true,
97-
secure: false
97+
secure: false,
98+
maxAge: 1000 * 60 * 60 * 24 * 28 // 4 weeks in milliseconds
9899
},
99100
store: new MongoStore({
100101
clientPromise,

translations/locales/en-US/translations.json

+1
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@
334334
"ReduxFormUtils": {
335335
"errorInvalidEmail": "Please enter a valid email address",
336336
"errorEmptyEmail": "Please enter an email",
337+
"errorEmptyEmailorUserName": "Please enter an email or username",
337338
"errorPasswordMismatch": "Passwords must match",
338339
"errorEmptyPassword": "Please enter a password",
339340
"errorShortPassword": "Password must be at least 6 characters",

0 commit comments

Comments
 (0)