Skip to content

Commit

Permalink
Merge pull request #8 from gunet/wwwallet-sync
Browse files Browse the repository at this point in the history
Wwwallet sync
  • Loading branch information
kkmanos authored Nov 8, 2024
2 parents b7599f5 + 2ee65ef commit b793a6f
Show file tree
Hide file tree
Showing 32 changed files with 360 additions and 258 deletions.
3 changes: 2 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ indent_style = tab
[{package.json,tsconfig.json,.vscode/settings.json}]
indent_style = space

[*.{yml,yaml}] # YAML does not allow tab indentation
# YAML does not allow tab indentation
[*.{yml,yaml}]
indent_style = space
indent_size = 2
12 changes: 12 additions & 0 deletions src/assets/images/custom_template.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/components/Auth/LoginLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export default function LoginLayout({ children, heading }: { children: React.Rea
const { t } = useTranslation();
return (
<section className="bg-gray-100 dark:bg-gray-900 h-full">
<div className='h-max min-h-screen'>
<div className="flex flex-col items-center justify-center px-6 py-8 mx-auto min-h-[95vh]">
<div className='h-max min-h-dvh'>
<div className="flex flex-col items-center justify-center px-6 py-8 mx-auto min-h-dvh">
<a href="/" className="flex justify-center mb-6 text-2xl font-semibold text-gray-900 dark:text-white">
<img className="w-40" src={logo} alt="logo" />
</a>
Expand Down
18 changes: 2 additions & 16 deletions src/components/Credentials/CredentialImage.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { useState, useEffect, useContext } from "react";
import StatusRibbon from '../../components/Credentials/StatusRibbon';
import ContainerContext from '../../context/ContainerContext';
import RenderSvgTemplate from "./RenderSvgTemplate";

const CredentialImage = ({ credential, className, onClick, showRibbon = true }) => {
const [parsedCredential, setParsedCredential] = useState(null);
const [svgImage, setSvgImage] = useState(null);
const container = useContext(ContainerContext);

useEffect(() => {
Expand All @@ -20,25 +18,13 @@ const CredentialImage = ({ credential, className, onClick, showRibbon = true })

}, [credential, container]);

const handleSvgGenerated = (svgUri) => {
setSvgImage(svgUri);
};

return (
<>
{parsedCredential && parsedCredential.credentialImage && parsedCredential.credentialImage.credentialImageSvgTemplateURL ? (
<>
<RenderSvgTemplate credential={parsedCredential} onSvgGenerated={handleSvgGenerated} />
{parsedCredential && svgImage && (
<img src={svgImage} alt={"Credential"} className={className} onClick={onClick} />
)}
</>
) : parsedCredential && parsedCredential.credentialImage && parsedCredential.credentialImage.credentialImageURL && (
{parsedCredential && parsedCredential.credentialImage && (
<img src={parsedCredential.credentialImage.credentialImageURL} alt={"Credential"} className={className} onClick={onClick} />
)}

{parsedCredential && showRibbon &&
<StatusRibbon credential={credential} />
<StatusRibbon parsedCredential={parsedCredential} />
}
</>
);
Expand Down
62 changes: 62 additions & 0 deletions src/components/Credentials/RenderCustomSvgTemplate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import axios from 'axios';
import jsonpointer from 'jsonpointer';
import { formatDate } from '../../functions/DateFormat';
import customTemplate from '../../assets/images/custom_template.svg';

async function getBase64Image(url) {
if (!url) return null;
try {
const response = await axios.get(url, { responseType: 'blob' });
const blob = response.data;

return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
} catch (error) {
console.error("Failed to load image", url);
return null;
}
}

const renderCustomSvgTemplate = async ({ beautifiedForm, name, description, logoURL, logoAltText, backgroundColor, textColor, backgroundImageURL }) => {
try {
const response = await fetch(customTemplate);
if (!response.ok) throw new Error("Failed to fetch SVG template");

let svgContent = await response.text();

const backgroundImageBase64 = await getBase64Image(backgroundImageURL);
const logoBase64 = await getBase64Image(logoURL);

svgContent = svgContent
.replace(/{{backgroundColor}}/g, backgroundColor)
.replace(
/{{backgroundImageBase64}}/g,
backgroundImageBase64
? `<image xlink:href="${backgroundImageBase64}" x="0" y="0" width="100%" height="100%" preserveAspectRatio="xMidYMid slice" />`
: ''
)
.replace(
/{{logoBase64}}/g,
logoBase64
? `<image xlink:href="${logoBase64}" x="50" y="380" height="20%"><title>${logoAltText}</title></image>`
: ''
)
.replace(/{{name}}/g, name)
.replace(/{{textColor}}/g, textColor)
.replace(/{{description}}/g, description);

const expiryDate = jsonpointer.get(beautifiedForm, "/expiry_date");
svgContent = svgContent.replace(/{{\/expiry_date}}/g, expiryDate ? `Expiry Date: ${formatDate(expiryDate, 'date')}` : '');

return `data:image/svg+xml;utf8,${encodeURIComponent(svgContent)}`;
} catch (error) {
console.error("Error rendering SVG template", error);
return null;
}
};

export default renderCustomSvgTemplate;
68 changes: 27 additions & 41 deletions src/components/Credentials/RenderSvgTemplate.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,34 @@
import { useState, useEffect } from 'react';
import axios from 'axios';
import jsonpointer from 'jsonpointer';
import { formatDate } from '../../functions/DateFormat';

const RenderSvgTemplate = ({ credential, onSvgGenerated }) => {
const [svgContent, setSvgContent] = useState(null);

useEffect(() => {
const fetchSvgContent = async () => {
try {
const response = await fetch(credential.credentialImage.credentialImageSvgTemplateURL);
if (!response.ok) {
throw new Error(`Failed to fetch SVG from ${credential.credentialImage.credentialImageSvgTemplateURL}`);
}

const svgText = await response.text();
setSvgContent(svgText);
} catch (error) {
console.error(error);
}
};

if (credential.credentialImage.credentialImageSvgTemplateURL) {
fetchSvgContent();
const renderSvgTemplate = async ({ beautifiedForm, credentialImageSvgTemplateURL }) => {
let svgContent = null;
try {
const response = await axios.get(credentialImageSvgTemplateURL);
if (response.status !== 200) {
throw new Error(`Failed to fetch SVG`);
}
}, [credential.credentialImage.credentialImageSvgTemplateURL]);

useEffect(() => {
if (svgContent) {
const regex = /{{([^}]+)}}/g;

const replacedSvgText = svgContent.replace(regex, (_match, content) => {
let res = jsonpointer.get(credential.beautifiedForm, content.trim());
if (res !== undefined) {
res = formatDate(res, 'date');
return res;
}
return '-';
});
const dataUri = `data:image/svg+xml;utf8,${encodeURIComponent(replacedSvgText)}`;
onSvgGenerated(dataUri);
}
}, [svgContent, credential.beautifiedForm, onSvgGenerated]);
svgContent = response.data;
} catch (error) {
return null; // Return null if fetching fails
}

if (svgContent) {
const regex = /{{([^}]+)}}/g;
const replacedSvgText = svgContent.replace(regex, (_match, content) => {
let res = jsonpointer.get(beautifiedForm, content.trim());
if (res !== undefined) {
res = formatDate(res, 'date');
return res;
}
return '-';
});
const dataUri = `data:image/svg+xml;utf8,${encodeURIComponent(replacedSvgText)}`;
return dataUri; // Return the data URI for the SVG
}

return null;
return null; // Return null if no SVG content is available
};

export default RenderSvgTemplate;
export default renderSvgTemplate;
22 changes: 3 additions & 19 deletions src/components/Credentials/StatusRibbon.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,11 @@
// StatusRibbon.js
import React, { useEffect, useState, useContext } from 'react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import ContainerContext from '../../context/ContainerContext';
import {CheckExpired} from '../../functions/CheckExpired';
import { CheckExpired } from '../../functions/CheckExpired';

const StatusRibbon = ({ credential }) => {
const StatusRibbon = ({ parsedCredential }) => {
const { t } = useTranslation();

const [parsedCredential, setParsedCredential] = useState(null);
const container = useContext(ContainerContext);

useEffect(() => {
if (container) {
container.credentialParserRegistry.parse(credential).then((c) => {
if ('error' in c) {
return;
}
setParsedCredential(c.beautifiedForm);
});
}

}, [credential, container]);

return (
<>
{parsedCredential && CheckExpired(parsedCredential.expiry_date) &&
Expand Down
1 change: 0 additions & 1 deletion src/components/Layout/Header.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useState, useEffect } from 'react';
import { AiOutlineMenu } from "react-icons/ai";
import { useNavigate, useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import ConnectionStatusIcon from './Navigation/ConnectionStatusIcon';
Expand Down
2 changes: 1 addition & 1 deletion src/components/Layout/Layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const Layout = ({ children }) => {
const toggleSidebar = () => setIsOpen(!isOpen);

return (
<div className="flex justify-end min-h-screen flex-col md:flex-row bg-gray-100 dark:bg-gray-900">
<div className="flex justify-end min-h-dvh flex-col md:flex-row bg-gray-100 dark:bg-gray-900">
<Sidebar isOpen={isOpen} toggle={toggleSidebar} />

{/* Header */}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Layout/Navigation/BottomNav.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext, useState } from 'react';
import React, { useContext } from 'react';
import { FaWallet, FaUserCircle } from "react-icons/fa";
import { IoIosAddCircle, IoIosSend } from "react-icons/io";
import { useLocation, useNavigate } from 'react-router-dom';
Expand Down
6 changes: 3 additions & 3 deletions src/components/Layout/Navigation/Sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const NavItem = ({
};

const Sidebar = ({ isOpen, toggle }) => {
const { isOnline, updateAvailable } = useContext(StatusContext);
const { updateAvailable } = useContext(StatusContext);
const { api, logout } = useContext(SessionContext);
const { username, displayName } = api.getSession();
const location = useLocation();
Expand All @@ -56,8 +56,8 @@ const Sidebar = ({ isOpen, toggle }) => {
return (
<div
className={`${isOpen && screenType !== 'desktop'
? 'w-full flex flex-col justify-between fixed h-screen z-30 bg-primary dark:bg-primary-hover text-white p-4 pb-24 md:pb-0 overflow-y-auto'
: 'hidden w-auto md:flex md:flex-col justify-between sticky top-0 bg-primary dark:bg-primary-hover w-auto text-white h-screen py-10 px-10 overflow-y-auto'
? 'w-full flex flex-col justify-between fixed h-dvh z-30 bg-primary dark:bg-primary-hover text-white p-4 pb-24 md:pb-0 overflow-y-auto'
: 'hidden w-auto md:flex md:flex-col justify-between sticky top-0 bg-primary dark:bg-primary-hover w-auto text-white h-dvh py-10 px-10 overflow-y-auto'
}`}
>
Expand Down
4 changes: 1 addition & 3 deletions src/components/Popups/RedirectPopup.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import React, { useState, useEffect } from 'react';
import Modal from 'react-modal';
import { FaShare } from 'react-icons/fa';
import { useTranslation } from 'react-i18next';
import Button from '../Buttons/Button';
import Spinner from '../Shared/Spinner';
import PopupLayout from './PopupLayout';

const RedirectPopup = ({ loading, availableCredentialConfigurations, onClose, handleContinue, popupTitle, popupMessage }) => {
Expand All @@ -19,7 +17,7 @@ const RedirectPopup = ({ loading, availableCredentialConfigurations, onClose, ha
if (availableCredentialConfigurations) {
setSelectedConfiguration(Object.keys(availableCredentialConfigurations)[0])
}
}, [])
}, [availableCredentialConfigurations, setSelectedConfiguration])

const handleOptionChange = (event) => {
if (availableCredentialConfigurations) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/Popups/SelectCredentialsPopup.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ function SelectCredentialsPopup({ isOpen, setIsOpen, setSelectionMap, conformant
const [selectedCredential, setSelectedCredential] = useState(null);
const container = useContext(ContainerContext);
const screenType = useScreenType();
const [credentialDisplay, setCredentialDisplay] = useState({});
const [currentSlide, setCurrentSlide] = useState(1);

useEffect(() => {
Expand Down Expand Up @@ -115,6 +114,7 @@ function SelectCredentialsPopup({ isOpen, setIsOpen, setSelectionMap, conformant
keys,
setSelectionMap,
setIsOpen,
container.credentialParserRegistry,
]);

useEffect(() => {
Expand Down Expand Up @@ -197,7 +197,7 @@ function SelectCredentialsPopup({ isOpen, setIsOpen, setSelectionMap, conformant

return (
<PopupLayout isOpen={isOpen} onClose={onClose} loading={false} fullScreen={screenType !== 'desktop'}>
<div className='pb-16'>
<div className={`${screenType !== 'desktop' && 'pb-16'}`}>
<div>
{stepTitles && (
<h2 className="text-lg font-bold mb-2 text-primary dark:text-white">
Expand Down
5 changes: 3 additions & 2 deletions src/components/Shared/Slider.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { EffectCards } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/effect-cards';

const Slider = ({ items, renderSlideContent, onSlideChange }) => {
const [currentSlide, setCurrentSlide] = useState(1);
const Slider = ({ items, renderSlideContent, onSlideChange, initialSlide = 1 }) => {
const [currentSlide, setCurrentSlide] = useState(initialSlide);
const sliderRef = useRef(null);
const { t } = useTranslation();

Expand All @@ -33,6 +33,7 @@ const Slider = ({ items, renderSlideContent, onSlideChange }) => {
grabCursor={true}
modules={[EffectCards]}
slidesPerView={1}
initialSlide={currentSlide -1}
onSlideChange={(swiper) => {
setCurrentSlide(swiper.activeIndex + 1);
if (onSlideChange) onSlideChange(swiper.activeIndex);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Shared/Spinner.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function Spinner() {
}, []);

return (
<div className="flex justify-center items-center h-screen" role="status" aria-live="polite">
<div className="flex justify-center items-center h-dvh" role="status" aria-live="polite">
<div className="relative h-40 w-40">
<div className={`absolute rounded-full h-40 w-40 border-t-4 border-b-4 border-main-blue ${imageLoaded ? 'animate-spin' : ''}`}></div>
<div className={`absolute inset-0 flex items-center justify-center ${!imageLoaded && 'opacity-0'}`}>
Expand Down
6 changes: 3 additions & 3 deletions src/components/WelcomeTourGuide/WelcomeTourGuide.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const TourGuide = ({ toggleMenu, isOpen }) => {
useEffect(() => {

const getStepSelectorSmallScreen = (stepName) => {
if (screenType != 'desktop') {
if (screenType !== 'desktop') {
return stepName + '-small-screen';
} else {
return stepName;
Expand Down Expand Up @@ -76,7 +76,7 @@ const TourGuide = ({ toggleMenu, isOpen }) => {
return {
...step,
action: () => {
if (screenType != 'desktop') {
if (screenType !== 'desktop') {
if (index >= 5 && index <= 6 && !isOpen) {
toggleMenu();
} else if ((index < 5 || index > 6) && isOpen) {
Expand All @@ -88,7 +88,7 @@ const TourGuide = ({ toggleMenu, isOpen }) => {
});

setSteps(updatedSteps);
}, [t, toggleMenu, isOpen]);
}, [t, toggleMenu, isOpen, screenType]);

const startTour = () => {
setIsModalOpen(false);
Expand Down
Loading

0 comments on commit b793a6f

Please sign in to comment.