Skip to content

Commit

Permalink
Beta 2 - QoL (#479)
Browse files Browse the repository at this point in the history
* prepare Recipe "TriggerRandom" Payload to accept "actions by tag"

* QoL: Recipe Tooltips - Show ActionList entries with Icon+Name

* Refactor the Action Assignment Dialog

* QoL: if new action dialog is opened with a type - we can skip the type selection

* reset checkedMap on a single action assigning mode

* ActionList Config - able to choose by Tag - or - choose multiple in one dialog

* dont use a play length on media that doesnt need it

* new recipe command block - full item as button

* connection list now hides an invalid "token valid date"

* obs connection now doesn't react on settings changes other than obs + log twitch auth validation

* fix some deepsource issues
  • Loading branch information
negue committed Sep 17, 2022
1 parent 430dfbb commit 8c185e9
Show file tree
Hide file tree
Showing 106 changed files with 1,199 additions and 579 deletions.
39 changes: 38 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,42 @@
## 2022.1 to be released

### Feature

* [ ] Show Errors in the Dashboard - incl. a way to create a GitHub Issue from that
* [ ] Tell the Streamer with warnings / dialogs that the Token will expire in X Days
* [ ] Ability to re-authenticate even when you are not in the normal ports
* [ ] Twitch Auth: Improve custom scopes handling
* [ ] Recipe: more Twitch Command Blocks: Announce, Clear Chat, Start Commercial, Create Stream Marker, Slow Mode, Chat Settings
* [ ] Recipe: more Obs Command Blocks: TBD
* [ ] if easy/fast todo: Support for new OBS-websocket(.js) v5

### Misc

* [ ] Added a Twitch Raid Script Example
* [ ] Recipe Example: Panic Button

## 2022.1-beta2

### Feature

* [x] Recipe "Trigger random action" - now you can select actions based on a Tag
* [x] Recipe "Trigger random action" - now you can select multiple actions at the same time

### Changes

* [x] Recipe "Trigger random action" - now shows the Actions in a vertical list instead of comma separated
* [x] Action Settings: Added a hint why a screen time is needed or can stay empty
* [x] When the Twitch Token is close to be invalid, show a warning in console

### Quality of Life

* [x] When creating a new action, the dialog will jump already into the Action Settings
* [x] Added a few Tooltips in the Recipe Dialog
* [x] Recipe new Command Block full entry is now a button
* [x] When you authenticate it'll be automatically saved

## 2022.1-beta1

### Breaking Changes

* [x] The Headless Mode will not open the browser on start anymore, use `--open=true` for that
Expand Down Expand Up @@ -54,7 +91,7 @@
* [x] The Arrange View was redesigned by `@owehmer`
* [x] "HTML" is now renamed to "Widgets"
* [x] "Media" is now renamed to "Actions"
* [ ] Overhaul of the Media Creation Dialog(s)
* [x] Overhaul of the Media Creation Dialog(s)


## 2021.2.1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
import { DialogService } from "../../../../../src/app/shared/dialogs/dialog.service";
import { ClipAssigningMode } from "@memebox/contracts";
import { BehaviorSubject, combineLatest } from "rxjs";
import { AppQueries } from "@memebox/app-state";
import { map } from "rxjs/operators";
import { ActionVariableTypes } from "@memebox/action-variables";
import {Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges} from '@angular/core';
import {DialogService} from "../../../../../src/app/shared/dialogs/dialog.service";
import {ActionAssigningMode} from "@memebox/contracts";
import {BehaviorSubject, combineLatest} from "rxjs";
import {AppQueries} from "@memebox/app-state";
import {map} from "rxjs/operators";
import {ActionVariableTypes} from "@memebox/action-variables";

@Component({
selector: 'app-action-variable-input',
Expand Down Expand Up @@ -59,9 +59,9 @@ export class ActionVariableInputComponent implements OnInit, OnChanges {
}

async selectSingleMedia() {
const actionId = await this.dialogService.showActionSelectionDialogAsync({
mode: ClipAssigningMode.Single,
selectedItemId: this.visibleActionIdList$.value[0] ?? null,
const [actionId] = await this.dialogService.showActionSelectionDialogAsync({
mode: ActionAssigningMode.Single,
selectedActionIdList: this.visibleActionIdList$.value,
dialogTitle: 'Action Variable',
showMetaItems: true,

Expand Down
2 changes: 1 addition & 1 deletion projects/app-state/src/lib/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from '../services.module';
export * from './config.service';
export * from './snackbar.service';
export * from './network-interfaces.service';
export * from './memebox-websocket.service';
Expand Down
45 changes: 43 additions & 2 deletions projects/app-state/src/lib/state/app.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ export class AppService {
this.snackbar.normal('Media saved!');
}

public async addOrUpdateScreenClip(screenId: string, screenClip: Partial<ScreenMedia>) {
public async addOrUpdateScreenMedia(screenId: string, screenClip: Partial<ScreenMedia>) {
screenClip = fillDefaultsScreenClip(screenClip);

// add the action to api & await
Expand All @@ -249,6 +249,47 @@ export class AppService {

this.snackbar.normal(`Media ${wasAlreadyAdded ? 'Settings updated' : 'added to screen'}!`);
}
public async addOrUpdateAssignedScreenMediaInBulk(
screenId: string,
addedScreenMediaIdList: string[],
deletedScreenMediaIdList: string[],
) {
// TODO create Endpoint to update this completely in the backend

for (const mediaId of deletedScreenMediaIdList) {
// send the api call
await this.memeboxApi.delete(`${ENDPOINTS.SCREEN}/${screenId}/${ENDPOINTS.OBS_CLIPS}/${mediaId}`);
}

const addedScreenMedia: ScreenMedia[] = [];

for (const mediaId of addedScreenMediaIdList) {
const screenMedia: ScreenMedia = {
id: mediaId,
visibility: VisibilityEnum.Play
};
// add the action to api & await
await this.memeboxApi.put(
`${ENDPOINTS.SCREEN}/${screenId}/${ENDPOINTS.OBS_CLIPS}/${mediaId}`,
screenMedia
);
addedScreenMedia.push(screenMedia);
}

// add to the state
this.appStore.update(state => {
for (const mediaId of deletedScreenMediaIdList) {
delete state.screen[screenId].clips[mediaId];
}

for (const media of addedScreenMedia) {
addOrUpdateScreenClip(state, screenId, media);
}
});

// todo rename those snackbars
this.snackbar.normal(`Updated assigned Screen Items`);
}

// TODO rename action and screenclip settings
public async addOrUpdateScreenActionInBulk(screenId: string, changedActions: Partial<ScreenMedia>[]) {
Expand All @@ -268,7 +309,7 @@ export class AppService {
this.snackbar.normal(`Screen Media Settings updated!`);
}

public async deleteScreenClip(screenId: string, id: string) {
public async deleteScreenMedia(screenId: string, id: string) {
// send the api call
await this.memeboxApi.delete(`${ENDPOINTS.SCREEN}/${screenId}/${ENDPOINTS.OBS_CLIPS}/${id}`);

Expand Down
2 changes: 1 addition & 1 deletion projects/contracts/src/lib/dialogs-contracts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

export enum ClipAssigningMode {
export enum ActionAssigningMode {
Multiple,
Single
}
Expand Down
20 changes: 19 additions & 1 deletion projects/contracts/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,12 @@ export interface Tag extends HasId {
color: string;
}

export interface UserDataState {
actions: Dictionary<Action>;
screen: Dictionary<Screen>;
tags: Dictionary<Tag>;
}

/**
* Settings.json - State
*
Expand Down Expand Up @@ -266,6 +272,9 @@ export interface Config {

export interface TwitchConfig {
channel: string;
/**
* @deprecated might need to be deleted
*/
enableLog?: boolean;
bot?: TwitchBotConfig;
token: string|null;
Expand Down Expand Up @@ -341,7 +350,7 @@ export interface ChangedInfo {
id?: string;
targetScreenId?: string;
dataType: 'everything'|'action'|'tags'|'screens'|'screen-action-config'
|'settings'|'twitch-events'|'timers'|'twitch-setting';
|'settings'|'twitch-events'|'timers'|'twitch-setting'|'obs-settings';
actionType?: ActionType;
changeType: 'added'|'changed'|'removed';
}
Expand Down Expand Up @@ -396,3 +405,12 @@ export interface ObsSourceFilterEntry {
name: string;
settings: Record<string, unknown>;
}


export function getUserDataState (settings: SettingsState): UserDataState {
return {
actions: settings.clips,
screen: settings.screen,
tags: settings.tags
}
}
45 changes: 23 additions & 22 deletions projects/recipe-core/src/lib/command-blocks.memebox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import {
RecipeCommandConfigActionPayload
} from "./recipe.types";
import {map, take} from "rxjs/operators";
import {generateRandomCharacters} from "./utils";
import {combineLatest} from "rxjs";
import {generateRandomCharacters, listActionsOfActionListPayload} from "./utils";
import {ACTION_TYPE_INFORMATION} from "@memebox/contracts";

function createMemeboxApiVariable(
Expand Down Expand Up @@ -104,14 +103,14 @@ export function registerMemeboxCommandBlocks (
entries: []
});
},
toScriptCode: (step, context) => {
toScriptCode: (step, context, userData) => {
const actionPayload = step.payload.action as RecipeCommandConfigActionPayload;

const actionOverrides = actionPayload.overrides;

return `${createMemeboxApiVariable(actionPayload)}
.triggerWhile(async (helpers_${step.payload._suffix}) => {
${generateCodeByStep(step, context)}
${generateCodeByStep(step, context, userData)}
}
${actionOverrides ? ',' + JSON.stringify(actionOverrides) : ''});`;
},
Expand Down Expand Up @@ -157,36 +156,38 @@ export function registerMemeboxCommandBlocks (
}
],
awaitCodeHandledInternally: true,
toScriptCode: (step) => {
toScriptCode: (step, context, userData) => {
const awaitCode = step.awaited ? 'await ': '';

const actionsToChooseFrom = listActionsOfActionListPayload(
step.payload.actions as RecipeCommandConfigActionListPayload,
userData
);

return `
${awaitCode} (() => {
const actionsToChooseFrom = [${
(step.payload.actions as RecipeCommandConfigActionListPayload)
.map(action => JSON.stringify(action))
.join(',')
}];
const actionsToChooseFrom = [${actionsToChooseFrom
.map(action => JSON.stringify(action))
.join(',')}];
return memebox.triggerRandom(actionsToChooseFrom);
})();
`;
},
commandEntryLabelAsync: async (queries, payload) => {
const actionPayload = payload.actions as RecipeCommandConfigActionPayload[];
const actionListPayload = (payload.actions as RecipeCommandConfigActionListPayload);

const namesOfActions = await combineLatest(
actionPayload.map(a => queries.getActionById$(a.actionId).pipe(
map(actionInfo => actionInfo?.name ?? 'unknown action'),
if (actionListPayload.actionsByTag) {
const tags = await queries.tagList$.pipe(
take(1)
))
)
.pipe(
map(allNames => allNames.join(','))
)
.toPromise();

return 'trigger random: '+ namesOfActions;
).toPromise();
const tagName = tags.find(t => t.id === actionListPayload.actionsByTag)?.name
?? `\r\nUnknown Tag: ${actionListPayload.actionsByTag}`;

return `trigger any action with the tag: ${tagName}`;
}

return `trigger any of the following: ${actionListPayload.selectedActions.length}`;
},
};

Expand Down
17 changes: 11 additions & 6 deletions projects/recipe-core/src/lib/generateCodeByRecipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ import {uuid} from "@gewd/utils";
import {registerMemeboxCommandBlocks} from "./command-blocks.memebox";
import {registerObsCommandBlocks} from "./command-blocks.obs";
import {registerTwitchCommandBlocks} from "./command-blocks.twitch";
import {UserDataState} from "@memebox/contracts";

export interface RecipeStepConfigArgument {
name: string;
label: string;
type: string;
type: string; // todo change to the enum
}

export const RecipeCommandBlockGroups: Record<string, RecipeCommandSelectionGroup> = {
Expand Down Expand Up @@ -51,6 +52,7 @@ export const RecipeCommandRegistry: RecipeCommandBlockRegistry = {
commandEntryLabelAsync: (queries, payload, parentStep) => {
return Promise.resolve(`sleep: ${payload.seconds} seconds`);
},
entryIcon: () => 'hourglass_top'
},
"sleepMs": {
pickerLabel: "Wait for Milliseconds",
Expand All @@ -66,11 +68,12 @@ export const RecipeCommandRegistry: RecipeCommandBlockRegistry = {
commandEntryLabelAsync: (queries, payload, parentStep) => {
return Promise.resolve(`sleep: ${payload.ms}ms`);
},
entryIcon: () => 'hourglass_top'
}
};


function generateCodeByStep (step: RecipeEntry, context: RecipeContext) {
function generateCodeByStepAsync (step: RecipeEntry, context: RecipeContext, userData: UserDataState): string {
const result: string[] = [];

for (const subStepInfo of step.subCommandBlocks) {
Expand All @@ -88,7 +91,9 @@ function generateCodeByStep (step: RecipeEntry, context: RecipeContext) {
result.push('await ');
}

result.push(entryDefinition.toScriptCode(subEntry, context).trim());
const createdStepCode = entryDefinition.toScriptCode(subEntry, context, userData);

result.push(createdStepCode.trim());

// result.push(`logger.log('Post: ${subEntry.commandType}');`);
} else {
Expand All @@ -102,13 +107,13 @@ function generateCodeByStep (step: RecipeEntry, context: RecipeContext) {
}

export function generateCodeByRecipe(
recipeContext: RecipeContext
recipeContext: RecipeContext, userData: UserDataState
): string {
const result: string[] = [];

const rootEntry = recipeContext.entries[recipeContext.rootEntry];

result.push(generateCodeByStep(rootEntry, recipeContext));
result.push(generateCodeByStepAsync(rootEntry, recipeContext, userData));

return result.join('\r\n');
}
Expand All @@ -127,6 +132,6 @@ export function generateRecipeEntryCommandCall (
};
}

registerMemeboxCommandBlocks(RecipeCommandRegistry, generateCodeByStep);
registerMemeboxCommandBlocks(RecipeCommandRegistry, generateCodeByStepAsync);
registerObsCommandBlocks(RecipeCommandRegistry);
registerTwitchCommandBlocks(RecipeCommandRegistry);
Loading

0 comments on commit 8c185e9

Please sign in to comment.