Skip to content

Refactor frontend part 7 #2846

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

Merged
merged 44 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
f337e05
Improve useRef type arguments for HTML elements
RichDom2185 Jun 18, 2023
17b9bff
Use named imports for React hooks
RichDom2185 Jun 18, 2023
8324d10
Refactor MobileWorkspace
RichDom2185 Jun 18, 2023
8928b9e
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 17, 2024
8cf7a0e
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 17, 2024
1c0750c
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 18, 2024
238f30d
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 18, 2024
4059f27
Simplify classnames call
RichDom2185 Mar 18, 2024
1581227
Remove unnecessary braces around string attributes
RichDom2185 Mar 18, 2024
6257789
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 21, 2024
08fcde6
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 22, 2024
10770f0
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 23, 2024
dc5eae0
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 23, 2024
195955b
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 24, 2024
93ed217
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 24, 2024
7a55c4c
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 24, 2024
d1230e7
Merge branch 'master' of https://github.com/source-academy/frontend i…
RichDom2185 Mar 26, 2024
33faf93
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 26, 2024
861b254
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 26, 2024
cc0b5c8
Merge branch 'master' into fe-refactor-7
RichDom2185 Mar 26, 2024
eb3f2ad
Deduplicate dependencies
RichDom2185 Mar 26, 2024
7bd0de5
Merge branch 'master' into fe-refactor-7
RichDom2185 Apr 1, 2024
56a6f1a
Merge branch 'master' into fe-refactor-7
RichDom2185 Apr 6, 2024
41f2bff
Merge branch 'master' into fe-refactor-7
RichDom2185 Apr 10, 2024
3e0d594
Merge branch 'master' into fe-refactor-7
RichDom2185 Apr 13, 2024
6969132
Create `useTokens` hook
RichDom2185 Apr 13, 2024
2563d2e
Update default throw behavior for `useTokens`
RichDom2185 Apr 13, 2024
95647b8
Render SICP chatbot only when logged in
RichDom2185 Apr 13, 2024
e678868
Fix overloaded types for `useTokens`
RichDom2185 Apr 13, 2024
dfeb9da
Use `useTokens` where applicable
RichDom2185 Apr 13, 2024
06b21ab
Refactor SICP chatbox
RichDom2185 Apr 13, 2024
e296979
Refactor SICP chatbox further
RichDom2185 Apr 13, 2024
ebc015d
Reorganize SICP chatbot files
RichDom2185 Apr 13, 2024
85f914a
Refactor chat completion logic
RichDom2185 Apr 13, 2024
b8cf72b
Refactor SICP chat box component
RichDom2185 Apr 13, 2024
cc0052f
Decouple rendering logic from chat completion logic
RichDom2185 Apr 13, 2024
dc5d15c
Fix whitespace issue
RichDom2185 Apr 13, 2024
57f97a3
Fix filename capitalization
RichDom2185 Apr 13, 2024
1ecec53
Merge branch 'master' into fe-refactor-7
RichDom2185 Apr 13, 2024
e54e635
Remove duplicated badge code
RichDom2185 Apr 13, 2024
1f53b0a
Merge branch 'master' into fe-refactor-7
RichDom2185 Apr 13, 2024
4cc27cb
Merge branch 'master' of https://github.com/source-academy/frontend i…
RichDom2185 Apr 13, 2024
8f50bb2
Fix double request
RichDom2185 Apr 13, 2024
0106e40
Block chat input when loading response
RichDom2185 Apr 13, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const TextAreaContent: React.FC<TextAreaContentProps> = props => {
const makeEditingTextarea = () => (
<Textarea
autoFocus={true}
className={'editing-textarea'}
className="editing-textarea"
onChange={handleEditAssessment}
onBlur={saveEditAssessment}
value={fieldValue}
Expand Down
114 changes: 44 additions & 70 deletions src/commons/mobileWorkspace/MobileWorkspace.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FocusStyleManager } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { Ace } from 'ace-builds';
import React from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { DraggableEvent } from 'react-draggable';
import { useMediaQuery } from 'react-responsive';

Expand Down Expand Up @@ -30,16 +30,16 @@ export type MobileWorkspaceProps = {
const MobileWorkspace: React.FC<MobileWorkspaceProps> = props => {
const isAndroid = /Android/.test(navigator.userAgent);
const isPortrait = useMediaQuery({ orientation: 'portrait' });
const [draggableReplPosition, setDraggableReplPosition] = React.useState({ x: 0, y: 0 });
const [draggableReplPosition, setDraggableReplPosition] = useState({ x: 0, y: 0 });

// For disabling draggable Repl when in stepper tab
const [isDraggableReplDisabled, setIsDraggableReplDisabled] = React.useState(false);
const [isDraggableReplDisabled, setIsDraggableReplDisabled] = useState(false);

// Get rid of the focus border on blueprint components
FocusStyleManager.onlyShowFocusOnTabs();

// Handles the panel height when the mobile top controlbar is rendered in the Assessment Workspace
React.useEffect(() => {
useEffect(() => {
if (props.mobileSideContentProps.workspaceLocation === 'assessment') {
document.documentElement.style.setProperty(
'--mobile-panel-height',
Expand All @@ -59,7 +59,7 @@ const MobileWorkspace: React.FC<MobileWorkspaceProps> = props => {
* soft keyboard on Android devices. This is due to the viewport height changing when the soft
* keyboard is up on Android devices. IOS devices are not affected.
*/
React.useEffect(() => {
useEffect(() => {
if (isPortrait && isAndroid) {
document.documentElement.style.setProperty('overflow', 'auto');
const metaViewport = document.querySelector('meta[name=viewport]');
Expand All @@ -83,50 +83,38 @@ const MobileWorkspace: React.FC<MobileWorkspaceProps> = props => {
};
}, [isPortrait, isAndroid]);

const [targetKeyboardInput, setTargetKeyboardInput] = React.useState<Ace.Editor | null>(null);
const [targetKeyboardInput, setTargetKeyboardInput] = useState<Ace.Editor | null>(null);

const clearTargetKeyboardInput = () => setTargetKeyboardInput(null);

const enableMobileKeyboardForEditor = (props: EditorContainerProps): EditorContainerProps => {
const onFocus = (event: any, editor?: Ace.Editor) => {
if (props.onFocus) {
props.onFocus(event, editor);
}
if (!editor) {
return;
}
setTargetKeyboardInput(editor);
};
const onBlur = (event: any, editor?: Ace.Editor) => {
if (props.onBlur) {
props.onBlur(event, editor);
}
clearTargetKeyboardInput();
};
return {
...props,
onFocus,
onBlur
onFocus: (event, editor?) => {
props.onFocus?.(event, editor);
if (!editor) {
return;
}
setTargetKeyboardInput(editor);
},
onBlur: (event, editor?) => {
props.onBlur?.(event, editor);
clearTargetKeyboardInput();
}
};
};

const enableMobileKeyboardForRepl = (props: ReplProps): ReplProps => {
const onFocus = (editor: Ace.Editor) => {
if (props.onFocus) {
props.onFocus(editor);
}
setTargetKeyboardInput(editor);
};
const onBlur = () => {
if (props.onBlur) {
props.onBlur();
}
clearTargetKeyboardInput();
};
return {
...props,
onFocus,
onBlur
onFocus: editor => {
props.onFocus?.(editor);
setTargetKeyboardInput(editor);
},
onBlur: () => {
props.onBlur?.();
clearTargetKeyboardInput();
}
};
};

Expand Down Expand Up @@ -184,13 +172,11 @@ const MobileWorkspace: React.FC<MobileWorkspaceProps> = props => {
};

const handleEditorEval = props.editorContainerProps?.handleEditorEval;
const handleTabChangeForRepl = React.useCallback(
const handleTabChangeForRepl = useCallback(
(newTabId: SideContentType, prevTabId: SideContentType) => {
// Evaluate program upon pressing the run tab.
if (newTabId === SideContentType.mobileEditorRun) {
if (handleEditorEval) {
handleEditorEval();
}
handleEditorEval?.();
}

// Show the REPL upon pressing the run tab if the previous tab is not listed below.
Expand Down Expand Up @@ -226,7 +212,7 @@ const MobileWorkspace: React.FC<MobileWorkspaceProps> = props => {
);

const onChange = props.mobileSideContentProps.onChange;
const onSideContentTabChange = React.useCallback(
const onSideContentTabChange = useCallback(
(
newTabId: SideContentType,
prevTabId: SideContentType,
Expand All @@ -241,27 +227,7 @@ const MobileWorkspace: React.FC<MobileWorkspaceProps> = props => {
// Convert sidebar tabs with a side content tab ID into side content tabs.
const sideBarTabs: SideContentTab[] = props.sideBarProps.tabs.filter(tab => tab.id !== undefined);

const mobileEditorTab: SideContentTab = React.useMemo(
() => ({
label: 'Editor',
iconName: IconNames.EDIT,
body: null,
id: SideContentType.mobileEditor
}),
[]
);

const mobileRunTab: SideContentTab = React.useMemo(
() => ({
label: 'Run',
iconName: IconNames.PLAY,
body: null,
id: SideContentType.mobileEditorRun
}),
[]
);

const updatedMobileSideContentProps = React.useCallback(() => {
const updatedMobileSideContentProps = useCallback(() => {
return {
...props.mobileSideContentProps,
onChange: onSideContentTabChange,
Expand All @@ -277,13 +243,7 @@ const MobileWorkspace: React.FC<MobileWorkspaceProps> = props => {
]
}
};
}, [
onSideContentTabChange,
mobileEditorTab,
mobileRunTab,
props.mobileSideContentProps,
sideBarTabs
]);
}, [onSideContentTabChange, props.mobileSideContentProps, sideBarTabs]);

const inAssessmentWorkspace = props.mobileSideContentProps.workspaceLocation === 'assessment';

Expand Down Expand Up @@ -318,3 +278,17 @@ const MobileWorkspace: React.FC<MobileWorkspaceProps> = props => {
};

export default MobileWorkspace;

const mobileEditorTab: SideContentTab = {
label: 'Editor',
iconName: IconNames.EDIT,
body: null,
id: SideContentType.mobileEditor
};

const mobileRunTab: SideContentTab = {
label: 'Run',
iconName: IconNames.PLAY,
body: null,
id: SideContentType.mobileEditorRun
};
2 changes: 1 addition & 1 deletion src/commons/navigationBar/NavigationBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ const NavigationBar: React.FC = () => {
position={Position.BOTTOM_RIGHT}
interactionKind="hover"
content={desktopNavbarLeftPopoverContent}
popoverClassName={'desktop-navbar-popover'}
popoverClassName="desktop-navbar-popover"
disabled={!enableDesktopPopover}
>
<NavLink
Expand Down
22 changes: 0 additions & 22 deletions src/commons/sagas/RequestsSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1660,28 +1660,6 @@ export async function deleteDevice(device: Pick<Device, 'id'>, tokens?: Tokens):
return true;
}

/**
* POST /chat
*/

export async function chat(
tokens: Tokens,
payload: { role: string; content: string }[]
): Promise<string> {
const response = await request(`chat`, 'POST', {
...tokens,
body: { json: payload }
});
if (!response) {
throw new Error('Unknown error occurred.');
}
if (!response.ok) {
const message = await response.text();
throw new Error(`Failed to chat to louis: ${message}`);
}
return response.text();
}

function fillTokens(tokens?: Tokens): Tokens {
tokens = tokens || getTokensFromStore();
if (!tokens) {
Expand Down
4 changes: 2 additions & 2 deletions src/commons/sideContent/content/SideContentFaceapiDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const SideContentFaceapiDisplay: React.FC = () => {
<div className="sa-video-header">
<div className="sa-video-header-element">
<Button
className={'sa-live-video-button'}
className="sa-live-video-button"
style={{ height: 20 }}
icon={IconNames.CAMERA}
onClick={takePhoto}
Expand All @@ -35,7 +35,7 @@ const SideContentFaceapiDisplay: React.FC = () => {
<Divider />
<div className="sa-video-header-element">
<Button
className={'sa-still-image-button'}
className="sa-still-image-button"
style={{ height: 20 }}
icon={IconNames.RESET}
onClick={resetPhoto}
Expand Down
2 changes: 1 addition & 1 deletion src/commons/sourceRecorder/SourceRecorderTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ class SourcecastTable extends React.Component<SourceRecorderTableProps, State> {
<div className="SourcecastTable">
<div className="ag-grid-parent">
<AgGridReact
domLayout={'autoHeight'}
domLayout="autoHeight"
columnDefs={this.state.columnDefs}
defaultColDef={this.defaultColumnDefs}
onGridReady={this.onGridReady}
Expand Down
21 changes: 21 additions & 0 deletions src/commons/utils/Hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import { useMediaQuery } from 'react-responsive';

import { OverallState } from '../application/ApplicationTypes';
import { Tokens } from '../application/types/SessionTypes';
import Constants from './Constants';
import { readLocalStorage, setLocalStorage } from './LocalStorageHelper';

Expand Down Expand Up @@ -135,3 +136,23 @@ export const useSession = () => {
isLoggedIn
};
};

// Overload for useTokens
type UseTokens = {
(): Tokens;
(options: { throwWhenEmpty: true }): Tokens;
(options: { throwWhenEmpty: false }): Partial<Tokens>;
};

/**
* Returns the access token and refresh token from the session.
* @param throwWhenEmpty (optional) If true, throws an error if no tokens are found.
*/
export const useTokens: UseTokens = ({ throwWhenEmpty = true } = {}) => {
const accessToken = useTypedSelector(state => state.session.accessToken);
const refreshToken = useTypedSelector(state => state.session.refreshToken);
if (throwWhenEmpty && (!accessToken || !refreshToken)) {
throw new Error('No access token or refresh token found');
}
return { accessToken, refreshToken } as Tokens;
};
20 changes: 10 additions & 10 deletions src/commons/workspace/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { IconNames } from '@blueprintjs/icons';
import { useFullscreen } from '@mantine/hooks';
import { Enable, NumberSize, Resizable, ResizableProps, ResizeCallback } from 're-resizable';
import { Direction } from 're-resizable/lib/resizer';
import React from 'react';
import React, { useEffect, useRef, useState } from 'react';

import ControlBar, { ControlBarProps } from '../controlBar/ControlBar';
import EditorContainer, { EditorContainerProps } from '../editor/EditorContainer';
Expand Down Expand Up @@ -35,15 +35,15 @@ type StateProps = {
};

const Workspace: React.FC<WorkspaceProps> = props => {
const sideBarResizable = React.useRef<Resizable | null>(null);
const contentContainerDiv = React.useRef<HTMLDivElement | null>(null);
const editorDividerDiv = React.useRef<HTMLDivElement | null>(null);
const leftParentResizable = React.useRef<Resizable | null>(null);
const maxDividerHeight = React.useRef<number | null>(null);
const sideDividerDiv = React.useRef<HTMLDivElement | null>(null);
const sideBarResizable = useRef<Resizable | null>(null);
const contentContainerDiv = useRef<HTMLDivElement>(null);
const editorDividerDiv = useRef<HTMLDivElement>(null);
const leftParentResizable = useRef<Resizable | null>(null);
const maxDividerHeight = useRef<number | null>(null);
const sideDividerDiv = useRef<HTMLDivElement>(null);
const [contentContainerWidth] = useDimensions(contentContainerDiv);
const [expandedSideBarWidth, setExpandedSideBarWidth] = React.useState(200);
const [isSideBarExpanded, setIsSideBarExpanded] = React.useState(true);
const [expandedSideBarWidth, setExpandedSideBarWidth] = useState(200);
const [isSideBarExpanded, setIsSideBarExpanded] = useState(true);

const sideBarCollapsedWidth = 40;

Expand All @@ -52,7 +52,7 @@ const Workspace: React.FC<WorkspaceProps> = props => {

FocusStyleManager.onlyShowFocusOnTabs();

React.useEffect(() => {
useEffect(() => {
if (props.sideContentIsResizeable && maxDividerHeight.current === null) {
maxDividerHeight.current = sideDividerDiv.current!.clientHeight;
}
Expand Down
2 changes: 1 addition & 1 deletion src/features/cseMachine/CseMachineLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ export class Layout {
return Layout.prevLayout;
} else {
const layout = (
<div className={'sa-cse-machine'} data-testid="sa-cse-machine">
<div className="sa-cse-machine" data-testid="sa-cse-machine">
<div
id="scroll-container"
ref={Layout.scrollContainerRef}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export class ControlItemComponent extends Visible implements IHoverable {
<Tag
{...ShapeDefaultProps}
stroke="black"
fill={'black'}
fill="black"
opacity={ControlStashConfig.TooltipOpacity}
/>
<Text
Expand Down
2 changes: 1 addition & 1 deletion src/features/cseMachine/components/StashItemComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export class StashItemComponent extends Visible implements IHoverable {
<Tag
{...ShapeDefaultProps}
stroke="black"
fill={'black'}
fill="black"
opacity={ControlStashConfig.TooltipOpacity}
/>
<Text
Expand Down
4 changes: 2 additions & 2 deletions src/features/cseMachine/components/values/FnValue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export class FnValue extends Value implements IHoverable {
visible={true}
ref={this.labelRef}
>
<KonvaTag stroke="black" fill={'white'} opacity={Config.FnTooltipOpacity} />
<KonvaTag stroke="black" fill="white" opacity={Config.FnTooltipOpacity} />
<KonvaText
text={this.exportTooltip}
fontFamily={Config.FontFamily}
Expand All @@ -185,7 +185,7 @@ export class FnValue extends Value implements IHoverable {
visible={false}
ref={this.labelRef}
>
<KonvaTag stroke="black" fill={'black'} opacity={Config.FnTooltipOpacity} />
<KonvaTag stroke="black" fill="black" opacity={Config.FnTooltipOpacity} />
<KonvaText
text={this.tooltip}
fontFamily={Config.FontFamily}
Expand Down
Loading