Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into dev/riknoll/new-blo…
Browse files Browse the repository at this point in the history
…ckly-comments
  • Loading branch information
riknoll committed Apr 30, 2024
2 parents 0ec36be + 86dfad5 commit c4f9482
Show file tree
Hide file tree
Showing 86 changed files with 33,904 additions and 152 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ webapp/public/authcode.html
webapp/public/multiplayer.html
webapp/public/kiosk.html
webapp/public/teachertool.html
webapp/public/tutorialtool.html
localtypings/blockly.d.ts
node_modules
*.sw?
Expand Down
6 changes: 6 additions & 0 deletions cli/webapps-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
"localServeWebConfigUrl": false,
"localServeEndpoint": "eval"
},
{
"name": "tutorialtool",
"buildCss": false,
"localServeWebConfigUrl": false,
"localServeEndpoint": "tt"
},
{
"name": "skillmap",
"buildCss": true,
Expand Down
16 changes: 7 additions & 9 deletions common-docs/teachertool/catalog-shared.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
{
"criteria": [
{
"id": "8F97C9A6-CF16-48B4-A84F-3105C24B20DE",
"use": "functions_have_comments",
"template": "All function definitions have comments",
"docPath": "/teachertool",
"description": "All user-defined functions in the program have comments attached to them."
},
{
"id": "D21D76A2-D9FD-4F9B-B0AC-973CB870EA78",
"use": "variable_set",
"template": "At least one custom variable is set",
"docPath": "/teachertool",
"description": "At least one user-defined variable is set to a value."
"description": "At least one user-defined variable is set to a value.",
"maxCount": 1
},
{
"id": "0173898D-8A48-4266-AAB9-CE934471A734",
"use": "variable_accessed",
"template": "At least one variable is accessed",
"docPath": "/teachertool",
"description": "At least one variable's value is read."
"description": "At least one variable's value is read.",
"maxCount": 1
},
{
"id": "7AE7EA2A-3AC8-42DC-89DB-65E3AE157156",
"use": "block_comment_used",
"template": "At least ${count} comments",
"description": "The project contains at least the specified number of comments.",
"docPath": "/teachertool",
"maxCount": 1,
"params": [
{
"name": "count",
Expand Down Expand Up @@ -62,6 +58,7 @@
"template": "At least ${count} loops used",
"docPath": "/teachertool",
"description": "The program uses at least this many loops of any kind (for, repeat, while, or for-of).",
"maxCount": 1,
"params": [
{
"name": "count",
Expand All @@ -77,6 +74,7 @@
"template": "At least ${count} custom functions exist and get called",
"docPath": "/teachertool",
"description": "At least this many user-defined functions are created and called.",
"maxCount": 1,
"params": [
{
"name": "count",
Expand Down
1 change: 1 addition & 0 deletions common-docs/teachertool/test/catalog-shared.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"template": "Ask Copilot: ${question}",
"description": "Experimental: AI outputs may not be accurate. Use with caution and always review responses.",
"docPath": "/teachertool",
"maxCount": 10,
"params": [
{
"name": "question",
Expand Down
8 changes: 7 additions & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,12 @@ const kiosk = createWebappTasks("kiosk");

const teacherTool = createWebappTasks("teachertool");

/********************************************************
Tutorial Tool
*********************************************************/

const tutorialTool = createWebappTasks("tutorialtool");

/********************************************************
Webapp build wrappers
*********************************************************/
Expand All @@ -604,7 +610,7 @@ const maybeUpdateWebappStrings = () => {

const maybeBuildWebapps = () => {
if (!shouldBuildWebapps()) return noop;
return gulp.parallel(skillmap, authcode, multiplayer, kiosk, teacherTool);
return gulp.parallel(skillmap, authcode, multiplayer, kiosk, teacherTool, tutorialTool);
}

/********************************************************
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pxt-core",
"version": "10.0.18",
"version": "10.0.20",
"description": "Microsoft MakeCode provides Blocks / JavaScript / Python tools and editors",
"keywords": [
"TypeScript",
Expand Down
1 change: 0 additions & 1 deletion pxtlib/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,4 @@ namespace pxt.shell {
export function setToolboxAnimation(): void {
pxt.storage.setLocal("toolboxanimation", "1");
}

}
64 changes: 64 additions & 0 deletions pxtservices/backendRequests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { ErrorCode } from "./constants";
import { logError } from "./loggingService";

export async function fetchJsonDocAsync<T = any>(url: string): Promise<T | undefined> {
try {
const response = await fetch(url, {
cache: "no-cache",
});
if (!response.ok) {
throw new Error("Unable to fetch the json file");
} else {
const json = await response.json();
return json;
}
} catch (e) {
logError(ErrorCode.fetchJsonDocAsync, e);
}

return undefined;
}

export async function getProjectTextAsync(projectId: string): Promise<pxt.Cloud.JsonText | undefined> {
try {
const projectTextUrl = `${pxt.Cloud.apiRoot}/${projectId}/text`;
const response = await fetch(projectTextUrl);
if (!response.ok) {
throw new Error("Unable to fetch the project details");
} else {
const projectText = await response.json();
return projectText;
}
} catch (e) {
logError(ErrorCode.getProjectTextAsync, e);
}

return undefined;
}

export async function getProjectMetaAsync(projectId: string): Promise<pxt.Cloud.JsonScript | undefined> {
try {
const projectMetaUrl = `${pxt.Cloud.apiRoot}/${projectId}`;
const response = await fetch(projectMetaUrl);
if (!response.ok) {
throw new Error("Unable to fetch the project meta information");
} else {
const projectMeta = await response.json();
return projectMeta;
}
} catch (e) {
logError(ErrorCode.getProjectMetaAsync, e);
}

return undefined;
}

export async function downloadTargetConfigAsync(): Promise<pxt.TargetConfig | undefined> {
try {
return await pxt.targetConfigAsync();
} catch (e) {
logError(ErrorCode.downloadTargetConfigAsync, e);
}

return undefined;
}
6 changes: 6 additions & 0 deletions pxtservices/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export enum ErrorCode {
downloadTargetConfigAsync = "downloadTargetConfigAsync",
fetchJsonDocAsync = "fetchJsonDocAsync",
getProjectTextAsync = "getProjectTextAsync",
getProjectMetaAsync = "getProjectMetaAsync",
}
2 changes: 1 addition & 1 deletion pxtservices/iframeEmbeddedClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class IFrameEmbeddedClient {
else if ((window as any).acquireVsCodeApi) {
(window as any).acquireVsCodeApi().postMessage(message)
}
else {
else if (window.parent && window.parent !== window) {
window.parent.postMessage(message, "*");
}
}
Expand Down
49 changes: 49 additions & 0 deletions pxtservices/loggingService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
let tickEvent = "pxt.error";

const timestamp = () => {
const time = new Date();
const hours = padTime(time.getHours());
const minutes = padTime(time.getMinutes());
const seconds = padTime(time.getSeconds());

return `[${hours}:${minutes}:${seconds}]`;
};

const padTime = (time: number) => ("0" + time).slice(-2);

export const logError = (errorCode: string, message?: any, data: pxt.Map<string | number> = {}) => {
let dataObj = { ...data };
if (message) {
if (typeof message === "object") {
dataObj = { ...dataObj, ...message };
// Look for non-enumerable properties found on Error objects
["message", "stack", "name"].forEach(key => {
if (message[key]) {
dataObj[key] = message[key];
}
});
} else {
dataObj.message = message;
}
}
pxt.tickEvent(tickEvent, {
...dataObj,
errorCode,
});
console.error(timestamp(), errorCode, dataObj);
};

export const logInfo = (message: any) => {
console.log(timestamp(), message);
};

export const logDebug = (message: any, data?: any) => {
if (pxt.BrowserUtils.isLocalHost() || pxt.options.debug) {
console.log(timestamp(), message, data);
}
};


export const setTickEvent = (event: string) => {
tickEvent = event;
}
20 changes: 20 additions & 0 deletions react-common/components/profile/SignInButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from "react";
import { Button } from "../controls/Button";
import { classList } from "../util";

interface IProps {
onSignInClick: () => void;
className?: string;
}

export const SignInButton: React.FC<IProps> = ({ onSignInClick, className }) => {
return (
<Button
className={classList(className, "sign-in-button")}
rightIcon="xicon cloud-user large"
title={lf("Sign In")}
label={lf("Sign In")}
onClick={onSignInClick}
/>
);
};
59 changes: 59 additions & 0 deletions react-common/components/profile/UserAvatarDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from "react";
import { MenuDropdown, MenuItem } from "../controls/MenuDropdown";
import { classList } from "../util";

interface IProps {
userProfile: pxt.auth.UserProfile;
title: string;

onSignOutClick?: () => void;
className?: string;
items?: MenuItem[];
}

export const UserAvatarDropdown: React.FC<IProps> = (props) => {
const { userProfile, title, items, onSignOutClick, className } = props;

const avatarUrl = userProfile?.idp?.pictureUrl ?? encodedAvatarPic(userProfile);

const avatarElem = <UserAvatar avatarPicUrl={avatarUrl} />;
const initialsElem = <UserInitials userProfile={userProfile} />;

const allItems = items ? items.slice() : [];

if (onSignOutClick) {
allItems.unshift({
id: "signout",
title: lf("Sign Out"),
label: lf("Sign Out"),
onClick: onSignOutClick
});
}

return (
<MenuDropdown
className={classList("user-avatar-dropdown", className)}
title={title}
label={avatarUrl ? avatarElem : initialsElem}
items={allItems}
/>
);
};

const UserInitials = (props: { userProfile: pxt.auth.UserProfile }) => (
<span>
<div className="user-avatar-initials" aria-hidden="true">{pxt.auth.userInitials(props.userProfile)}</div>
</span>
);

const UserAvatar = (props: { avatarPicUrl: string }) => (
<div className="user-avatar-image">
<img src={props.avatarPicUrl} alt={lf("Profile Image")} referrerPolicy="no-referrer" aria-hidden="true" />
</div>
);

function encodedAvatarPic(user: pxt.auth.UserProfile): string {
const type = user?.idp?.picture?.mimeType;
const encodedImg = user?.idp?.picture?.encoded;
return type && encodedImg ? `data:${type};base64,${encodedImg}` : "";
}
19 changes: 19 additions & 0 deletions react-common/styles/profile/SignInButton.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.common-button.sign-in-button {
padding-left: 0.75rem;
padding-right: 0.75rem;

.common-button-label {
font-family: Segoe UI, Tahoma, Geneva, Verdana;
font-weight: 500;
}
}

@media @tabletAndBelow {
.common-button.sign-in-button {
padding-left: 0.25rem;

.common-button-label {
display: none;
}
}
}
25 changes: 25 additions & 0 deletions react-common/styles/profile/UserAvatarDropdown.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.user-avatar-image {
display: flex;
align-items: center;
justify-content: center;
height: 100%;

img {
border: solid 2px @buttonMenuTextColor;
border-radius: 100%;
width: 2.5rem;
height: 2.5rem;
}
}

.user-avatar-initials {
display: flex;
align-items: center;
justify-content: center;
border: solid 2px @buttonMenuTextColor;
border-radius: 100%;
width: 2.5rem;
height: 2.5rem;

background-color: var(--pxt-headerbar-accent);
}
3 changes: 3 additions & 0 deletions react-common/styles/profile/profile.less
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
@import "./SignInButton.less";
@import "./UserAvatarDropdown.less";

.user-profile {
display: flex;
flex-direction: row;
Expand Down
Loading

0 comments on commit c4f9482

Please sign in to comment.