Skip to content

Commit

Permalink
[front] feat: complete PWA manifest (#1877)
Browse files Browse the repository at this point in the history
* update manifest.json

* add favicon images defined in pwa manifest

* update icons and add screenshots

* define share_target

* attempt to fix share_target

* update background color on PWA spash screen

* update icon on iOS

* add missing screenshots

* redirect shared content to entity page and create video in db when user is logged in

* fix typescript check
  • Loading branch information
amatissart authored Jan 18, 2024
1 parent e3dd3e9 commit 71264c3
Show file tree
Hide file tree
Showing 22 changed files with 159 additions and 30 deletions.
6 changes: 3 additions & 3 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
">

<meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" />
<link rel="icon" href="/icons/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
Expand All @@ -27,14 +27,14 @@
<meta property="og:type" content="website">
<meta property="og:site_name" content="Tournesol">
<meta property="og:title" content="Tournesol">
<meta property="og:description" content="Compare online contents and contribute to the development of responsible content recommendation">
<meta property="og:description" content="Compare online content and contribute to the development of responsible content recommendations.">
<!--
og:image and og:url will be built dynamically by Nginx, based on the request URL
-->
<meta property="og:image" content="__META_OG_IMAGE__">
<meta property="og:url" content="__META_OG_URL__">
<meta property="twitter:card" content="summary_large_image">
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="apple-touch-icon" href="/icons/maskable-icon-512x512.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
Expand Down
Binary file removed frontend/public/android-chrome-192x192.png
Binary file not shown.
Binary file removed frontend/public/android-chrome-512x512.png
Binary file not shown.
Binary file removed frontend/public/apple-touch-icon.png
Binary file not shown.
Binary file removed frontend/public/favicon-16x16.png
Binary file not shown.
Binary file removed frontend/public/favicon-32x32.png
Binary file not shown.
Binary file added frontend/public/icons/compare.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/favicon-128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/favicon-512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
Binary file added frontend/public/icons/maskable-icon-512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/icons/recommendations.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 60 additions & 5 deletions frontend/public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,70 @@
{
"short_name": "Tournesol",
"name": "Tournesol",
"description": "Compare online content and contribute to the development of responsible content recommendations.",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"src": "/icons/favicon.ico",
"sizes": "48x48",
"type": "image/x-icon"
},
{
"src": "/icons/favicon-128x128.png",
"type": "image/png",
"sizes": "128x128"
},
{
"src": "/icons/favicon-512x512.png",
"type": "image/png",
"sizes": "512x512"
},
{
"src": "/icons/maskable-icon-512x512.png",
"type": "image/png",
"sizes": "512x512",
"purpose": "maskable"
}
],
"start_url": ".",
"id": "/",
"start_url": "/?utm_source=pwa",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
"theme_color": "#ffc800",
"background_color": "#a09b87",
"share_target": {
"action": "/shared-content",
"method": "GET",
"params": {
"title": "title",
"text": "text",
"url": "url"
}
},
"shortcuts": [
{
"name": "Compare",
"url": "/comparison?utm_source=pwa",
"icons": [{ "src": "/icons/compare.png", "sizes": "96x96" }]
},
{
"name": "Recommendations",
"url": "/recommendations?utm_source=pwa",
"icons": [{ "src": "/icons/recommendations.png", "sizes": "96x96" }]
}
],
"screenshots" : [
{
"src": "/images/screenshot_comparison_desktop.png",
"sizes": "1300x850",
"type": "image/png",
"form_factor": "wide",
"label": "Tournesol comparison page on Desktop"
},
{
"src": "/images/screenshot_comparison_mobile.png",
"sizes": "1290x2796",
"type": "image/png",
"form_factor": "narrow",
"label": "Tournesol comparison page on Mobile"
}
]
}
5 changes: 0 additions & 5 deletions frontend/public/svg/details.svg

This file was deleted.

3 changes: 0 additions & 3 deletions frontend/public/svg/later.svg

This file was deleted.

5 changes: 0 additions & 5 deletions frontend/public/svg/more_info.svg

This file was deleted.

