Skip to content

Commit

Permalink
Add basic additional donations support
Browse files Browse the repository at this point in the history
  • Loading branch information
zoton2 committed Feb 22, 2024
1 parent ee1387d commit f58be68
Show file tree
Hide file tree
Showing 13 changed files with 235 additions and 5 deletions.
27 changes: 26 additions & 1 deletion configschema.json
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,30 @@
"type": "string"
}
}
},
"additionalDonations": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"key": {
"type": "string"
},
"description": {
"type": "string"
},
"amount": {
"type": "number"
}
},
"required": [
"key",
"description",
"amount"
]
},
"default": []
}
},
"required": [
Expand All @@ -785,6 +809,7 @@
"discord",
"streamlabsCharity",
"therungg",
"tiltify"
"tiltify",
"additionalDonations"
]
}
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,14 @@
"file": "rabbitmq-test.html",
"workspace": "Z9 - Debug",
"headerColor": "#c49215"
},
{
"name": "additional-donations-control",
"title": "Additional Donations Control",
"width": 3,
"file": "additional-donations-control.html",
"workspace": "Z2 - ESA Advanced",
"headerColor": "#c49215"
}
],
"graphics": [
Expand Down
21 changes: 21 additions & 0 deletions schemas/additionalDonations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"key": {
"type": "string"
},
"active": {
"type": "boolean"
}
},
"required": [
"key",
"active"
]
},
"default": []
}
5 changes: 4 additions & 1 deletion src/browser_shared/replicant_store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Bids, BigbuttonPlayerMap, Commentators, CommentatorsNew, Countdown, CurrentRunDelay, DonationAlerts, DonationReader, DonationReaderNew, DonationTotal, DonationTotalMilestones, DonationsToRead, GameLayouts, IntermissionSlides, MusicData, ObsData, Omnibar, OtherStreamData, Prizes, ReaderIntroduction, ServerTimestamp, StreamDeckData, TtsVoices, UpcomingRunID, VideoPlayer } from '@esa-layouts/types/schemas';
import type { AdditionalDonations, Bids, BigbuttonPlayerMap, Commentators, CommentatorsNew, Countdown, CurrentRunDelay, DonationAlerts, DonationReader, DonationReaderNew, DonationTotal, DonationTotalMilestones, DonationsToRead, GameLayouts, IntermissionSlides, MusicData, ObsData, Omnibar, OtherStreamData, Prizes, ReaderIntroduction, ServerTimestamp, StreamDeckData, TtsVoices, UpcomingRunID, VideoPlayer } from '@esa-layouts/types/schemas';
import type NodeCGTypes from '@nodecg/types';
import clone from 'clone';
import { SpeedcontrolUtilBrowser } from 'speedcontrol-util';
Expand All @@ -13,6 +13,7 @@ const sc = new SpeedcontrolUtilBrowser(nodecg);

