Porównaj commity

...

8 Commity

Autor SHA1 Wiadomość Data
Victoria (Balazs) Nadasdi 7abfb635dd
Merge f3818f6681 into 7376cb1e99 2024-04-19 09:22:52 +08:00
Lim Chee Aun 7376cb1e99 Fix muted="false" means still muted 🤦‍♂️🤦‍♂️🤦‍♂️ 2024-04-19 08:46:10 +08:00
Lim Chee Aun ffbae70178 Remove newline from regex for shortcode 2024-04-19 08:41:16 +08:00
Lim Chee Aun 9235d2c800 Hide poll button if maxOptions <= 1
It's not a poll if there's only 1 option
2024-04-18 23:12:29 +08:00
Lim Chee Aun 6ccefaebe1 Handle invalid date
Ugly solution for now, but it's already ugly
2024-04-18 23:11:18 +08:00
Lim Chee Aun 5a448c8049 Fix infinite reloading
Comment these out because this used to fix an old bug with instances not loaded properly
2024-04-18 23:10:26 +08:00
Lim Chee Aun 9bf77fa97a Mentions also need fixNotifications
It's also from notifications API
2024-04-18 17:15:51 +08:00
Victoria Nadasdi f3818f6681
feat: add Docker image build on prod release and publish to ghcr.io
This patch add a simple Docker image that builds the application with
node and puts the end result into a small nginx image.

Updtaed the prod release GitHub action to build and publish the image on
GitHub's OCI registry (ghcr.io) and connect it to the repository.

Why?

Docker is fun, more and more people deploy images on their homelab,
Docker Swarm, or Kubernetes clusters. I do the same. First I build a
builder for myself[^1], then I had a "waaaaait a minute...." moment and
realised I can upstream it and put it here.

Another options for the GitHub action:
I can move the whole build to a separate workflow and trigger on new
tag and instead of building the application, just download the artifact
from the release page on GitHub (like my version[^2] does on manual trigger).

[^1]: https://github.com/yitsushi/phanpy-docker/pkgs/container/phanpy-docker
[^2]: https://github.com/yitsushi/phanpy-docker/blob/main/.github/workflows/build-and-publish.yaml
2024-04-16 18:40:29 +02:00
9 zmienionych plików z 88 dodań i 23 usunięć

Wyświetl plik

