mirror of
https://github.com/cheeaun/phanpy.git
synced 2024-05-20 15:36:51 +01:00
Compare commits
6 commits
064dca7e5b
...
d80a8bf8d4
Author | SHA1 | Date | |
---|---|---|---|
d80a8bf8d4 | |||
90f06c511a | |||
e7aad03279 | |||
1c6b0aa0d7 | |||
3e1b9ff53d | |||
f3818f6681 |
17
.github/workflows/prodtag.yml
vendored
17
.github/workflows/prodtag.yml
vendored
|
@ -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
Normal file
16
Dockerfile
Normal file
|
@ -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
|
21
README.md
21
README.md
|
@ -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/).
|
||||
|
|
3011
package-lock.json
generated
3011
package-lock.json
generated
File diff suppressed because it is too large
Load diff
26
package.json
26
package.json
|
@ -19,7 +19,7 @@
|
|||
"@szhsin/react-menu": "~4.1.0",
|
||||
"@uidotdev/usehooks": "~2.4.1",
|
||||
"compare-versions": "~6.1.0",
|
||||
"dayjs": "~1.11.10",
|
||||
"dayjs": "~1.11.11",
|
||||
"dayjs-twitter": "~0.5.0",
|
||||
"fast-blurhash": "~1.1.2",
|
||||
"fast-equals": "~5.0.1",
|
||||
|
@ -28,18 +28,18 @@
|
|||
"idb-keyval": "~6.2.1",
|
||||
"just-debounce-it": "~3.2.0",
|
||||
"lz-string": "~1.5.0",
|
||||
"masto": "~6.7.2",
|
||||
"masto": "~6.7.7",
|
||||
"moize": "~6.1.6",
|
||||
"p-retry": "~6.2.0",
|
||||
"p-throttle": "~6.1.0",
|
||||
"preact": "~10.20.2",
|
||||
"preact": "~10.21.0",
|
||||
"punycode": "~2.3.1",
|
||||
"react-hotkeys-hook": "~4.5.0",
|
||||
"react-intersection-observer": "~9.8.2",
|
||||
"react-intersection-observer": "~9.10.2",
|
||||
"react-quick-pinch-zoom": "~5.1.0",
|
||||
"react-router-dom": "6.6.2",
|
||||
"string-length": "6.0.0",
|
||||
"swiped-events": "~1.1.9",
|
||||
"swiped-events": "~1.2.0",
|
||||
"toastify-js": "~1.12.0",
|
||||
"uid": "~2.0.2",
|
||||
"use-debounce": "~10.0.0",
|
||||
|
@ -51,18 +51,18 @@
|
|||
"@preact/preset-vite": "~2.8.2",
|
||||
"@trivago/prettier-plugin-sort-imports": "~4.3.0",
|
||||
"postcss": "~8.4.38",
|
||||
"postcss-dark-theme-class": "~1.2.3",
|
||||
"postcss-preset-env": "~9.5.8",
|
||||
"postcss-dark-theme-class": "~1.3.0",
|
||||
"postcss-preset-env": "~9.5.11",
|
||||
"twitter-text": "~3.1.0",
|
||||
"vite": "~5.2.10",
|
||||
"vite": "~5.2.11",
|
||||
"vite-plugin-generate-file": "~0.1.1",
|
||||
"vite-plugin-html-config": "~1.0.11",
|
||||
"vite-plugin-pwa": "~0.19.8",
|
||||
"vite-plugin-pwa": "~0.20.0",
|
||||
"vite-plugin-remove-console": "~2.2.0",
|
||||
"workbox-cacheable-response": "~7.0.0",
|
||||
"workbox-expiration": "~7.0.0",
|
||||
"workbox-routing": "~7.0.0",
|
||||
"workbox-strategies": "~7.0.0"
|
||||
"workbox-cacheable-response": "~7.1.0",
|
||||
"workbox-expiration": "~7.1.0",
|
||||
"workbox-routing": "~7.1.0",
|
||||
"workbox-strategies": "~7.1.0"
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
|
|
|
@ -17,6 +17,21 @@
|
|||
);
|
||||
filter: saturate(0.5);
|
||||
}
|
||||
|
||||
&:is(a) {
|
||||
pointer-events: auto;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--outline-hover-color);
|
||||
}
|
||||
|
||||
.status {
|
||||
filter: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.accounts-list {
|
||||
|
|
|
@ -11,6 +11,7 @@ import useLocationChange from '../utils/useLocationChange';
|
|||
|
||||
import AccountBlock from './account-block';
|
||||
import Icon from './icon';
|
||||
import Link from './link';
|
||||
import Loader from './loader';
|
||||
import Status from './status';
|
||||
|
||||
|
@ -143,9 +144,12 @@ export default function GenericAccounts({
|
|||
</header>
|
||||
<main>
|
||||
{post && (
|
||||
<div class="post-preview">
|
||||
<Link
|
||||
to={`/${instance || currentInstance}/s/${post.id}`}
|
||||
class="post-preview"
|
||||
>
|
||||
<Status status={post} size="s" readOnly />
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
{accounts.length > 0 ? (
|
||||
<>
|
||||
|
|
|
@ -28,6 +28,7 @@ const NOTIFICATION_ICONS = {
|
|||
'admin.signup': 'account-edit',
|
||||
'admin.report': 'account-warning',
|
||||
severed_relationships: 'heart-break',
|
||||
moderation_warning: 'alert',
|
||||
emoji_reaction: 'emoji2',
|
||||
'pleroma:emoji_reaction': 'emoji2',
|
||||
};
|
||||
|
@ -45,6 +46,8 @@ poll = A poll you have voted in or created has ended
|
|||
update = A status you interacted with has been edited
|
||||
admin.sign_up = Someone signed up (optionally sent to admins)
|
||||
admin.report = A new report has been filed
|
||||
severed_relationships = Severed relationships
|
||||
moderation_warning = Moderation warning
|
||||
*/
|
||||
|
||||
function emojiText(emoji, emoji_url) {
|
||||
|
@ -91,6 +94,7 @@ const contentText = {
|
|||
Lost connections with <i>{name}</i>.
|
||||
</>
|
||||
),
|
||||
moderation_warning: <b>Moderation warning</b>,
|
||||
emoji_reaction: emojiText,
|
||||
'pleroma:emoji_reaction': emojiText,
|
||||
};
|
||||
|
@ -117,6 +121,17 @@ const SEVERED_RELATIONSHIPS_TEXT = {
|
|||
),
|
||||
};
|
||||
|
||||
const MODERATION_WARNING_TEXT = {
|
||||
none: 'Your account has received a moderation warning.',
|
||||
disable: 'Your account has been disabled.',
|
||||
mark_statuses_as_sensitive:
|
||||
'Some of your posts have been marked as sensitive.',
|
||||
delete_statuses: 'Some of your posts have been deleted.',
|
||||
sensitive: 'Your posts will be marked as sensitive from now on.',
|
||||
silence: 'Your account has been limited.',
|
||||
suspend: 'Your account has been suspended.',
|
||||
};
|
||||
|
||||
const AVATARS_LIMIT = 50;
|
||||
|
||||
function Notification({
|
||||
|
@ -125,8 +140,16 @@ function Notification({
|
|||
isStatic,
|
||||
disableContextMenu,
|
||||
}) {
|
||||
const { id, status, account, report, event, _accounts, _statuses } =
|
||||
notification;
|
||||
const {
|
||||
id,
|
||||
status,
|
||||
account,
|
||||
report,
|
||||
event,
|
||||
moderation_warning,
|
||||
_accounts,
|
||||
_statuses,
|
||||
} = notification;
|
||||
let { type } = notification;
|
||||
|
||||
// status = Attached when type of the notification is favourite, reblog, status, mention, poll, or update
|
||||
|
@ -314,6 +337,20 @@ function Notification({
|
|||
.
|
||||
</div>
|
||||
)}
|
||||
{type === 'moderation_warning' && !!moderation_warning && (
|
||||
<div>
|
||||
{MODERATION_WARNING_TEXT[moderation_warning.action]}
|
||||
<br />
|
||||
<a
|
||||
href={`/disputes/strikes/${moderation_warning.id}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn more <Icon icon="external" size="s" />
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{_accounts?.length > 1 && (
|
||||
|
|
|
@ -569,8 +569,15 @@
|
|||
font-weight: bold;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
|
||||
&.horizontal {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
.status-filtered-badge.badge-meta {
|
||||
.status-filtered-badge:not(.horizontal).badge-meta {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
|
@ -584,10 +591,10 @@
|
|||
border-color: var(--text-color);
|
||||
background: var(--bg-color);
|
||||
}
|
||||
.status-filtered-badge.badge-meta > span:first-child {
|
||||
.status-filtered-badge:not(.horizontal).badge-meta > span:first-child {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.status-filtered-badge.badge-meta > span + span {
|
||||
.status-filtered-badge:not(.horizontal).badge-meta > span + span {
|
||||
display: block;
|
||||
font-size: 9px;
|
||||
font-weight: normal;
|
||||
|
@ -601,6 +608,10 @@
|
|||
left: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.status-filtered-badge.horizontal.badge-meta > span + span {
|
||||
font-weight: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.status.large > .container > .content-container {
|
||||
margin-left: calc(-50px - 16px);
|
||||
|
|
|
@ -646,7 +646,11 @@ const TimelineItem = memo(
|
|||
>
|
||||
<Link class="status-link timeline-item" to={url}>
|
||||
{showCompact ? (
|
||||
<TimelineStatusCompact status={item} instance={instance} />
|
||||
<TimelineStatusCompact
|
||||
status={item}
|
||||
instance={instance}
|
||||
filterContext={filterContext}
|
||||
/>
|
||||
) : useItemID ? (
|
||||
<Status
|
||||
statusID={statusID}
|
||||
|
@ -820,11 +824,12 @@ function StatusCarousel({ title, class: className, children }) {
|
|||
);
|
||||
}
|
||||
|
||||
function TimelineStatusCompact({ status, instance }) {
|
||||
function TimelineStatusCompact({ status, instance, filterContext }) {
|
||||
const snapStates = useSnapshot(states);
|
||||
const { id, visibility, language } = status;
|
||||
const statusPeekText = statusPeek(status);
|
||||
const sKey = statusKey(id, instance);
|
||||
const filterInfo = isFiltered(status.filtered, filterContext);
|
||||
return (
|
||||
<article
|
||||
class={`status compact-thread ${
|
||||
|
@ -850,13 +855,24 @@ function TimelineStatusCompact({ status, instance }) {
|
|||
lang={language}
|
||||
dir="auto"
|
||||
>
|
||||
{statusPeekText}
|
||||
{status.sensitive && status.spoilerText && (
|
||||
{!!filterInfo ? (
|
||||
<b
|
||||
class="status-filtered-badge badge-meta horizontal"
|
||||
title={filterInfo?.titlesStr || ''}
|
||||
>
|
||||
<span>Filtered</span>: <span>{filterInfo?.titlesStr || ''}</span>
|
||||
</b>
|
||||
) : (
|
||||
<>
|
||||
{' '}
|
||||
<span class="spoiler-badge">
|
||||
<Icon icon="eye-close" size="s" />
|
||||
</span>
|
||||
{statusPeekText}
|
||||
{status.sensitive && status.spoilerText && (
|
||||
<>
|
||||
{' '}
|
||||
<span class="spoiler-badge">
|
||||
<Icon icon="eye-close" size="s" />
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -102,6 +102,17 @@ function Notifications({ columnMode }) {
|
|||
// },
|
||||
// });
|
||||
|
||||
// TEST: Slot in a fake notification to test 'moderation_warning'
|
||||
// notifications.unshift({
|
||||
// id: '123123',
|
||||
// type: 'moderation_warning',
|
||||
// createdAt: new Date().toISOString(),
|
||||
// moderation_warning: {
|
||||
// id: '1231234',
|
||||
// action: 'mark_statuses_as_sensitive',
|
||||
// },
|
||||
// });
|
||||
|
||||
// console.log({ notifications });
|
||||
|
||||
const groupedNotifications = groupNotifications(notifications);
|
||||
|
|
Loading…
Reference in a new issue