Skip to content

Commit

Permalink
feat: rework interface data (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
paradoxuum authored Dec 25, 2023
1 parent f588b7c commit d207795
Show file tree
Hide file tree
Showing 15 changed files with 234 additions and 184 deletions.
27 changes: 19 additions & 8 deletions src/client/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ import { mergeDeep } from "@rbxts/sift/out/Dictionary";
import { ClientDispatcher } from "./dispatcher";
import { DEFAULT_OPTIONS } from "./options";
import { ClientRegistry } from "./registry";
import { CommanderEvents } from "./types";

export namespace CommanderClient {
let started = false;
const registryInstance = new ClientRegistry();
const dispatcherInstance = new ClientDispatcher(registryInstance);
const events: CommanderEvents = {
historyUpdated: new Instance("BindableEvent"),
commandAdded: new Instance("BindableEvent"),
groupAdded: new Instance("BindableEvent"),
};
const registryInstance = new ClientRegistry(events);
const dispatcherInstance = new ClientDispatcher(registryInstance, events);
let optionsObject = DEFAULT_OPTIONS;

const IS_CLIENT = RunService.IsClient();
Expand All @@ -34,18 +40,23 @@ export namespace CommanderClient {

callback(registryInstance);
await registryInstance.sync();

registryInstance.freeze();
started = true;

if (options.app !== undefined) {
options.app({
options: optionsObject,
execute: (path, text) => dispatcherInstance.run(path, text),
commands: registryInstance.getCommandOptions(),
groups: registryInstance.getGroupOptions(),
history: dispatcherInstance.getHistory(),
onHistoryUpdated: dispatcherInstance.getHistorySignal(),
addHistoryEntry: (entry) => dispatcherInstance.addHistoryEntry(entry),
initialData: {
commands: registryInstance.getCommandOptions(),
groups: registryInstance.getGroupOptions(),
history: dispatcherInstance.getHistory(),
},
events: {
historyUpdated: events.historyUpdated.Event,
commandAdded: events.commandAdded.Event,
groupAdded: events.groupAdded.Event,
},
});
}
}
Expand Down
26 changes: 11 additions & 15 deletions src/client/dispatcher.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { Players } from "@rbxts/services";
import { CommandPath } from "../shared";
import { BaseRegistry, CommandPath } from "../shared";
import { BaseDispatcher } from "../shared/core/dispatcher";
import { DEFAULT_HISTORY_LENGTH } from "./options";
import { ClientOptions, HistoryEntry } from "./types";
import { ClientOptions, CommanderEvents, HistoryEntry } from "./types";

export class ClientDispatcher extends BaseDispatcher {
private readonly history: HistoryEntry[] = [];
private readonly historyEvent = new Instance("BindableEvent");
private maxHistoryLength = DEFAULT_HISTORY_LENGTH;

constructor(
registry: BaseRegistry,
private readonly events: CommanderEvents,
) {
super(registry);
}

/**
* Initialises the client dispatcher.
*
Expand Down Expand Up @@ -60,22 +66,12 @@ export class ClientDispatcher extends BaseDispatcher {
return this.history;
}

/**
* Gets the history signal, which will be fired each time a new
* {@link HistoryEntry} is added.
*
* @returns The history signal
*/
getHistorySignal() {
return this.historyEvent.Event;
}

private addHistoryEntry(entry: HistoryEntry) {
addHistoryEntry(entry: HistoryEntry) {
if (this.history.size() >= this.maxHistoryLength) {
this.history.remove(0);
}

this.history.push(entry);
this.historyEvent.Fire(entry);
this.events.historyUpdated.Fire(this.history);
}
}
6 changes: 3 additions & 3 deletions src/client/interface/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import "./config";
import { createPortal, createRoot } from "@rbxts/react-roblox";
import Roact, { StrictMode } from "@rbxts/roact";
import { Players } from "@rbxts/services";
import { AppData } from "../../types";
import { AppContext } from "../../types";
import { Layer } from "../components/interface/Layer";
import Terminal from "../components/terminal/Terminal";
import { RootProvider } from "../providers/rootProvider";
Expand All @@ -18,7 +18,7 @@ function getSuggestion(query: SuggestionQuery) {
return getCommandSuggestion(query.parentPath, query.text);
}

export function CommanderApp(data: AppData) {
export function CommanderApp(data: AppContext) {
const root = createRoot(new Instance("Folder"));
const target = Players.LocalPlayer.WaitForChild("PlayerGui");

Expand All @@ -27,7 +27,7 @@ export function CommanderApp(data: AppData) {
<StrictMode>
<RootProvider
key="root-provider"
data={data}
value={data}
getSuggestion={getSuggestion}
>
<Layer key="terminal">
Expand Down
4 changes: 2 additions & 2 deletions src/client/interface/components/terminal/Terminal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import Roact, { useContext, useMemo } from "@rbxts/roact";
import { GuiService, UserInputService } from "@rbxts/services";
import { useRem } from "../../hooks/useRem";
import { useStore } from "../../hooks/useStore";
import { DataContext } from "../../providers/dataProvider";
import { CommanderContext } from "../../providers/commanderProvider";
import { selectVisible } from "../../store/app";
import { Group } from "../interface/Group";
import { TerminalWindow } from "./TerminalWindow";
import { SuggestionList } from "./suggestion";

export default function Terminal() {
const rem = useRem();
const data = useContext(DataContext);
const data = useContext(CommanderContext);
const store = useStore();

const visible = useSelector(selectVisible);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { fonts } from "../../constants/fonts";
import { palette } from "../../constants/palette";
import { useRem } from "../../hooks/useRem";
import { useStore } from "../../hooks/useStore";
import { DataContext } from "../../providers/dataProvider";
import { CommanderContext } from "../../providers/commanderProvider";
import { SuggestionContext } from "../../providers/suggestionProvider";
import { selectVisible } from "../../store/app";
import { getArgumentNames } from "../../util/argument";
Expand All @@ -47,7 +47,7 @@ export function TerminalTextField({
}: TerminalTextFieldProps) {
const rem = useRem();
const ref = useRef<TextBox>();
const data = useContext(DataContext);
const data = useContext(CommanderContext);
const suggestion = useContext(SuggestionContext).suggestion;
const store = useStore();

Expand Down Expand Up @@ -143,6 +143,7 @@ export function TerminalTextField({
const suggestionTextParts = suggestionTextValue
.gsub("%s+", " ")[0]
.split(" ");

const nextCommand = data.commands.get(
formatPartsAsPath(suggestionTextParts),
);
Expand Down
40 changes: 10 additions & 30 deletions src/client/interface/components/terminal/TerminalWindow.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
import { useEventListener, useMountEffect } from "@rbxts/pretty-react-hooks";
import { useSelector } from "@rbxts/react-reflex";
import Roact, { useContext, useEffect, useMemo, useState } from "@rbxts/roact";
import { TextService } from "@rbxts/services";
import { copy, pop, slice } from "@rbxts/sift/out/Array";
import { copyDeep } from "@rbxts/sift/out/Dictionary";
import { ImmutableCommandPath } from "../../../../shared";
import {
endsWithSpace,
formatPartsAsPath,
splitStringBySpace,
} from "../../../../shared/util/string";
import { DEFAULT_HISTORY_LENGTH } from "../../../options";
import { DEFAULT_FONT } from "../../constants/fonts";
import { palette } from "../../constants/palette";
import { useMotion } from "../../hooks/useMotion";
import { useRem } from "../../hooks/useRem";
import { useStore } from "../../hooks/useStore";
import { DataContext } from "../../providers/dataProvider";
import { CommanderContext } from "../../providers/commanderProvider";
import { SuggestionContext } from "../../providers/suggestionProvider";
import { selectHistory } from "../../store/app";
import { HistoryLineData } from "../../types";
import { Frame } from "../interface/Frame";
import { Padding } from "../interface/Padding";
Expand All @@ -37,10 +32,9 @@ function getParentPath(parts: string[], atNextPart: boolean) {
export function TerminalWindow() {
const rem = useRem();
const store = useStore();
const data = useContext(DataContext);
const data = useContext(CommanderContext);
const suggestionData = useContext(SuggestionContext);

const history = useSelector(selectHistory);
const [historyData, setHistoryData] = useState<HistoryData>({
lines: [],
height: 0,
Expand All @@ -61,26 +55,15 @@ export function TerminalWindow() {
}, [rem]);

// Handle history updates
useMountEffect(() => {
store.setHistory(copyDeep(data.history));
});

useEventListener(data.onHistoryUpdated, (entry) => {
store.addHistoryEntry(
entry,
data.options.historyLength ?? DEFAULT_HISTORY_LENGTH,
);
});

useEffect(() => {
const historySize = history.size();
const historySize = data.history.size();
let totalHeight =
historySize > 0 ? rem(0.5) + (historySize - 1) * rem(0.5) : 0;

textBoundsParams.Size = rem(1.5);

const historyLines: HistoryLineData[] = [];
for (const entry of history) {
for (const entry of data.history) {
textBoundsParams.Text = entry.text;
const textSize = TextService.GetTextBoundsAsync(textBoundsParams);
totalHeight += textSize.Y;
Expand All @@ -94,7 +77,7 @@ export function TerminalWindow() {
lines: historyLines,
height: totalHeight,
});
}, [history, rem]);
}, [data.history, rem]);

return (
<Frame
Expand Down Expand Up @@ -212,14 +195,11 @@ export function TerminalWindow() {
const storeState = store.getState();
const command = storeState.app.command;
if (command === undefined) {
store.addHistoryEntry(
{
success: false,
text: "Command not found.",
sentAt: os.time(),
},
data.options.historyLength ?? DEFAULT_HISTORY_LENGTH,
);
data.addHistoryEntry({
success: false,
text: "Command not found.",
sentAt: os.time(),
});
return;
}

Expand Down
75 changes: 75 additions & 0 deletions src/client/interface/providers/commanderProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useEventListener } from "@rbxts/pretty-react-hooks";
import Roact, { createContext, useState } from "@rbxts/roact";
import { copyDeep } from "@rbxts/sift/out/Dictionary";
import { CommandOptions, CommandPath, GroupOptions } from "../../../shared";
import { DEFAULT_OPTIONS } from "../../options";
import { AppContext, ClientOptions, HistoryEntry } from "../../types";

export interface CommanderContextData {
options: ClientOptions;
execute: (path: CommandPath, text: string) => Promise<HistoryEntry>;
commands: Map<string, CommandOptions>;
groups: Map<string, GroupOptions>;
history: HistoryEntry[];
addHistoryEntry: (entry: HistoryEntry) => void;
}

const DEFAULT_EXECUTE_CALLBACK = async () => ({
text: "Command executed.",
success: true,
sentAt: DateTime.now().UnixTimestamp,
});

export const DEFAULT_COMMANDER_CONTEXT: CommanderContextData = {
options: DEFAULT_OPTIONS,
execute: DEFAULT_EXECUTE_CALLBACK,
commands: new Map(),
groups: new Map(),
history: [],
addHistoryEntry: () => {},
};

export interface CommanderProviderProps extends Roact.PropsWithChildren {
value: AppContext;
}

export const CommanderContext = createContext<CommanderContextData>(
DEFAULT_COMMANDER_CONTEXT,
);

export function CommanderProvider({ value, children }: CommanderProviderProps) {
const [history, setHistory] = useState(value.initialData.history);
const [commands, setCommands] = useState(value.initialData.commands);
const [groups, setGroups] = useState(value.initialData.groups);

useEventListener(value.events.historyUpdated, (entries) => {
setHistory(copyDeep(entries));
});

useEventListener(value.events.commandAdded, (key, command) => {
const newData = copyDeep(commands);
newData.set(key, command);
setCommands(newData);
});

useEventListener(value.events.groupAdded, (key, group) => {
const newData = copyDeep(groups);
groups.set(key, group);
setGroups(newData);
});

return (
<CommanderContext.Provider
value={{
addHistoryEntry: value.addHistoryEntry,
execute: value.execute,
options: value.options,
history,
commands,
groups,
}}
>
{children}
</CommanderContext.Provider>
);
}
29 changes: 0 additions & 29 deletions src/client/interface/providers/dataProvider.tsx

This file was deleted.

Loading

0 comments on commit d207795

Please sign in to comment.