Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCV first pass #10355

Merged
merged 34 commits into from
Feb 6, 2025
Merged
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d31eca8
plumbing for showing the feedback iframe
srietkerk Dec 12, 2024
f55fca2
add https flag to the cli to use https when debugging ocv iframe
srietkerk Dec 19, 2024
c705f96
Merge branch 'master' of https://github.com/microsoft/pxt into srietk…
srietkerk Dec 20, 2024
87236db
more config, removed functions, changed height and width of iframe
srietkerk Dec 20, 2024
0593552
using different modal for the feedback
srietkerk Jan 14, 2025
5259143
added feedback menu item to editor menu
srietkerk Jan 14, 2025
9354dc2
got rid of iframe border
srietkerk Jan 14, 2025
a764d25
some small changes and styling
srietkerk Jan 25, 2025
6446b2a
added comments
srietkerk Jan 27, 2025
2120588
move app id to configs
srietkerk Jan 27, 2025
4da2fdc
fix onclose reference
srietkerk Jan 27, 2025
05d4be7
moved frame url to configs
srietkerk Jan 27, 2025
816c662
remove tutorial completion feedback for now
srietkerk Jan 27, 2025
8336f42
use emoji instead of sui icon
srietkerk Jan 27, 2025
bab52a2
merged master
srietkerk Jan 27, 2025
f905236
update iframeelement variable in event listener
srietkerk Jan 28, 2025
4a4eb79
add space to less file
srietkerk Jan 28, 2025
6c86476
another iframe element var change
srietkerk Jan 28, 2025
cd43ef9
moved height, width to less file, fixed some styling
srietkerk Jan 28, 2025
be206b5
remove redundant jsdoc types
srietkerk Jan 28, 2025
2bb7a05
use pxt log functions, fix spacing in the event listener
srietkerk Jan 28, 2025
b209592
move emoji out of lf
srietkerk Jan 28, 2025
9438c18
use lf for the displayed strings for the rating
srietkerk Jan 28, 2025
158939c
types added
srietkerk Jan 30, 2025
c714b42
move types to localtypings and wrap them in a namespace
srietkerk Jan 30, 2025
728ebd8
change to using the comment bubble icon
srietkerk Jan 30, 2025
69ec94d
added types
srietkerk Jan 31, 2025
9dc5367
actually using feedback kind
srietkerk Feb 4, 2025
3e8c6ba
rename feedback app theme field
srietkerk Feb 4, 2025
83b45f8
move iframe init and posting code to helper function
srietkerk Feb 4, 2025
980020a
use optional chaining for ondismiss call
srietkerk Feb 4, 2025
3b6a3ff
move current theme into send update theme function, update theme options
srietkerk Feb 4, 2025
943c493
move questions for rating config to a function
srietkerk Feb 4, 2025
36c823c
move appid, frame url to apptheme
srietkerk Feb 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
plumbing for showing the feedback iframe
  • Loading branch information