// Declaring replicants.
export const reps: {
additionalDonations: NodeCGTypes.ClientReplicant<AdditionalDonations>;
assetsDonationAlertAssets: NodeCGTypes.ClientReplicant<NodeCGTypes.AssetFile[]>;
assetsIntermissionSlides: NodeCGTypes.ClientReplicant<NodeCGTypes.AssetFile[]>;
assetsReaderIntroductionImages: NodeCGTypes.ClientReplicant<NodeCGTypes.AssetFile[]>;
Expand Down Expand Up @@ -47,6 +48,7 @@ export const reps: {
videoPlayer: NodeCGTypes.ClientReplicant<VideoPlayer>;
[k: string]: NodeCGTypes.ClientReplicant<unknown>;
} = {
additionalDonations: nodecg.Replicant('additionalDonations'),
assetsDonationAlertAssets: nodecg.Replicant('assets:donation-alert-assets'),
assetsIntermissionSlides: nodecg.Replicant('assets:intermission-slides'),
assetsReaderIntroductionImages: nodecg.Replicant('assets:reader-introduction-images'),
Expand Down Expand Up @@ -83,6 +85,7 @@ export const reps: {

// All the replicant types.
export interface ReplicantTypes {
additionalDonations: AdditionalDonations;
assetsDonationAlertAssets: NodeCGTypes.AssetFile[];
assetsIntermissionSlides: NodeCGTypes.AssetFile[];
assetsReaderIntroductionImages: NodeCGTypes.AssetFile[];
Expand Down
34 changes: 34 additions & 0 deletions src/dashboard/additional-donations-control/components/Donation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<template>
<div class="d-flex">
{{ donation.description }} - ${{ donation.amount }}
- <v-checkbox class="ma-0 pa-0 ml-1" v-model="toggle" />
</div>
</template>

<script lang="ts">
import MediaCard from '@esa-layouts/dashboard/_misc/components/MediaCard.vue';
import { Component, Prop, Vue } from 'vue-property-decorator';
import { storeModule } from '../store';
@Component({
components: {
MediaCard,
},
})
export default class extends Vue {
@Prop({ type: Object, required: true }) readonly donation!: {
key: string,
description: string,
amount: number,
active: boolean,
};
get toggle(): boolean {
return this.donation.active;
}
set toggle(val: boolean) {
storeModule.toggleItem({ key: this.donation.key, active: val });
nodecg.sendMessage('additionalDonationToggle', { key: this.donation.key, active: val });
}
}
</script>
16 changes: 16 additions & 0 deletions src/dashboard/additional-donations-control/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* eslint no-new: off, @typescript-eslint/explicit-function-return-type: off */

import { setUpReplicants } from '@esa-layouts/browser_shared/replicant_store';
import vuetify from '@esa-layouts/_misc/vuetify';
import Vue from 'vue';
import App from './main.vue';
import store from './store';

setUpReplicants(store).then(() => {
new Vue({
vuetify,
store,
el: '#App',
render: (h) => h(App),
});
});
43 changes: 43 additions & 0 deletions src/dashboard/additional-donations-control/main.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<template>
<v-app>
<div class="mb-2">
<donation
v-for="donation of additionalDonationsMapped"
:key="donation.key"
:donation="donation"
/>
</div>
</v-app>
</template>

<script lang="ts">
import { replicantNS } from '@esa-layouts/browser_shared/replicant_store';
import { AdditionalDonations } from '@esa-layouts/types/schemas';
import { Component, Vue } from 'vue-property-decorator';
import Donation from './components/Donation.vue';
@Component({
components: {
Donation,
},
})
export default class extends Vue {
@replicantNS.State(
(s) => s.reps.additionalDonations,
) readonly additionalDonations!: AdditionalDonations;
additionalDonationsCfg = nodecg.bundleConfig.additionalDonations;
formatAmount(val: number): string {
return val.toLocaleString('en-US', { maximumFractionDigits: 0 });
}
get additionalDonationsMapped() {
return this.additionalDonationsCfg.map((d) => ({
key: d.key,
description: d.description,
amount: d.amount,
active: this.additionalDonations.find((a) => a.key === d.key)?.active ?? false,
}));
}
}
</script>
35 changes: 35 additions & 0 deletions src/dashboard/additional-donations-control/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { replicantModule, ReplicantModule, ReplicantTypes } from '@esa-layouts/browser_shared/replicant_store';
import clone from 'clone';
import Vue from 'vue';
import Vuex, { Store } from 'vuex';
import { getModule, Module, Mutation, VuexModule } from 'vuex-module-decorators';

Vue.use(Vuex);

@Module({ name: 'OurModule' })
class OurModule extends VuexModule {
// Helper getter to return all replicants.
get reps(): ReplicantTypes {
return this.context.rootState.ReplicantModule.reps;
}

@Mutation
toggleItem({ key, active }: { key: string, active: boolean }): void {
const donations = clone(replicantModule.repsTyped.additionalDonations);
const donationIndex = donations.findIndex((d) => d.key === key);
if (donationIndex >= 0) {
donations[donationIndex].active = active;
} else {
donations.push({ key, active });
}
replicantModule.setReplicant({ name: 'additionalDonations', val: donations });
}
}

const store = new Store({
strict: process.env.NODE_ENV !== 'production',
state: {},
modules: { ReplicantModule, OurModule },
});
export default store;
export const storeModule = getModule(OurModule, store);
3 changes: 2 additions & 1 deletion src/extension/util/replicants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable max-len */

import { Bids, BigbuttonPlayerMap, CapturePositions, Commentators, CommentatorsNew, Countdown, CurrentRunDelay, DelayedTimer, DonationAlerts, DonationReader, DonationReaderNew, DonationsToRead, DonationTotal, DonationTotalMilestones, GameLayouts, IntermissionSlides, MusicData, NameCycle, NotableDonations, ObsData, Omnibar, OtherStreamData, Prizes, ReaderIntroduction, ServerTimestamp, StreamDeckData, TaskmasterTimestamps, TtsVoices, UpcomingRunID, VideoPlayer } from '@esa-layouts/types/schemas';
import { AdditionalDonations, Bids, BigbuttonPlayerMap, CapturePositions, Commentators, CommentatorsNew, Countdown, CurrentRunDelay, DelayedTimer, DonationAlerts, DonationReader, DonationReaderNew, DonationsToRead, DonationTotal, DonationTotalMilestones, GameLayouts, IntermissionSlides, MusicData, NameCycle, NotableDonations, ObsData, Omnibar, OtherStreamData, Prizes, ReaderIntroduction, ServerTimestamp, StreamDeckData, TaskmasterTimestamps, TtsVoices, UpcomingRunID, VideoPlayer } from '@esa-layouts/types/schemas';
import type NodeCGTypes from '@nodecg/types';
import { HoraroImportStatus, OengusImportStatus, TwitchAPIData, TwitchChannelInfo } from 'speedcontrol-util/types/schemas';
import { get as nodecg } from './nodecg';
Expand All @@ -9,6 +9,7 @@ import { get as nodecg } from './nodecg';
* This is where you can declare all your replicant to import easily into other files,
* and to make sure they have any correct settings on startup.
*/
export const additionalDonations = nodecg().Replicant<AdditionalDonations>('additionalDonations') as unknown as NodeCGTypes.ServerReplicantWithSchemaDefault<AdditionalDonations>;
export const assetsDonationAlertAssets = nodecg().Replicant<NodeCGTypes.AssetFile[]>('assets:donation-alert-assets') as unknown as NodeCGTypes.ServerReplicantWithSchemaDefault<NodeCGTypes.AssetFile[]>;
export const assetsIntermissionSlides = nodecg().Replicant<NodeCGTypes.AssetFile[]>('assets:intermission-slides') as unknown as NodeCGTypes.ServerReplicantWithSchemaDefault<NodeCGTypes.AssetFile[]>;
export const assetsMediaBoxImages = nodecg().Replicant<NodeCGTypes.AssetFile[]>('assets:media-box-images') as unknown as NodeCGTypes.ServerReplicantWithSchemaDefault<NodeCGTypes.AssetFile[]>;
Expand Down
30 changes: 28 additions & 2 deletions src/graphics/omnibar/components/Total.vue
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@
</template>

<script lang="ts">
import { replicantModule } from '@esa-layouts/browser_shared/replicant_store';
import { replicantModule, replicantNS } from '@esa-layouts/browser_shared/replicant_store';
import { formatUSD } from '@esa-layouts/graphics/_misc/helpers';
import { AdditionalDonations } from '@esa-layouts/types/schemas';
import gsap from 'gsap';
import { Component, Vue } from 'vue-property-decorator';
Expand All @@ -149,9 +150,24 @@ export default class extends Vue {
alertText = '$0';
alertList: { total?: number, amount?: number, showAlert: boolean }[] = [];
donationTotalTimeout: number | undefined;
@replicantNS.State(
(s) => s.reps.additionalDonations,
) readonly additionalDonations!: AdditionalDonations;
additionalDonationsCfg = nodecg.bundleConfig.additionalDonations;
get additionalDonationsMapped() {
return this.additionalDonationsCfg.map((d) => ({
key: d.key,
description: d.description,
amount: d.amount,
active: this.additionalDonations.find((a) => a.key === d.key)?.active ?? false,
}));
}
get rawTotal(): number {
return replicantModule.repsTyped.donationTotal;
const additional = this.additionalDonationsMapped
.filter((d) => d.active).reduce((partialSum, a) => partialSum + a.amount, 0);
return replicantModule.repsTyped.donationTotal + additional;
}
get totalStr(): string {
Expand Down Expand Up @@ -241,6 +257,16 @@ export default class extends Vue {
});
if (!this.playingAlerts) this.playNextAlert(true);
});
nodecg.listenFor('additionalDonationToggle', (data: { key: string, active: boolean }) => {
const donation = this.additionalDonationsMapped.find((d) => d.key === data.key);
if (donation) {
this.alertList.push({
amount: (data.active ? 1 : -1) * donation.amount,
showAlert: data.active,
});
if (!this.playingAlerts) this.playNextAlert(true);
}
});
}
}
</script>
Expand Down
11 changes: 11 additions & 0 deletions src/types/schemas/additionalDonations.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable */
/**
* This file was automatically generated by json-schema-to-typescript.
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
* and run json-schema-to-typescript to regenerate this file.
*/

export type AdditionalDonations = {
key: string;
active: boolean;
}[];
5 changes: 5 additions & 0 deletions src/types/schemas/configschema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ export interface Configschema {
errorDiscordWebhook?: string;
errorDiscordWebhookUserIdToPing?: string;
};
additionalDonations: {
key: string;
description: string;
amount: number;
}[];
}
export interface BidwarBias {
bidId: number;
Expand Down
2 changes: 2 additions & 0 deletions src/types/schemas/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// @ts-ignore
export * from './configschema';
// @ts-ignore
export * from './additionalDonations';
// @ts-ignore
export * from './bids';
// @ts-ignore
export * from './bigbuttonPlayerMap';
Expand Down

0 comments on commit f58be68

Please sign in to comment.