Skip to content

Commit

Permalink
Update article view to allow clicking on images.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lawrence Godfrey committed Feb 5, 2025
1 parent e699fe4 commit ecf5673
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 35 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/build-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,38 @@ jobs:
source: "client/dist"
target: "/var/www/html"

- name: Update and restart backend
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd ${{ vars.BACKEND_PATH }} || exit 1
# Stash any local changes to avoid conflicts
git stash
# Verify we're on the correct branch
git checkout master
# Fetch and verify the remote changes
git fetch origin master
# Check if there are any changes between local and remote
if git diff --quiet HEAD origin/master; then
echo "No changes to pull"
else
# Pull changes and check for errors
if ! git pull origin master; then
echo "Failed to pull changes"
exit 1
fi
fi
# Verify the application can start before restarting
if ! pm2 start ${{ vars.PM2_APP_NAME }} --no-daemon --no-autorestart; then
echo "Application failed to start"
exit 1
fi
# If test start successful, restart the actual service
pm2 restart ${{ vars.PM2_APP_NAME }}
# restart NGINX
- name: Restart NGINX
uses: appleboy/ssh-action@master
Expand Down
106 changes: 71 additions & 35 deletions client/src/components/ArticleView.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { useEffect, useState } from 'react';
import { PencilSquareIcon } from '@heroicons/react/24/outline';
import { PencilSquareIcon, XMarkIcon } from '@heroicons/react/24/outline';
import axios from 'axios';
import ReactMarkdown from 'react-markdown';
import { Link } from 'react-router-dom';

const ArticleView = ({ article }) => {
const [author, setAuthor] = useState(null);
const [fullScreenImage, setFullScreenImage] = useState(null);

useEffect(() => {
axios.get(`/api/users/${article.author}`)
Expand All @@ -24,50 +25,85 @@ const ArticleView = ({ article }) => {
const year = new Date(article.createdAt).getFullYear();
const formattedDate = `${month} ${day}, ${year}`;

// Custom renderer for images in markdown
const imageRenderer = {
img: ({ src, alt }) => (
<img
src={src}
alt={alt}
className="cursor-pointer hover:opacity-90 transition-opacity"
onClick={() => setFullScreenImage(src)}
/>
)
};

return (
<div className="flex justify-center items-center min-h-screen">
<div className="wrapper z-0 mt-32 xl:mt-0 flex flex-col justify-center max-w-[800px] px-6">
<h1 className="mb-8 mt-16 text-3xl font-extrabold leading-none tracking-normal text-gray-800 md:text-4xl
<>
<div className="flex justify-center items-center min-h-screen">
<div className="wrapper z-0 mt-32 xl:mt-0 flex flex-col justify-center max-w-[800px] px-6">
<h1 className="mb-8 mt-16 text-3xl font-extrabold leading-none tracking-normal text-gray-800 md:text-4xl
lg:text-4xl dark:text-white">
{ article.title }
</h1>
{ article.title }
</h1>

<div className="flex items-center justify-between space-x-4">
<div className="flex items-center space-x-4">
<img className="w-10 h-10 rounded-full"
src={author.avatar}
alt={author.username}
referrerPolicy="no-referrer"
crossOrigin="anonymous"
></img>
<div className="font-medium dark:text-white">
<div>{author.firstName} {author.lastName}</div>
<div className="text-sm text-gray-500 dark:text-gray-400">
Published {formattedDate}
<div className="flex items-center justify-between space-x-4">
<div className="flex items-center space-x-4">
<img className="w-10 h-10 rounded-full"
src={author.avatar}
alt={author.username}
referrerPolicy="no-referrer"
crossOrigin="anonymous"
></img>
<div className="font-medium dark:text-white">
<div>{author.firstName} {author.lastName}</div>
<div className="text-sm text-gray-500 dark:text-gray-400">
Published {formattedDate}
</div>
</div>
</div>
<Link to={`/articles/${article.id}/edit`}>
<PencilSquareIcon className="h-6 w-6 text-gray-500 hover:text-gray-700
dark:text-white dark:hover:text-gray-300" />
</Link>
</div>
<Link to={`/articles/${article.id}/edit`}>
<PencilSquareIcon className="h-6 w-6 text-gray-500 hover:text-gray-700
dark:text-white dark:hover:text-gray-300" />
</Link>
</div>

<hr className="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"></hr>
<hr className="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"></hr>

{article.description &&
<>
<p className="text-gray-500 dark:text-gray-400">
{ article.description }
</p>
<hr className="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"></hr>
</>
}
<div className="prose mt-10 mb-10">
<ReactMarkdown>{article.content}</ReactMarkdown>
{article.description &&
<>
<p className="text-gray-500 dark:text-gray-400">
{ article.description }
</p>
<hr className="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"></hr>
</>
}
<div className="prose mt-10 mb-10">
<ReactMarkdown components={imageRenderer}>{article.content}</ReactMarkdown>
</div>
</div>
</div>
</div>

{/* Full-screen image modal */}
{fullScreenImage && (
<div
className="fixed inset-0 bg-black bg-opacity-90 z-50 flex items-center justify-center"
onClick={() => setFullScreenImage(null)}
>
<button
className="absolute top-4 right-4 text-white hover:text-gray-300"
onClick={() => setFullScreenImage(null)}
>
<XMarkIcon className="h-8 w-8" />
</button>
<img
src={fullScreenImage}
alt="Full screen view"
className="max-h-[90vh] max-w-[90vw] object-contain"
onClick={(e) => e.stopPropagation()}
/>
</div>
)}
</>
);
};

Expand Down

0 comments on commit ecf5673

Please sign in to comment.