Compare commits

...

33 Commits

Author SHA1 Message Date
8968e990ec fixed comment button moving during further comments 2024-06-19 13:35:54 +02:00
94996fb45c pinned answer Buttons to bottom of post 2024-06-19 13:01:11 +02:00
29af08f79d moved reaction buttons a little up 2024-06-19 12:03:35 +02:00
a6ef1c71e2 added time to post 2024-06-19 11:57:46 +02:00
2f4eb05233 fixed post sorting for chrome and opera 2024-06-19 09:49:50 +02:00
34e44d2310 fix to not being able to reply to comments 2024-06-18 12:27:34 +02:00
f2f096ef67 changed displayed username 2024-06-15 13:09:22 +02:00
Pete-Gerlach
3f13bbdd19 show anonymousID in Posts and Comments 2024-06-14 16:27:02 +02:00
1ca4235283 added linebreak in posts 2024-06-14 09:44:24 +02:00
96753a774c added dev env 2024-06-13 23:40:09 +02:00
625485d318 removed logging 2024-06-13 23:13:20 +02:00
eeae861353 typo 2024-06-13 23:09:54 +02:00
d524a831cd test 2024-06-13 23:07:58 +02:00
1f947e3200 removed unnecessary entrypoint code 2024-06-13 22:50:43 +02:00
f92b7345ae removed unnecessary entrypoint code 2024-06-13 22:49:06 +02:00
6f9933f332 removed unnecessary entrypoint code 2024-06-13 22:48:55 +02:00
3936d1c651 removed unnecessary entrypoint code 2024-06-13 22:42:04 +02:00
e173888692 more tests 2024-06-13 22:36:57 +02:00
621e414f95 more tests 2024-06-13 22:28:01 +02:00
454973b017 test 2024-06-13 22:19:23 +02:00
17a29376a9 typo 2024-06-13 22:05:34 +02:00
d26044ee3f added config file 2024-06-13 22:04:32 +02:00
47e8f0fc11 added config file 2024-06-13 22:04:03 +02:00
f02083d44c added custom entrypoint 2024-06-13 21:55:08 +02:00
66eaf91521 changed to prod 2024-06-13 16:28:03 +02:00
0704ea59b1 moved to env variables 2024-06-13 15:28:13 +02:00
848ee1ccb4 reversed longitude and latitude in api url 2024-06-13 14:53:27 +02:00
103e3bace3 reversed longitude and latitude in api url 2024-06-13 14:51:36 +02:00
088252f5bc reversed longitude and latitude in api url 2024-06-13 14:47:24 +02:00
9b8dc8cde7 changed tab name 2024-06-13 10:55:51 +02:00
5cb6ef0798 added sort for posts by date 2024-06-13 09:54:10 +02:00
9804bd196b added refresh and edit button 2024-06-13 08:59:35 +02:00
88bdfb35d3 added new favicon 2024-06-13 08:29:47 +02:00
12 changed files with 156 additions and 53 deletions

View File

@@ -13,9 +13,14 @@ RUN npm install
FROM npm-installed AS builder FROM npm-installed AS builder
ADD . . ADD . .
RUN npm run build RUN npm run build --production
FROM nginx:stable-alpine3.17-slim FROM nginx:stable-alpine3.17-slim
COPY entrypoint.sh .
RUN chmod +x entrypoint.sh
COPY --from=builder /source/build /usr/share/nginx/html COPY --from=builder /source/build /usr/share/nginx/html
ENTRYPOINT ["./entrypoint.sh"]

28
entrypoint.sh Normal file
View File

@@ -0,0 +1,28 @@
#!/bin/sh
# vim:sw=4:ts=4:et
set -e
# Set up endpoint for env retrieval
echo "window._env_ = {" > /usr/share/nginx/html/env-config.js
# Collect enviroment variables for react
eval enviroment_variables="$(env | grep REACT_APP.*=)"
# Loop over variables
env | grep REACT_APP.*= | while read -r line;
do
printf "%s',\n" $line | sed "s/=/:'/" >> /usr/share/nginx/html/env-config.js
# Notify the user
printf "Env variable %s' was injected into React App. \n" $line | sed "0,/=/{s//:'/}"
done
# End the object creation
echo "}" >> /usr/share/nginx/html/env-config.js
echo "Enviroment Variable Injection Complete."
# Start Nginx
nginx -g "daemon off;"

4
public/env-config.js Normal file
View File

