Skip to content

Commit

Permalink
Merge pull request #14 from gunet/gunet-workflows
Browse files Browse the repository at this point in the history
Gunet workflows
  • Loading branch information
kkmanos authored Jun 3, 2024
2 parents 9e6d111 + fc628ce commit fff9210
Show file tree
Hide file tree
Showing 16 changed files with 255 additions and 150 deletions.
30 changes: 15 additions & 15 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
HOST='0.0.0.0'
PORT=3000
REACT_APP_FIREBASE_VAPIDKEY=<Your_Vapid_Key>
REACT_APP_WS_URL=ws://wallet-backend-server:8002
REACT_APP_WALLET_BACKEND_URL=http://wallet-backend-server:8002
REACT_APP_LOGIN_WITH_PASSWORD=false
REACT_APP_FIREBASE_API_KEY=<Your_Firebase_API_Key>
REACT_APP_FIREBASE_AUTH_DOMAIN=<Your_Firebase_Auth_Domain>
REACT_APP_FIREBASE_PROJECT_ID=<Your_Firebase_Project_ID>
REACT_APP_FIREBASE_STORAGE_BUCKET=<Your_Firebase_Storage_Bucket>
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=<Your_Firebase_Messaging_Sender_ID>
REACT_APP_FIREBASE_APP_ID=<Your_Firebase_App_ID>
REACT_APP_FIREBASE_MEASUREMENT_ID=<Your_Firebase_Measurement_ID>
REACT_APP_DID_KEY_VERSION=jwk_jcs-pub
HOST=VAR_HOST
PORT=VAR_PORT
REACT_APP_FIREBASE_VAPIDKEY=VAR_REACT_APP_FIREBASE_VAPIDKEY
REACT_APP_WS_URL=VAR_REACT_APP_WS_URL
REACT_APP_WALLET_BACKEND_URL=VAR_REACT_APP_WALLET_BACKEND_URL
REACT_APP_LOGIN_WITH_PASSWORD=VAR_REACT_APP_LOGIN_WITH_PASSWORD
REACT_APP_FIREBASE_API_KEY=VAR_REACT_APP_FIREBASE_API_KEY
REACT_APP_FIREBASE_AUTH_DOMAIN=VAR_REACT_APP_FIREBASE_AUTH_DOMAIN
REACT_APP_FIREBASE_PROJECT_ID=VAR_REACT_APP_FIREBASE_PROJECT_ID
REACT_APP_FIREBASE_STORAGE_BUCKET=VAR_REACT_APP_FIREBASE_STORAGE_BUCKET
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=VAR_REACT_APP_FIREBASE_MESSAGING_SENDER_ID
REACT_APP_FIREBASE_APP_ID=VAR_REACT_APP_FIREBASE_APP_ID
REACT_APP_FIREBASE_MEASUREMENT_ID=VAR_REACT_APP_FIREBASE_MEASUREMENT_ID
REACT_APP_DID_KEY_VERSION=VAR_REACT_APP_DID_KEY_VERSION
REACT_APP_VERSION=$npm_package_version
REACT_APP_DISPLAY_CONSOLE=true
REACT_APP_DISPLAY_CONSOLE=VAR_REACT_APP_DISPLAY_CONSOLE
20 changes: 20 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Build Docker image
on:
push:
branches-ignore:
- master
- dc4eu-rome
pull_request:

jobs:
build-front:
permissions:
contents: read
packages: read

uses: gunet/wallet-ecosystem/.github/workflows/docker-build-push.yml@dc4eu-rome
secrets: inherit
with:
image-tag: ghcr.io/gunet/wallet-frontend:latest
docker-push: false
dockerfile-path: ./Dockerfile
20 changes: 20 additions & 0 deletions .github/workflows/docker-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Push Docker image
on:
push:
tags:
- v0.*
- v1.*
- v2.*

jobs:
push-front:
permissions:
contents: read
packages: write