Binary file removed frontend/public/tournesol_screenshot_og.png
Binary file not shown.
4 changes: 4 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import About from './pages/about/About';
import { OpenAPI } from 'src/services/openapi';
import { LoginState } from './features/login/LoginState.model';
import { polls } from './utils/constants';
import SharedContent from './app/SharedContent';
import PollRoutes from './app/PollRoutes';
import { PollProvider } from './hooks/useCurrentPoll';
import FAQ from './pages/faq/FAQ';
Expand Down Expand Up @@ -144,6 +145,9 @@ function App() {
<PublicRoute path="/reset-password">
<ResetPassword />
</PublicRoute>
<PublicRoute path="/shared-content">
<SharedContent />
</PublicRoute>
{/* Polls */}
{polls.map(({ name, path }) => (
<PublicRoute key={name} path={path}>
Expand Down
39 changes: 39 additions & 0 deletions frontend/src/app/SharedContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import { Redirect } from 'react-router-dom';
import RedirectState from 'src/features/login/RedirectState';
import { useLoginState } from 'src/hooks';
import { extractVideoId } from 'src/utils/video';

/*
This component handles a shared content, received via the "share_target"
defined in the PWA manifest.
*/
const SharedContent = () => {
const { isLoggedIn } = useLoginState();
const queryParams = new URLSearchParams(document.location.search);
let sharedUrl = queryParams.get('url');
if (!sharedUrl) {
// The Youtube mobile app shares the video url as "text"
sharedUrl = queryParams.get('text');
}

const videoId = extractVideoId(sharedUrl ?? '');
if (!videoId) {
return <Redirect to="/entities/invalid" />;
}

const destination = `/entities/yt:${videoId}`;
if (!isLoggedIn) {
return (
<Redirect
to={{
pathname: '/login',
state: { from: destination } as RedirectState,
}}
/>
);
}
return <Redirect to={destination} />;
};

export default SharedContent;
62 changes: 53 additions & 9 deletions frontend/src/pages/entities/EntityAnalysisPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ import { Link, useParams } from 'react-router-dom';
import { Box, Button, Container, Grid, Typography } from '@mui/material';

import { LoaderWrapper } from 'src/components';
import { useCurrentPoll } from 'src/hooks/useCurrentPoll';
import { ApiError, PollsService, Recommendation } from 'src/services/openapi';
import { useCurrentPoll, useLoginState } from 'src/hooks';
import {
ApiError,
PollsService,
Recommendation,
VideoService,
} from 'src/services/openapi';
import {
PRESIDENTIELLE_2022_POLL_NAME,
YOUTUBE_POLL_NAME,
} from 'src/utils/constants';
import { extractVideoId } from 'src/utils/video';

const CandidateAnalysisPage = React.lazy(
() => import('src/pages/entities/CandidateAnalysisPage')
Expand Down Expand Up @@ -67,6 +73,7 @@ const EntityNotFound = ({ apiError }: { apiError: ApiError | undefined }) => {
const EntityAnalysisPage = () => {
const { uid } = useParams<{ uid: string }>();
const { name: pollName } = useCurrentPoll();
const { isLoggedIn } = useLoginState();

const { i18n } = useTranslation();
const currentLang = i18n.resolvedLanguage;
Expand All @@ -75,7 +82,33 @@ const EntityAnalysisPage = () => {
const [isLoading, setIsLoading] = useState(true);
const [apiError, setApiError] = useState<ApiError>();

const tryToCreateVideo = async () => {
if (pollName !== YOUTUBE_POLL_NAME) {
return false;
}
if (!isLoggedIn) {
return false;
}
const videoId = extractVideoId(uid);
if (!videoId) {
return false;
}
try {
await VideoService.videoCreate({
requestBody: {
video_id: videoId,
},
});
return true;
} catch (err) {
console.error(err);
return false;
}
};

useEffect(() => {
setIsLoading(true);

async function getEntityWithPollStats(): Promise<Recommendation> {
const entity = await PollsService.pollsEntitiesRetrieve({
name: pollName,
Expand All @@ -84,15 +117,26 @@ const EntityAnalysisPage = () => {
return entity;
}

getEntityWithPollStats()
.then((entity) => {
async function getEntity(createVideo = true): Promise<void> {
try {
const entity = await getEntityWithPollStats();
setEntity(entity);
setIsLoading(false);
})
.catch((reason: ApiError) => {
} catch (error) {
const reason: ApiError = error;
if (reason.status === 404 && createVideo) {
const created = await tryToCreateVideo();
if (created) {
return getEntity(false);
}
}
setApiError(reason);
setIsLoading(false);
});
}
}

getEntity().finally(() => {
setIsLoading(false);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentLang, pollName, uid]);

return (
Expand Down

0 comments on commit 71264c3

Please sign in to comment.