diff --git a/assets/images/gifs/menhera-chan.gif b/assets/images/gifs/menhera-chan.gif new file mode 100644 index 00000000..de96c744 Binary files /dev/null and b/assets/images/gifs/menhera-chan.gif differ diff --git a/assets/images/gifs/menhera-sad.gif b/assets/images/gifs/menhera-sad.gif new file mode 100644 index 00000000..efe7f6b7 Binary files /dev/null and b/assets/images/gifs/menhera-sad.gif differ diff --git a/assets/images/gifs/txt-bg.gif b/assets/images/gifs/txt-bg.gif new file mode 100644 index 00000000..9f97ff43 Binary files /dev/null and b/assets/images/gifs/txt-bg.gif differ diff --git a/assets/jsons/patreons.json b/assets/jsons/patreons.json index 1610ea14..0637a088 100644 --- a/assets/jsons/patreons.json +++ b/assets/jsons/patreons.json @@ -1,3 +1 @@ -[ - -] \ No newline at end of file +[] \ No newline at end of file diff --git a/src/main/ipcs/index.ts b/src/main/ipcs/index.ts index 356c0bac..e008ce72 100644 --- a/src/main/ipcs/index.ts +++ b/src/main/ipcs/index.ts @@ -4,3 +4,4 @@ import './bs-launcher-ipcs'; import './bs-version-ipcs'; import './bs-uninstall-ipcs'; import './map-ipcs'; +import './supporters-ipcs'; diff --git a/src/main/ipcs/supporters-ipcs.ts b/src/main/ipcs/supporters-ipcs.ts new file mode 100644 index 00000000..21dc0a01 --- /dev/null +++ b/src/main/ipcs/supporters-ipcs.ts @@ -0,0 +1,15 @@ +import { ipcMain } from "electron"; +import { SupportersService } from "../services/supporters.service"; +import { IpcRequest } from "shared/models/ipc"; +import { UtilsService } from '../services/utils.service'; + +ipcMain.on("get-supporters", (event, request: IpcRequest) => { + const utils = UtilsService.getInstance(); + const supportersService = SupportersService.getInstance(); + + supportersService.getSupporters().then(supporters => { + utils.ipcSend(request.responceChannel, {success: true, data: supporters}); + }).catch(() => { + utils.ipcSend(request.responceChannel, {success: false}); + }); +}); \ No newline at end of file diff --git a/src/main/services/supporters.service.ts b/src/main/services/supporters.service.ts new file mode 100644 index 00000000..5bb59130 --- /dev/null +++ b/src/main/services/supporters.service.ts @@ -0,0 +1,71 @@ +import { writeFileSync } from "fs"; +import { get } from "https"; +import isOnline from "is-online"; +import path from "path"; +import { Supporter } from "shared/models/supporters/supporter.interface"; +import { UtilsService } from "./utils.service"; + +export class SupportersService { + + private readonly PATREONS_URL = "https://raw.githubusercontent.com/Zagrios/bs-manager/master/assets/jsons/patreons.json" + private readonly PATREONS_FILE = "patreons.json"; + + private static instance: SupportersService; + + private cache: Supporter[]; + + private readonly utilsService: UtilsService; + + public static getInstance(): SupportersService{ + if(!SupportersService.instance){ SupportersService.instance = new SupportersService(); } + return SupportersService.instance; + } + + private constructor(){ + this.utilsService = UtilsService.getInstance(); + } + + private async updateLocalSupporters(supporters: Supporter[]): Promise{ + const patreonsPath = path.join(this.utilsService.getAssestsJsonsPath(), this.PATREONS_FILE); + writeFileSync(patreonsPath, JSON.stringify(supporters, null, "\t"), {encoding: 'utf-8', flag: 'w'}); + } + + private async getRemoteSupporters(): Promise{ + return new Promise((resolve, reject) => { + let body = '' + get(this.PATREONS_URL, (res) => { + res.on('data', chunk => body += chunk); + res.on('end', () => resolve(JSON.parse(body))); + res.on('error', () => reject(null)) + }) + }) + } + + private async getLocalSupporters(): Promise{ + const patreonsPath = path.join(this.utilsService.getAssestsJsonsPath(), this.PATREONS_FILE); + const rawPatreons = (await this.utilsService.readFileAsync(patreonsPath)).toString(); + return JSON.parse(rawPatreons); + } + + private async loadSupporters(): Promise{ + if(!!this.cache && this.cache.length){ return this.cache; } + + const isOnlineRes = await isOnline({timeout: 1500}); + + const [localVersions, remoteVersions] = await Promise.all([ + this.getLocalSupporters(), (isOnlineRes && this.getRemoteSupporters()) + ]); + + const supporters = remoteVersions?.length ? remoteVersions : localVersions; + + if(!!remoteVersions && remoteVersions.length){ this.updateLocalSupporters(remoteVersions); } + + this.cache = supporters; + return this.cache; + } + + public getSupporters(): Promise{ + return this.loadSupporters(); + } + +} \ No newline at end of file diff --git a/src/renderer/components/settings/supporters-view/supporter-item.component.tsx b/src/renderer/components/settings/supporters-view/supporter-item.component.tsx new file mode 100644 index 00000000..af49e4cb --- /dev/null +++ b/src/renderer/components/settings/supporters-view/supporter-item.component.tsx @@ -0,0 +1,19 @@ +import { CSSProperties } from "react"; +import { Supporter } from "shared/models/supporters"; +import { motion } from "framer-motion"; +import txtBg from "../../../../../assets/images/gifs/txt-bg.gif" + +interface Props { supporter: Supporter, delay?: number } + +export function SupporterItem({supporter, delay}: Props) { + + const additionnalStyles: CSSProperties = (() => { + if(supporter.type === "gold"){ return {color: "#ffe270", textShadow: "0px 0px 10px #ffdd59", backgroundImage: `url(${txtBg})`, backgroundSize: "70% 15px", backgroundRepeat: "no-repeat", backgroundPosition: "center"}; } + if(supporter.type === "diamond"){ return {color: "#e574fc", textShadow: "0px 0px 15px #e056fd", backgroundImage: `url(${txtBg})`, backgroundSize: "70% 19px", backgroundRepeat: "no-repeat", backgroundPosition: "center"}; } + return {}; + })() + + return ( + {supporter.username} + ) +} diff --git a/src/renderer/components/settings/supporters-view/supporters-block.component.tsx b/src/renderer/components/settings/supporters-view/supporters-block.component.tsx new file mode 100644 index 00000000..caa4f931 --- /dev/null +++ b/src/renderer/components/settings/supporters-view/supporters-block.component.tsx @@ -0,0 +1,22 @@ +import { Supporter } from "shared/models/supporters"; +import { SupporterItem } from "./supporter-item.component"; + +interface Props {className?: string, title: string, supporters: Supporter[]} + +export function SupportersBlock({className, title, supporters}: Props) { + + const someDelay = () => { + const [min, max] = [0, .55] + const baseDelay = .15; + return baseDelay + Math.random() * (max - min) + min; + } + + return ( +
+

{title}

+
+ {supporters.map(s => )} +
+
+ ) +} diff --git a/src/renderer/components/settings/supporters-view/supporters-view.component.tsx b/src/renderer/components/settings/supporters-view/supporters-view.component.tsx new file mode 100644 index 00000000..961c0467 --- /dev/null +++ b/src/renderer/components/settings/supporters-view/supporters-view.component.tsx @@ -0,0 +1,49 @@ +import { motion, AnimatePresence } from "framer-motion" +import { useEffect, useState } from "react"; +import { BsmButton } from "renderer/components/shared/bsm-button.component" +import { Supporter } from "shared/models/supporters"; +import { SupportersService } from "renderer/services/supporters.service"; +import { SupportersBlock } from "./supporters-block.component"; +import ManheraChanGif from "../../../../../assets/images/gifs/menhera-chan.gif" +import ManheraSadGif from "../../../../../assets/images/gifs/menhera-sad.gif" + +interface Props {isVisible: boolean, setVisible: (b: boolean) => void} + +export function SupportersView({isVisible, setVisible}: Props) { + + const supportersService = SupportersService.getInstance(); + + const [supporters, setSupporters] = useState([] as Supporter[]); + const [sponsors, setSponsors] = useState([] as Supporter[]); + + useEffect(() => { + if(isVisible){ + supportersService.getSupporters().then(supporters => { + setSponsors(supporters.filter(s => s.type === "sponsor")); + setSupporters(supporters.filter(s => !s.type || s.type !== "sponsor")); + }); + } + }, [isVisible]) + + + return ( + + ({isVisible && + + setVisible(false)}> + {(!!sponsors.length || !!supporters.length) && } +
+ {!!sponsors.length && } + {!!supporters.length && } + {(!sponsors.length && !supporters.length) && ( + <> +

No supporter yet

+ + + )} +
+
+ }) +
+ ) +} diff --git a/src/renderer/pages/settings-page.component.tsx b/src/renderer/pages/settings-page.component.tsx index 3535908d..f076111d 100644 --- a/src/renderer/pages/settings-page.component.tsx +++ b/src/renderer/pages/settings-page.component.tsx @@ -16,6 +16,7 @@ import { ModalExitCode, ModalService, ModalType } from "renderer/services/modale import { NotificationService } from "renderer/services/notification.service"; import { ProgressBarService } from "renderer/services/progress-bar.service"; import { ThemeService } from "renderer/services/theme.service"; +import { SupportersView } from "renderer/components/settings/supporters-view/supporters-view.component"; export function SettingsPage() { @@ -45,6 +46,7 @@ export function SettingsPage() { const[themeIdSelected, setThemeIdSelected]= useState(themeItem.find(e => e.value === themeService.getTheme()).id); const[languageSelected, setLanguageSelected]= useState(languagesItems.find(e => e.value === i18nService.currentLanguage).id); const [installationFolder, setInstallationFolder] = useState(null); + const [showSupporters, setShowSupporters] = useState(true); useEffect(() => { loadInstallationFolder(); @@ -110,49 +112,56 @@ export function SettingsPage() { ipcService.sendLazy("new-window", {args: "https://www.patreon.com/bsmanager?fan_landing=true"}) } - return ( -
+ const toogleShowSupporters = () => { + setShowSupporters(show => !show); + } -
+ return ( +
- - - +
+ + + + + + +
+ + +
+ +
+
+ + + +
+ + +
+ {installationFolder} + +
+
+ + + + + + +
+ + +
+
+ +
- -
- - -
-
-
- - - -
- -
- {installationFolder} - + +
-
- - - - - - -
- - -
-
- -
- -
-
- ) + ) } diff --git a/src/renderer/services/supporters.service.ts b/src/renderer/services/supporters.service.ts new file mode 100644 index 00000000..fb5bfa73 --- /dev/null +++ b/src/renderer/services/supporters.service.ts @@ -0,0 +1,27 @@ +import { Supporter } from "shared/models/supporters"; +import { IpcService } from "./ipc.service"; + + +export class SupportersService { + + private static instance: SupportersService; + + private ipcService: IpcService; + + private constructor(){ + this.ipcService = IpcService.getInstance(); + } + + public static getInstance(): SupportersService{ + if(!SupportersService.instance){ SupportersService.instance = new SupportersService(); } + return SupportersService.instance; + } + + public getSupporters(): Promise{ + return this.ipcService.send("get-supporters").then(res => { + if(!res.success){ return null; } + return res.data; + }); + } + +} \ No newline at end of file diff --git a/src/shared/models/supporters/index.ts b/src/shared/models/supporters/index.ts new file mode 100644 index 00000000..cf537418 --- /dev/null +++ b/src/shared/models/supporters/index.ts @@ -0,0 +1,2 @@ +export { SupporterType } from "./supporter.type"; +export { Supporter } from "./supporter.interface"; \ No newline at end of file diff --git a/src/shared/models/supporters/supporter.interface.ts b/src/shared/models/supporters/supporter.interface.ts new file mode 100644 index 00000000..f3ed768a --- /dev/null +++ b/src/shared/models/supporters/supporter.interface.ts @@ -0,0 +1,8 @@ +import { SupporterType } from "./supporter.type"; + +export interface Supporter { + username: string, + type?: SupporterType, + link?: string, + image?: string, +} \ No newline at end of file diff --git a/src/shared/models/supporters/supporter.model.ts b/src/shared/models/supporters/supporter.model.ts new file mode 100644 index 00000000..e8742c77 --- /dev/null +++ b/src/shared/models/supporters/supporter.model.ts @@ -0,0 +1,19 @@ +import { SupporterType } from "./supporter.type"; +import { SupporterInterface } from "./supporter.interface"; + +export class SupporterModel{ + constructor(private args: SupporterInterface){} + + public get username(): string{ return this.args.username; } + public get type(): SupporterType{ return this.args.type; } + + public get link(): string{ + if(this.type === "basic"){ return null; } + return this.args.link; + } + + public get image(): string{ + if(this.type !== "sponsor"){ return null; } + return this.args.image; + } +} \ No newline at end of file diff --git a/src/shared/models/supporters/supporter.type.ts b/src/shared/models/supporters/supporter.type.ts new file mode 100644 index 00000000..6aeb4268 --- /dev/null +++ b/src/shared/models/supporters/supporter.type.ts @@ -0,0 +1 @@ +export type SupporterType = "gold"|"diamond"|"sponsor" \ No newline at end of file