uses: gunet/wallet-ecosystem/.github/workflows/docker-build-push.yml@dc4eu-rome
secrets: inherit
with:
image-tag: ghcr.io/gunet/wallet-frontend:${{ github.ref_name }}
docker-push: true
dockerfile-path: ./Dockerfile
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ src/config/config.prod.js
ssl_keys/*
*.tar.gz
.npmrc

variables.vars
8 changes: 6 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ WORKDIR /home/node/app

# Install dependencies first so rebuild of these layers is only needed when dependencies change
COPY package.json yarn.lock .
COPY .env.template .env
RUN --mount=type=secret,id=npmrc,required=true,target=./.npmrc,uid=1000 \
yarn cache clean -f && yarn install

Expand All @@ -28,9 +29,12 @@ FROM nginx:alpine as deploy
WORKDIR /usr/share/nginx/html

COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf

COPY --from=builder /home/node/app/build/ .

COPY ./var_replacement.sh /

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
RUN chmod +x /var_replacement.sh && cat /var_replacement.sh

CMD /bin/sh /var_replacement.sh /variables.vars && nginx -g "daemon off;"
59 changes: 30 additions & 29 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Spinner from './components/Spinner'; // Make sure this Spinner component
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n';

import { CredentialsProvider } from './context/CredentialsContext';
import useCheckURL from './components/useCheckURL'; // Import the custom hook
import handleServerMessagesGuard from './hoc/handleServerMessagesGuard';
import HandlerNotification from './components/HandlerNotification';
Expand Down Expand Up @@ -81,35 +82,35 @@ function App() {
};
return (
<I18nextProvider i18n={i18n}>
<Snowfalling />

<Router>
<Suspense fallback={<Spinner />}>
<HandlerNotification>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/settings" element={<PrivateRoute><Settings /></PrivateRoute>} />
<Route path="/" element={<PrivateRoute><Home /></PrivateRoute>} />
<Route path="/credential/:id" element={<PrivateRoute><CredentialDetail /></PrivateRoute>} />
<Route path="/history" element={<PrivateRoute><History /></PrivateRoute>} />
<Route path="/add" element={<PrivateRoute><AddCredentials /></PrivateRoute>} />
<Route path="/send" element={<PrivateRoute><SendCredentials /></PrivateRoute>} />
<Route path="/verification/result" element={<PrivateRoute><VerificationResult /></PrivateRoute>} />
<Route path="/cb" element={<PrivateRoute><Home /></PrivateRoute>} />
<Route path="*" element={<NotFound />} />
</Routes>
{showSelectCredentialsPopup &&
<SelectCredentialsPopup showPopup={showSelectCredentialsPopup} setShowPopup={setShowSelectCredentialsPopup} setSelectionMap={setSelectionMap} conformantCredentialsMap={conformantCredentialsMap} verifierDomainName={verifierDomainName} />
}
{showPinInputPopup &&
<PinInputPopup showPopup={showPinInputPopup} setShowPopup={setShowPinInputPopup} />
}
{showMessagePopup &&
<MessagePopup type={typeMessagePopup} message={textMessagePopup} onClose={() => setMessagePopup(false)} />
}
</HandlerNotification>
</Suspense>
</Router>
<CredentialsProvider>
<Snowfalling />
<Router>
<Suspense fallback={<Spinner />}>
<HandlerNotification/>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/settings" element={<PrivateRoute><Settings /></PrivateRoute>} />
<Route path="/" element={<PrivateRoute><Home /></PrivateRoute>} />
<Route path="/credential/:id" element={<PrivateRoute><CredentialDetail /></PrivateRoute>} />
<Route path="/history" element={<PrivateRoute><History /></PrivateRoute>} />
<Route path="/add" element={<PrivateRoute><AddCredentials /></PrivateRoute>} />
<Route path="/send" element={<PrivateRoute><SendCredentials /></PrivateRoute>} />
<Route path="/verification/result" element={<PrivateRoute><VerificationResult /></PrivateRoute>} />
<Route path="/cb" element={<PrivateRoute><Home /></PrivateRoute>} />
<Route path="*" element={<NotFound />} />
</Routes>
{showSelectCredentialsPopup &&
<SelectCredentialsPopup showPopup={showSelectCredentialsPopup} setShowPopup={setShowSelectCredentialsPopup} setSelectionMap={setSelectionMap} conformantCredentialsMap={conformantCredentialsMap} verifierDomainName={verifierDomainName} />
}
{showPinInputPopup &&
<PinInputPopup showPopup={showPinInputPopup} setShowPopup={setShowPinInputPopup} />
}
{showMessagePopup &&
<MessagePopup type={typeMessagePopup} message={textMessagePopup} onClose={() => setMessagePopup(false)} />
}
</Suspense>
</Router>
</CredentialsProvider>
</I18nextProvider>
);
}
Expand Down
48 changes: 13 additions & 35 deletions src/components/HandlerNotification.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useContext } from 'react';
import toast, { Toaster } from 'react-hot-toast';
import { onMessageListener } from '../firebase';
import { AiOutlineClose } from 'react-icons/ai';
import logo from '../assets/images/logo.png';
import CredentialsContext from '../context/CredentialsContext';

const ToastDisplay = ({ id, notification }) => {
return (
Expand All @@ -29,16 +30,12 @@ const ToastDisplay = ({ id, notification }) => {
);
};

const HandlerNotification = ({ children }) => {
const HandlerNotification = () => {
const [notification, setNotification] = useState({ title: '', body: '' });
const [isMessageReceived, setMessageReceived] = useState(null);
const { getData } = useContext(CredentialsContext);

const showToast = () =>
toast((t) => <ToastDisplay id={t.id} notification={notification} />, {
onClick: () => {
window.location.href = '/';
},
});
toast((t) => <ToastDisplay id={t.id} notification={notification} />);

useEffect(() => {
if (notification?.title) {
Expand All @@ -47,47 +44,28 @@ const HandlerNotification = ({ children }) => {
}, [notification]);

useEffect(() => {
let messageReceived = false;
const unregisterMessageListener = onMessageListener()
const messageListener = onMessageListener()
.then((payload) => {
// Process the received message
setNotification({
title: payload?.notification?.title,
body: payload?.notification?.body,
});
setMessageReceived(true); // Message has been received
getData();
})
.catch((err) => {
console.log('Failed to receive message:', err);
setMessageReceived(false); // Set isMessageReceived to false if there's an error
});


return () => {
if (!messageReceived) {
setMessageReceived(false); // Set isMessageReceived to false if no message was received before unmount
if (messageListener && typeof messageListener === 'function') {
messageListener();
}
};
}, []);

// Render just children when waiting for message reception
if (isMessageReceived === null || isMessageReceived === false) {
// Render children when waiting for a message
return (
<div>
{children}
</div>
);
} else {
// Render Toaster and children when a message is received
return (
<div>
<Toaster />
{children}
</div>
);
}
}, [getData]);

return (
<Toaster />
);
};

export default HandlerNotification;
2 changes: 1 addition & 1 deletion src/components/Popups/PinInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ function PinInput({ showPopup, setShowPopup }) {
onClick={() => handleInputClick(index)}
onPaste={(e) => handleInputPaste(e.clipboardData.getData('Text'))}
onKeyPress={(e) => handleInputKeyPress(e)}
className="w-10 px-3 mx-1 my-2 py-2 dark:bg-gray-700 dark:text-white border border-gray-300 dark:border-gray-500 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500"
className="w-10 px-3 mx-1 my-2 py-2 dark:bg-gray-700 dark:text-white border border-gray-300 dark:border-gray-500 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:inputDarkModeOverride"
ref={inputRefs[index]}
/>
))}
Expand Down
45 changes: 45 additions & 0 deletions src/context/CredentialsContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { createContext, useState, useEffect, useCallback } from 'react';
import { useApi } from '../api';
import { extractCredentialFriendlyName } from '../functions/extractCredentialFriendlyName';

const CredentialsContext = createContext();

export const CredentialsProvider = ({ children }) => {
const api = useApi();
const [vcEntityList, setVcEntityList] = useState([]);
const [latestCredentials, setLatestCredentials] = useState(new Set());

const getData = useCallback(async () => {
try {
const response = await api.get('/storage/vc');
const fetchedVcList = response.data.vc_list;
const vcEntityList = await Promise.all(fetchedVcList.map(async vcEntity => {
const name = await extractCredentialFriendlyName(vcEntity.credential);
return { ...vcEntity, friendlyName: name };
}));
vcEntityList.sort((vcA, vcB) => new Date(vcB.issuanceDate) - new Date(vcA.issuanceDate));

const latestIssuanceDate = vcEntityList[0]?.issuanceDate;
const latestCreds = new Set(vcEntityList.filter(vc => vc.issuanceDate === latestIssuanceDate).map(vc => vc.id));

if (window.location.pathname.includes('/cb')) {
setLatestCredentials(latestCreds);
setTimeout(() => {
setLatestCredentials(new Set());
}, 4000);
}

setVcEntityList(vcEntityList);
} catch (error) {
console.error('Failed to fetch data', error);
}
}, [api]);

return (
<CredentialsContext.Provider value={{ vcEntityList, latestCredentials, getData }}>
{children}
</CredentialsContext.Provider>
);
};

export default CredentialsContext;
41 changes: 41 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,44 @@ button.reactour__close {
top: 12px;
right: 12px;
}

/* Animations for new credentials */
@keyframes highlight-filter {
0%, 100% {
filter: brightness(1);
}
50% {
filter: brightness(1.17);
}
}

@keyframes fade-in {
0% {
opacity: 0;
transform: translateY(-300px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}

.highlight-filter {
animation: highlight-filter 3s ease-in-out;
}

.fade-in {
animation: fade-in 1s ease-in-out;
}

/* Light and Dark mode input autofill */
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 30px rgb(245, 245, 245) inset !important;
}

@layer components {
.inputDarkModeOverride:-webkit-autofill {
-webkit-box-shadow: 0 0 0 30px rgb(70, 70, 70) inset !important;
-webkit-text-fill-color: white;
}
}
2 changes: 1 addition & 1 deletion src/pages/AddCredentials/AddCredentials.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ const Issuers = () => {
<input
type="text"
placeholder={t('pageAddCredentials.searchPlaceholder')}
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg focus:outline-none focus:ring-blue-500 focus:border-blue-500"
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:inputDarkModeOverride"
value={searchQuery}
onChange={handleSearch}
/>
Expand Down
Loading

0 comments on commit fff9210

Please sign in to comment.