@@ -0,0 +1,4 @@
window._env_ = {
REACT_APP_API: "https://api.jodel.anxietyprime.de",
REACT_APP_KC: "https://keycloak.anxietyprime.de",
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 163 KiB

View File

@@ -9,7 +9,8 @@
name="description" name="description"
content="Web site created using create-react-app" content="Web site created using create-react-app"
/> />
<title>Jodel Klon</title> <title>SWA-Jodel</title>
<script src="%PUBLIC_URL%/env-config.js"></script>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@@ -2,7 +2,7 @@ import React, {useEffect, useState} from 'react';
import NewPostForm from './components/NewPostForm'; import NewPostForm from './components/NewPostForm';
import './App.scss'; import './App.scss';
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faTimes} from "@fortawesome/free-solid-svg-icons"; import {faPenToSquare, faRotate, faTimes} from "@fortawesome/free-solid-svg-icons";
import {postApi} from "./api/posts.api"; import {postApi} from "./api/posts.api";
import {locationUtils} from "./utils/location"; import {locationUtils} from "./utils/location";
import Post from "./components/Post"; import Post from "./components/Post";
@@ -27,14 +27,21 @@ const App = () => {
const reload = async () => { const reload = async () => {
await loadPosts().then(data => { await loadPosts().then(posts => {
setPosts(data); posts = recursiveSort(posts);
setPosts(posts);
}); });
} }
const recursiveSort = (posts) => {
posts.forEach((post, index) => {posts[index].comments = recursiveSort(post.comments)});
posts.sort((a, b) => a.date < b.date ? 1 : -1);
return posts;
}
const loadPosts = async () => { const loadPosts = async () => {
const location = await locationUtils.getCurrentLocation(); const location = await locationUtils.getCurrentLocation();
return await postApi.getPostsByCoords(location.lon, location.lat); return await postApi.getPostsByCoords(location.lat, location.lon);
} }
const addPost = async (title, text) => { const addPost = async (title, text) => {
@@ -62,9 +69,16 @@ const App = () => {
return ( return (
<div className="app"> <div className="app">
<div className="nav-bar">
<button className="nav-button" onClick={() => reload()}>
<FontAwesomeIcon className={"close-button"} icon={faRotate}/>
</button>
<button className="nav-button nav-space" onClick={() => keycloak.accountManagement()}>
<FontAwesomeIcon className={"close-button"} icon={faPenToSquare}/>
</button>
<button className="logout" onClick={() => keycloak.logout()}>Logout</button> <button className="logout" onClick={() => keycloak.logout()}>Logout</button>
</div>
<h1>SWA - Jodel</h1> <h1>SWA - Jodel</h1>
<div style={{height: selectedPost ? 0 : "auto", overflowY: "hidden"}}> <div style={{height: selectedPost ? 0 : "auto", overflowY: "hidden"}}>
<NewPostForm addPost={addPost}/> <NewPostForm addPost={addPost}/>
{posts.map(post => <Post key={post.id} post={post} recursionDepth={5} selectPost={selectPost} reload={reload} />)} {posts.map(post => <Post key={post.id} post={post} recursionDepth={5} selectPost={selectPost} reload={reload} />)}

View File

@@ -63,12 +63,18 @@ h3 {
.post-content { .post-content {
flex: 1; flex: 1;
margin-right: 20px; margin-right: 20px;
white-space: pre-wrap;
}
.post-container {
margin-right: -5em;
} }
.post-right { .post-right {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-evenly; justify-content: space-evenly;
width: 5em;
.post-date { .post-date {
position: relative; position: relative;
@@ -85,6 +91,7 @@ h3 {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin-top: -1em;
button { button {
padding: 10px; padding: 10px;
@@ -251,6 +258,8 @@ button {
} }
.nrOfComments { .nrOfComments {
position: relative;
right: -3em;
height: 100%; height: 100%;
display: flex; display: flex;
align-self: flex-end; align-self: flex-end;
@@ -259,7 +268,15 @@ button {
font-size: 1.5em; font-size: 1.5em;
padding: 0.35em; padding: 0.35em;
* { * {
padding-right: 0.25em; padding-right: 0.3em;
}
span {
padding-right: 0.6em;
}
button {
padding: 0.3em 0.8em;
line-height: 2em;
font-size: 0.8em;
} }
} }
@@ -267,11 +284,30 @@ button {
color: #ff9908; color: #ff9908;
} }
.logout {
margin-left: 75%;
width: 25%
}
textarea { textarea {
resize: none; resize: none;
} }
.nav-bar {
display: flex;
width: 100%;
flex-direction: row;
}
.nav-button {
height: 3em;
width: 3em;
}
.nav-space {
margin-left: auto;
}
.logout {
margin-left: 0.5em;
width: 25%
}
.spacer {
height: 4em;
}

View File

@@ -1,6 +1,10 @@
import Keycloak from "keycloak-js"; import Keycloak from "keycloak-js";
import getConfig from "../config/config";
const config = getConfig();
const keycloak = new Keycloak({ const keycloak = new Keycloak({
url: "https://keycloak.anxietyprime.de/", url: config.REACT_APP_KC || process.env.REACT_APP_KC,
realm: "Jodel", realm: "Jodel",
clientId: "jodel-frontend", clientId: "jodel-frontend",
}); });

View File

@@ -1,13 +1,15 @@
import axios from "axios" import axios from "axios"
import {locationUtils} from "../utils/location"; import {locationUtils} from "../utils/location";
import keycloak from "../Authentification/Keycloak"; import keycloak from "../Authentification/Keycloak";
import getConfig from "../config/config";
const path = "https://api.jodel.anxietyprime.de"; const config = getConfig();
const path = config.REACT_APP_API || process.env.REACT_APP_API;
export const postApi = { export const postApi = {
async getPostsByCoords (lon, lat) { async getPostsByCoords (lat, lon) {
if (!keycloak.authenticated) return []; if (!keycloak.authenticated) return [];
return (await axios.get(`${path}/posts/${lon}/${lat}`)).data; return (await axios.get(`${path}/posts/${lat}/${lon}`)).data;
}, },
async getPostByID (id) { async getPostByID (id) {
if (!keycloak.authenticated) return []; if (!keycloak.authenticated) return [];

View File

@@ -7,8 +7,8 @@ import {
faAngleUp, faAngleUp,
faAngleDown faAngleDown
} from '@fortawesome/free-solid-svg-icons'; } from '@fortawesome/free-solid-svg-icons';
import {faMessage} from "@fortawesome/free-solid-svg-icons/faMessage";
import {postApi} from "../api/posts.api"; import {postApi} from "../api/posts.api";
import keycloak from "../Authentification/Keycloak";
const Post = ({ post, recursionDepth, selectPost, reload }) => { const Post = ({ post, recursionDepth, selectPost, reload }) => {
@@ -40,13 +40,18 @@ const Post = ({ post, recursionDepth, selectPost, reload }) => {
return nrOfComments; return nrOfComments;
} }
const getDate = (dateStr) => {
let date = new Date(dateStr);
return date.toLocaleDateString('de-DE') + "\n" + ("00" + date.getHours()).slice(-2) + ":" + ("00" + date.getMinutes()).slice(-2);
}
return ( return (
<div> <div>
<div className="post" onClick={click => {if (!(click.target instanceof HTMLButtonElement)) setShowComments(!showComments)}}> <div className="post" onClick={click => {if (!(click.target instanceof HTMLButtonElement)) setShowComments(!showComments)}}>
<div className="post-container"> <div className="post-container">
{post.id === post.parent && <div className="post-title"> <div className="post-title">
<h3>{post.title}</h3> <h3>{post.id === post.parent ? post.title : "Kommentar"}<span style={{color: 'gray'}}> von {post.anonymousID === 0 ? keycloak.idTokenParsed.preferred_username : "Anonymous " + post.anonymousID}</span></h3>
</div>} </div>
<div className="post-content"> <div className="post-content">
<p>{post.content}</p> <p>{post.content}</p>
</div> </div>
@@ -54,10 +59,11 @@ const Post = ({ post, recursionDepth, selectPost, reload }) => {
{recursionDepth !== 0 && <div className="nrOfComments"> {recursionDepth !== 0 && <div className="nrOfComments">
<FontAwesomeIcon className="icon" icon={faComments}/> <FontAwesomeIcon className="icon" icon={faComments}/>
<span className="">{getComments(post, recursionDepth - 1)}</span> <span className="">{getComments(post, recursionDepth - 1)}</span>
<button onClick={() => selectPost(post)}>Antworten</button>
</div>} </div>}
<div className="post-right"> <div className="post-right">
<div className="post-date"> <div className="post-date">
{post.date.slice(8, 10) + "." + post.date.slice(5, 7) + "." + post.date.slice(0, 4)} {getDate(post.date)}
</div> </div>
<div className="row"> <div className="row">
<div className="votes"> <div className="votes">
@@ -76,18 +82,16 @@ const Post = ({ post, recursionDepth, selectPost, reload }) => {
</button> </button>
</div> </div>
</div> </div>
{recursionDepth !== 0 && <button onClick={() => selectPost(post)}>Antworten</button>} {recursionDepth !== 0 && <div className="spacer"></div>}
</div> </div>
</div> </div>
<div className="comment"> <div className="comment">
{recursionDepth !== 0 && showComments && post.comments.map(post => <Post key={post.id} post={post} {recursionDepth !== 0 && showComments && post.comments.map(post =>
recursionDepth={recursionDepth - 1} <Post key={post.id} post={post} recursionDepth={recursionDepth - 1} selectPost={selectPost} reload={reload} />
selectPost={selectPost} )}
reload={reload} />)}
</div> </div>
</div> </div>
) );
;
}; };
export default Post; export default Post;

5
src/config/config.js Normal file
View File

@@ -0,0 +1,5 @@
const getConfig = () => {
return window._env_ || {};
}
export default getConfig;