Skip to content

Commit

Permalink
Merge branch 'main' of github.com:valory-xyz/olas-operate-app
Browse files Browse the repository at this point in the history
  • Loading branch information
truemiller committed May 23, 2024
2 parents 9f30e38 + 02b474d commit 0115447
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 17 deletions.
12 changes: 6 additions & 6 deletions electron/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ let tray,
nextAppProcess,
nextAppProcessPid;

function showNotification(title, body) {
new Notification({ title, body }).show();
}

async function beforeQuit() {
if (operateDaemonPid) {
try {
Expand Down Expand Up @@ -220,12 +224,8 @@ const createMainWindow = () => {
mainWindow.setSize(width, height);
});

ipcMain.on('notify-agent-running', () => {
if (!mainWindow.isVisible()) {
new Notification({
title: 'Your agent is now running!',
}).show();
}
ipcMain.on('show-notification', (title, description) => {
showNotification(title, description || undefined);
});

mainWindow.webContents.on('did-fail-load', () => {
Expand Down
3 changes: 2 additions & 1 deletion electron/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ contextBridge.exposeInMainWorld('electronAPI', {
closeApp: () => ipcRenderer.send('close-app'),
minimizeApp: () => ipcRenderer.send('minimize-app'),
setAppHeight: (height) => ipcRenderer.send('set-height', height),
notifyAgentRunning: () => ipcRenderer.send('notify-agent-running'),
showNotification: (title, description) =>
ipcRenderer.send('show-notification', title, description),
});
11 changes: 4 additions & 7 deletions frontend/components/Main/MainHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { InfoCircleOutlined } from '@ant-design/icons';
import { Badge, Button, Flex, Popover, Typography } from 'antd';
import { formatUnits } from 'ethers/lib/utils';
import get from 'lodash/get';
import Image from 'next/image';
import { useCallback, useEffect, useMemo, useState } from 'react';

import { Chain, DeploymentStatus } from '@/client';
import { setTrayIcon } from '@/common-util';
import { COLOR, LOW_BALANCE, SERVICE_TEMPLATES } from '@/constants';
import { useBalance, useServiceTemplates } from '@/hooks';
import { useElectronApi } from '@/hooks/useElectronApi';
import { useServices } from '@/hooks/useServices';
import { useWallet } from '@/hooks/useWallet';
import { ServicesService } from '@/service';
Expand All @@ -25,13 +25,9 @@ enum ServiceButtonLoadingState {
NotLoading,
}

const notifyAgentRunning = () => {
const fn = get(window, 'electronAPI.notifyAgentRunning') ?? (() => null);
return fn();
};

export const MainHeader = () => {
const { services, serviceStatus, setServiceStatus } = useServices();
const { showNotification } = useElectronApi();
const { getServiceTemplates } = useServiceTemplates();
const { wallets, masterSafeAddress } = useWallet();
const {
Expand Down Expand Up @@ -118,7 +114,7 @@ export const MainHeader = () => {
setServiceStatus(DeploymentStatus.DEPLOYED);
setIsBalancePollingPaused(false);
setServiceButtonState(ServiceButtonLoadingState.NotLoading);
notifyAgentRunning();
showNotification?.('Your agent is now running!');
});
} catch (error) {
setIsBalancePollingPaused(false);
Expand All @@ -130,6 +126,7 @@ export const MainHeader = () => {
setIsBalancePollingPaused,
setServiceStatus,
wallets,
showNotification,
]);

const handlePause = useCallback(() => {
Expand Down
107 changes: 105 additions & 2 deletions frontend/components/Main/MainRewards.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { Col, Flex, Row, Skeleton, Tag, Typography } from 'antd';
import { Button, Col, Flex, Modal, Row, Skeleton, Tag, Typography } from 'antd';
import Image from 'next/image';
import { useCallback, useEffect, useState } from 'react';
import styled from 'styled-components';

import { balanceFormat } from '@/common-util';
import { COLOR } from '@/constants';
import { useBalance } from '@/hooks';
import { useElectronApi } from '@/hooks/useElectronApi';
import { useReward } from '@/hooks/useReward';

const { Text } = Typography;
import { ConfettiAnimation } from '../common/ConfettiAnimation';

const { Text, Title } = Typography;

const RewardsRow = styled(Row)`
margin: 0 -24px;
Expand Down Expand Up @@ -70,8 +75,106 @@ const DisplayRewards = () => {
);
};

const NotifyRewards = () => {
const { isEligibleForRewards, availableRewardsForEpochEth } = useReward();
const { totalOlasBalance } = useBalance();
const { showNotification } = useElectronApi();

const [canShowNotification, setCanShowNotification] = useState(false);

useEffect(() => {
// TODO: Implement this once state persistence is available
const hasAlreadyNotified = true;

if (!isEligibleForRewards) return;
if (hasAlreadyNotified) return;
if (!availableRewardsForEpochEth) return;

setCanShowNotification(true);
}, [isEligibleForRewards, availableRewardsForEpochEth, showNotification]);

// hook to show app notification
useEffect(() => {
if (!canShowNotification) return;

showNotification?.(
'Your agent earned its first staking rewards!',
`Congratulations! Your agent just got the first reward for you! Your current balance: ${availableRewardsForEpochEth} OLAS`,
);
}, [canShowNotification, availableRewardsForEpochEth, showNotification]);

const closeNotificationModal = useCallback(() => {
setCanShowNotification(false);
// TODO: add setter for hasAlreadyNotified
}, []);

if (!canShowNotification) return null;

return (
<Modal
open={canShowNotification}
width={400}
onCancel={closeNotificationModal}
footer={[
<Button
key="back"
type="primary"
block
size="large"
className="mt-8"
disabled
// TODO: add twitter share functionality
>
<Flex align="center" justify="center" gap={2}>
Share on
<Image
src="/twitter.svg"
width={24}
height={24}
alt="Share on twitter"
/>
</Flex>
</Button>,
]}
>
<ConfettiAnimation />

<Flex align="center" justify="center">
<Image
src="/splash-robot-head.png"
width={100}
height={100}
alt="OLAS logo"
/>
</Flex>

<Title level={5} className="mt-12">
Your agent just earned the first reward!
</Title>

<Flex vertical gap={16}>
<Text>
Congratulations! Your agent just earned the first
<Text strong>
{` ${balanceFormat(availableRewardsForEpochEth, 2)} OLAS `}
</Text>
for you!
</Text>

<Text>
Your current balance:
<Text strong>{` ${balanceFormat(totalOlasBalance, 2)} OLAS `}</Text>
</Text>

<Text>Keep it running to get even more!</Text>
</Flex>
</Modal>
);
};

export const MainRewards = () => (
<>
<DisplayRewards />
<NotifyRewards />
</>
);
43 changes: 43 additions & 0 deletions frontend/components/common/ConfettiAnimation.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useCallback, useRef } from 'react';
import ReactCanvasConfetti from 'react-canvas-confetti';
import { useInterval } from 'usehooks-ts';

const canvasStyles = {
position: 'fixed',
pointerEvents: 'none',
width: '100%',
height: '100%',
top: 0,
left: 0,
};

export const ConfettiAnimation = () => {
const animationInstance = useRef(null);

const makeShot = useCallback((particleRatio, opts) => {
if (!animationInstance.current) return;

animationInstance.current({
...opts,
origin: { y: 0.45 },
particleCount: Math.floor(200 * particleRatio),
});
}, []);

const fire = useCallback(() => {
makeShot(0.25, { spread: 26, startVelocity: 55 });
makeShot(0.2, { spread: 60 });
makeShot(0.35, { spread: 80, decay: 0.91, scalar: 0.8 });
makeShot(0.1, { spread: 100, startVelocity: 25, decay: 0.92, scalar: 1.2 });
makeShot(0.1, { spread: 100, startVelocity: 45 });
}, [makeShot]);

const getInstance = useCallback((instance) => {
animationInstance.current = instance;
}, []);

// Fire confetti every 2.5 seconds
useInterval(() => fire(), 2500);

return <ReactCanvasConfetti refConfetti={getInstance} style={canvasStyles} />;
};
3 changes: 3 additions & 0 deletions frontend/context/ElectronApiProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ type ElectronApiContextProps = {
setAppHeight?: (height: number) => void;
closeApp?: () => void;
minimizeApp?: () => void;
showNotification?: (title: string, body?: string) => void;
};

export const ElectronApiContext = createContext<ElectronApiContextProps>({
setAppHeight: undefined,
closeApp: undefined,
minimizeApp: undefined,
showNotification: undefined,
});

const getElectronApiFunction = (functionNameInWindow: string) => {
Expand All @@ -33,6 +35,7 @@ export const ElectronApiProvider = ({ children }: PropsWithChildren) => {
setAppHeight: getElectronApiFunction('setAppHeight'),
closeApp: getElectronApiFunction('closeApp'),
minimizeApp: getElectronApiFunction('minimizeApp'),
showNotification: getElectronApiFunction('showNotification'),
}}
>
{children}
Expand Down
3 changes: 2 additions & 1 deletion frontend/hooks/useElectronApi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { useContext } from 'react';
import { ElectronApiContext } from '@/context/ElectronApiProvider';

export const useElectronApi = () => {
const { setAppHeight, closeApp, minimizeApp } =
const { setAppHeight, closeApp, minimizeApp, showNotification } =
useContext(ElectronApiContext);

return {
setAppHeight,
closeApp,
minimizeApp,
showNotification,
};
};
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"next": "^14.2.3",
"react": "^18",
"react-dom": "^18",
"react-canvas-confetti": "1.2.1",
"sass": "^1.72.0",
"styled-components": "^6.1.8",
"usehooks-ts": "^2.14.0"
Expand Down
Binary file added frontend/public/splash-robot-head.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/public/twitter.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions frontend/styles/globals.scss
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ button, input, select, textarea, .ant-input-suffix {
margin-bottom: auto !important;
}

.mt-8 {
margin-top: 8px !important;
}

.mt-12 {
margin-top: 12px !important;
}

.mx-auto {
margin-left: auto !important;
margin-right: auto !important;
Expand Down
18 changes: 18 additions & 0 deletions frontend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,11 @@
dependencies:
"@babel/types" "^7.20.7"

"@types/[email protected]":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@types/canvas-confetti/-/canvas-confetti-1.4.0.tgz#22127a1a9ed9d456e626d6e2b9a4d3b0a240e18b"
integrity sha512-Neq4mvVecrHmTdyo98EY5bnKCjkZGQ6Ma7VyOrxIcMHEZPmt4kfquccqfBMrpNrdryMHgk3oGQi7XtpZacltnw==

"@types/graceful-fs@^4.1.3":
version "4.1.9"
resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz"
Expand Down Expand Up @@ -2152,6 +2157,11 @@ caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001587:
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz"
integrity sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA==

[email protected]:
version "1.4.0"
resolved "https://registry.yarnpkg.com/canvas-confetti/-/canvas-confetti-1.4.0.tgz#840f6db4a566f8f32abe28c00dcd82acf39c92bd"
integrity sha512-S18o4Y9PqI/uabdlT/jI3MY7XBJjNxnfapFIkjkMwpz6qNxLFZOm2b22OMf4ZYDL9lpNWI+Ih4fEMVPwO1KHFQ==

chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
Expand Down Expand Up @@ -5209,6 +5219,14 @@ rc-virtual-list@^3.11.1, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2:
rc-resize-observer "^1.0.0"
rc-util "^5.36.0"

[email protected]:
version "1.2.1"
resolved "https://registry.yarnpkg.com/react-canvas-confetti/-/react-canvas-confetti-1.2.1.tgz#22ac64cbc478cf57cb5f61c130322e359b35389d"
integrity sha512-onNjQNkhQjrB2JVCeRpJW7o8VlBNJvo3Eht9zlQlofNLVvvOd1+PzBLU5Ev8n5sFBkTbKJmIUVG0eZnkAl5cpg==
dependencies:
"@types/canvas-confetti" "1.4.0"
canvas-confetti "1.4.0"

react-dom@^18:
version "18.2.0"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz"
Expand Down

0 comments on commit 0115447

Please sign in to comment.