srietkerk committed Dec 12, 2024
commit d31eca89f0a8a2de1f01c2266c0edfc17936d20a
8 changes: 6 additions & 2 deletions cli/server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as fs from 'fs';
import * as path from 'path';
import * as http from 'http';
import * as https from 'https';
import * as url from 'url';
import * as querystring from 'querystring';
import * as nodeutil from './nodeutil';
@@ -949,7 +950,7 @@ export function serveAsync(options: ServeOptions) {
const wsServerPromise = initSocketServer(serveOptions.wsPort, serveOptions.hostname);
if (serveOptions.serial)
initSerialMonitor();

// https.createServer
const server = http.createServer(async (req, res) => {
const error = (code: number, msg: string = null) => {
res.writeHead(code, { "Content-Type": "text/plain" })
@@ -1362,7 +1363,10 @@ export function serveAsync(options: ServeOptions) {

const serverPromise = new Promise<void>((resolve, reject) => {
server.on("error", reject);
server.listen(serveOptions.port, serveOptions.hostname, () => resolve());
server.listen(serveOptions.port, serveOptions.hostname, () => {
console.log(`Server listening on port ${serveOptions.port}`);
return resolve()
});
});

return Promise.all([wsServerPromise, serverPromise])
1 change: 1 addition & 0 deletions localtypings/pxteditor.d.ts
Original file line number Diff line number Diff line change
@@ -1047,6 +1047,7 @@ declare namespace pxt.editor {
showLanguagePicker(): void;
showShareDialog(title?: string, kind?: "multiplayer" | "vscode" | "share"): void;
showAboutDialog(): void;
showFeedbackDialog(): void;
showTurnBackTimeDialogAsync(): Promise<void>;

showLoginDialog(continuationHash?: string): void;
169 changes: 169 additions & 0 deletions react-common/components/controls/Feedback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
export const feedbackConfig: any = {
feedbackUiType: "Modal",
isDisplayed: true,
isEmailCollectionEnabled: false, // to enable email collection
isFileUploadEnabled: false, // to enable file upload function
isScreenshotEnabled: true, // to enable screenshot
isScreenRecordingEnabled: false, // to enable screen recording
invokeOnDismissOnEsc: true,
}

export const themeOptions = {
baseTheme: "PublisherLightTheme",
}

export const initfeedbackOptions: any = {
appId: 50315,
ageGroup: "Undefined",
authenticationType: "Unauthenticated",
clientName: "MakeCode",
feedbackConfig: feedbackConfig,
isProduction: false,
themeOptions: themeOptions,
// telemetry - will likely want this
// userId
// userData

}
export interface FeedbackRequestEventPayload<T> {
Event: string
EventArgs: string
}
type FeedbackRequestPayloadType = FeedbackRequestEventPayload<any>

export interface FeedbackResponseEventPayload<T> {
event: string
data: T
error?: any
}

export const FEEDBACK_FRAME_ID = 'feedback-iframe'

let feedbackData = initfeedbackOptions

export const feedbackCallbackEventListener = (event: MessageEvent<FeedbackRequestPayloadType>) => {
if (event.data) {
const payload: FeedbackRequestPayloadType = event.data
switch (payload.Event) {
case 'InAppFeedbackInitOptions': //This is required to initialise feedback
sendFeedbackInitOptions()
break
// case 'InAppFeedbackScreenshot': //This is only needed if you want to have screenshot dynamically provided by host
// sendInAppFeedbackScreenshot()
// break
case 'InAppFeedbackOnError': //Invoked when an error occurrs on feedback submission
console.log('Error Message: ', payload.EventArgs)
break
case 'InAppFeedbackAttachDiagnosticLogs': //Invoked when submit button is clicked and feedbackData.feedbackConfig.diagnosticsConfig is configured
console.log('Host App can send diagnostics to a third party app data using', payload.EventArgs)
break
// case 'InAppFeedbackGetContextData': //This is needed if you want to pass in content samples to be displayed on the form
// sendInAppFeedbackGetContextData()
// break
case 'InAppFeedbackSetCurrentPage': //To follow the feedback Page number
console.log('pageName: ', payload.EventArgs)
break
case 'InAppFeedbackSetSubmitButtonState': //To follow the feedback Submit Button State
console.log('submitState: ', payload.EventArgs)
break
case 'InAppFeedbackInitializationComplete': //Invoked when feedback form is fully initialised and displays error/warning if any
console.log('InAppFeedbackInitializationComplete: ', payload.EventArgs)
break
case 'InAppFeedbackOnSuccess': //Invoked when feedback submission is successful
console.log('InAppFeedbackOnSuccess: ', payload.EventArgs)
break
case 'InAppFeedbackDismissWithResult': //Invoked when feedback is dismissed
console.log('InAppFeedbackDismissWithResult: ', payload.EventArgs)
break
case 'InAppFeedbackExtractFeedbackDataForHost': //Invoked when feedback is dismissed
console.log('InAppFeedbackExtractFeedbackDataForHost: ', payload.EventArgs)
break
}
}
}

let currentTheme = ''

export const sendUpdateTheme = () => {
type FeedbackResponsePayloadType = FeedbackResponseEventPayload<any>
if (currentTheme == 'WindowsDark') {
currentTheme = 'WindowsLight'
} else {
currentTheme = 'WindowsDark'
}
const response: FeedbackResponsePayloadType = {
event: 'OnFeedbackHostAppThemeChanged',
data: {
baseTheme: currentTheme,
},
}
const iFrameEle = document.getElementById(FEEDBACK_FRAME_ID) as HTMLIFrameElement
iFrameEle!.contentWindow!.postMessage(response, 'https://admin-ignite.microsoft.com')
}

//private functions
const sendFeedbackInitOptions = () => {
type FeedbackResponsePayloadType = FeedbackResponseEventPayload<any>
feedbackData.callbackFunctions = undefined
// feedbackData.feedbackConfig!.diagnosticsConfig!.attachDiagnostics = undefined
console.log("got the message to init");
let response: FeedbackResponsePayloadType = {
event: 'InAppFeedbackInitOptions',
data: feedbackData,
}
response = JSON.parse(JSON.stringify(response))
const iFrameEle = document.getElementById(FEEDBACK_FRAME_ID) as HTMLIFrameElement
iFrameEle!.contentWindow!.postMessage(response, 'https://admin-ignite.microsoft.com')
}

// const sendInAppFeedbackScreenshot = () => {
// type FeedbackResponsePayloadType = FeedbackResponseEventPayload<any>
// const response: FeedbackResponsePayloadType = {
// event: 'InAppFeedbackScreenshot',
// data: {
// providedScreenshotType: 'DynamicallyProvided',
// screenshotImageFormat: 'jpeg',
// screenshotBase64: SampleImage,
// },
// }
// const iFrameEle = document.getElementById(FEEDBACK_FRAME_ID) as HTMLIFrameElement
// iFrameEle!.contentWindow!.postMessage(response, 'https://admin-ignite.microsoft.com')
// }

// const sendInAppFeedbackGetContextData = () => {
// type FeedbackResponsePayloadType = FeedbackResponseEventPayload<any>
// const file1 = new File([SampleImage], 'sample file 1.txt', {
// type: 'text/plain',
// })
// const file2 = new File([SampleImage], 'sample file 2.txt', {
// type: 'text/plain',
// })

// const response: FeedbackResponsePayloadType = {
// event: 'InAppFeedbackGetContextData',
// data: [
// {
// fileName: 'sample file 1.png',
// fileType: 'png',
// fileDataBase64: SampleImage,
// },
// ],
// }
// const iFrameEle = document.getElementById(FEEDBACK_FRAME_ID) as HTMLIFrameElement
// iFrameEle!.contentWindow!.postMessage(response, 'https://admin-ignite.microsoft.com')
// }


export const Feedback = () => {
const appId = 50315;
return (
<iframe
title="feedback-demo"
height="600px" // You can change this according to your host app requirement
width="400px" // You can change this according to your host app requirement
id={FEEDBACK_FRAME_ID}
src={`https://admin-ignite.microsoft.com/centrohost?appname=ocvfeedback&feature=host-ocv-inapp-feedback&platform=web&appId=${appId}#/hostedpage`}
allow="display-capture;" // This is needed if you want to use the native screenshot/screen recording feature
sandbox="allow-scripts allow-same-origin allow-forms allow-popups" />
)
}
4 changes: 4 additions & 0 deletions webapp/src/app.tsx
Original file line number Diff line number Diff line change
@@ -4497,6 +4497,10 @@ export class ProjectView
dialogs.showAboutDialogAsync(this);
}

showFeedbackDialog(): void {
dialogs.showFeedbackDialogAsync();
}

async showTurnBackTimeDialogAsync() {
let simWasRunning = this.isSimulatorRunning();
if (simWasRunning) {
9 changes: 9 additions & 0 deletions webapp/src/dialogs.tsx
Original file line number Diff line number Diff line change
@@ -18,9 +18,18 @@ import { invalidate } from "./data";

import IProjectView = pxt.editor.IProjectView;
import ImportFileOptions = pxt.editor.ImportFileOptions;
import { Modal } from "../../react-common/components/controls/Modal";
import { Feedback } from "../../react-common/components/controls/Feedback";

let dontShowDownloadFlag = false;

export function showFeedbackDialogAsync() {
return core.confirmAsync({
header: lf("Give Feedback"),
jsx: <Feedback/>
})
}

export function showAboutDialogAsync(projectView: IProjectView) {
const compileService = pxt.appTarget.compileService;
const githubUrl = pxt.appTarget.appTheme.githubUrl;
7 changes: 7 additions & 0 deletions webapp/src/projects.tsx
Original file line number Diff line number Diff line change
@@ -239,6 +239,7 @@ export class ProjectSettingsMenu extends data.Component<ProjectSettingsMenuProps
this.showResetDialog = this.showResetDialog.bind(this);
this.showReportAbuse = this.showReportAbuse.bind(this);
this.showAboutDialog = this.showAboutDialog.bind(this);
this.showFeedbackDialog = this.showFeedbackDialog.bind(this)
this.signOutGithub = this.signOutGithub.bind(this);
}

@@ -278,6 +279,11 @@ export class ProjectSettingsMenu extends data.Component<ProjectSettingsMenuProps
this.props.parent.showAboutDialog();
}

showFeedbackDialog() {
pxt.tickEvent("home.feedback");
this.props.parent.showFeedbackDialog();
}

signOutGithub() {
pxt.tickEvent("home.github.signout");
this.props.parent.signOutGithub();
@@ -310,6 +316,7 @@ export class ProjectSettingsMenu extends data.Component<ProjectSettingsMenuProps
{reportAbuse ? <sui.Item role="menuitem" icon="warning circle" text={lf("Report Abuse...")} onClick={this.showReportAbuse} /> : undefined}
<sui.Item role="menuitem" icon='sign out' text={lf("Reset")} onClick={this.showResetDialog} />
<sui.Item role="menuitem" text={lf("About...")} onClick={this.showAboutDialog} />
<sui.Item role="menuitem" text={lf("Give Feedback")} onClick={this.showFeedbackDialog} />
{targetTheme.feedbackUrl ? <a className="ui item" href={targetTheme.feedbackUrl} role="menuitem" title={lf("Give Feedback")} target="_blank" rel="noopener noreferrer" >{lf("Give Feedback")}</a> : undefined}
</sui.DropdownMenu>;
}