@ -30,3 +30,20 @@ jobs:
files: |
phanpy-dist.zip
phanpy-dist.tar.gz
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
${{ steps.tag_name.outputs.tag_name }}
latest
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
build-args: |
VERSION=${{ steps.tag_name.outputs.tag_name }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

16
Dockerfile 100644
Wyświetl plik

@ -0,0 +1,16 @@
from node:21-alpine as builder
workdir /code
copy . /code
run npm ci && npm run build
from nginx:1.25.4-alpine
label maintainer="Victoria Nadasdi <efertone@pm.me>"
label org.opencontainers.image.source=https://github.com/cheeaun/phanpy
label org.opencontainers.image.description="Docker Image for Phanpy"
label org.opencontainers.image.licenses=MIT
copy --from=builder /code/dist /usr/share/nginx/html

Wyświetl plik

@ -123,12 +123,31 @@ Some of these may change in the future. The front-end world is ever-changing.
This is a **pure static web app**. You can host it anywhere you want.
Two ways (choose one):
Three ways (choose one):
### Easy way
Go to [Releases](https://github.com/cheeaun/phanpy/releases) and download the latest `phanpy-dist.zip` or `phanpy-dist.tar.gz`. It's pre-built so don't need to run any install/build commands. Extract it. Serve the folder of extracted files.
### The Docker way
```bash
docker run -p 8080:80 ghcr.io/cheeaun/phanpy:latest
```
With Docker Compose:
```yaml
---
version: "3.9"
services:
phanpy:
image: ghcr.io/cheeaun/phanpy:latest
container_name: phanpy
ports:
- 8080:80
```
### Custom-build way
Requires [Node.js](https://nodejs.org/).

Wyświetl plik

@ -131,7 +131,7 @@ const HASHTAG_RE = new RegExp(
// https://github.com/mastodon/mastodon/blob/23e32a4b3031d1da8b911e0145d61b4dd47c4f96/app/models/custom_emoji.rb#L31
const SHORTCODE_RE_FRAGMENT = '[a-zA-Z0-9_]{2,}';
const SCAN_RE = new RegExp(
`([^A-Za-z0-9_:\\n]|^)(:${SHORTCODE_RE_FRAGMENT}:)(?=[^A-Za-z0-9_:]|$)`,
`(^|[^=\\/\\w])(:${SHORTCODE_RE_FRAGMENT}:)(?=[^A-Za-z0-9_:]|$)`,
'g',
);
@ -1219,22 +1219,30 @@ function Compose({
/>
<Icon icon="attachment" />
</label>{' '}
<button
type="button"
class="toolbar-button"
disabled={
uiState === 'loading' || !!poll || !!mediaAttachments.length
}
onClick={() => {
setPoll({
options: ['', ''],
expiresIn: 24 * 60 * 60, // 1 day
multiple: false,
});
}}
>
<Icon icon="poll" alt="Add poll" />
</button>{' '}
{/* If maxOptions is not defined or defined and is greater than 1, show poll button */}
{maxOptions == null ||
(maxOptions > 1 && (
<>
<button
type="button"
class="toolbar-button"
disabled={
uiState === 'loading' ||
!!poll ||
!!mediaAttachments.length
}
onClick={() => {
setPoll({
options: ['', ''],
expiresIn: 24 * 60 * 60, // 1 day
multiple: false,
});
}}
>
<Icon icon="poll" alt="Add poll" />
</button>{' '}
</>
))}
<button
type="button"
class="toolbar-button"

Wyświetl plik

@ -388,7 +388,7 @@ function Media({
data-orientation="${orientation}"
preload="auto"
autoplay
muted="${isGIF}"
${isGIF ? 'muted' : ''}
${isGIF ? '' : 'controls'}
playsinline
loop="${loopable}"

Wyświetl plik

@ -21,6 +21,7 @@ export default function RelativeTime({ datetime, format }) {
const [renderCount, rerender] = useReducer((x) => x + 1, 0);
const date = useMemo(() => dayjs(datetime), [datetime]);
const [dateStr, dt, title] = useMemo(() => {
if (!date.isValid()) return ['' + datetime, '', ''];
let str;
if (format === 'micro') {
// If date <= 1 day ago or day is within this year
@ -37,6 +38,7 @@ export default function RelativeTime({ datetime, format }) {
}, [date, format, renderCount]);
useEffect(() => {
if (!date.isValid()) return;
let timeout;
let raf;
function rafRerender() {

Wyświetl plik

@ -4,6 +4,7 @@ import { useSearchParams } from 'react-router-dom';
import Link from '../components/link';
import Timeline from '../components/timeline';
import { api } from '../utils/api';
import { fixNotifications } from '../utils/group-notifications';
import { saveStatus } from '../utils/states';
import useTitle from '../utils/useTitle';
@ -30,6 +31,8 @@ function Mentions({ columnMode, ...props }) {
const results = await mentionsIterator.current.next();
let { value } = results;
if (value?.length) {
value = fixNotifications(value);
if (firstLoad) {
latestItem.current = value[0].id;
console.log('First load', latestItem.current);

Wyświetl plik

@ -9,7 +9,7 @@ const notificationTypeKeys = {
poll: ['status'],
update: ['status'],
};
function fixNotifications(notifications) {
export function fixNotifications(notifications) {
return notifications.filter((notification) => {
const { type, id, createdAt } = notification;
if (!type) {

Wyświetl plik

@ -107,10 +107,10 @@ export function getCurrentInstance() {
return (currentInstance = instances[instance]);
} catch (e) {
console.error(e);
alert(`Failed to load instance configuration. Please try again.\n\n${e}`);
// alert(`Failed to load instance configuration. Please try again.\n\n${e}`);
// Temporary fix for corrupted data
store.local.del('instances');
location.reload();
// store.local.del('instances');
// location.reload();
return {};
}
}