From 03f525f3cf3a547915734c4fdf59eb1dc7f54fe4 Mon Sep 17 00:00:00 2001 From: sheodox Date: Thu, 31 Aug 2023 07:25:12 -0500 Subject: [PATCH 1/8] Improve mobile display of community header This moves the row of buttons into a new ChainButtons component which automatically collapses buttons that don't fit into an ExtraActions dropdown button. --- src/lib/ChainButtons.svelte | 94 ++++++++++++++++++++++ src/lib/ExtraActionButton.svelte | 34 ++++++++ src/lib/ExtraActions.svelte | 30 ++----- src/lib/comments/Comment.svelte | 2 +- src/lib/feeds/posts/CommunityHeader.svelte | 53 ++++++------ src/lib/feeds/posts/FeedHeader.svelte | 26 ++++-- src/lib/feeds/posts/PostLayout.svelte | 2 +- 7 files changed, 185 insertions(+), 56 deletions(-) create mode 100644 src/lib/ChainButtons.svelte create mode 100644 src/lib/ExtraActionButton.svelte diff --git a/src/lib/ChainButtons.svelte b/src/lib/ChainButtons.svelte new file mode 100644 index 0000000..3ce735d --- /dev/null +++ b/src/lib/ChainButtons.svelte @@ -0,0 +1,94 @@ + +
+ {#each outside as action, index} +
+ +
+ {/each} + + {#if overflowWidth === null || collapsed.length} +
+ + +
+ {/if} +
+ + diff --git a/src/lib/ExtraActionButton.svelte b/src/lib/ExtraActionButton.svelte new file mode 100644 index 0000000..90943bf --- /dev/null +++ b/src/lib/ExtraActionButton.svelte @@ -0,0 +1,34 @@ + + +{#if action.href} + {action.text} +{:else if action.click} + +{/if} + + diff --git a/src/lib/ExtraActions.svelte b/src/lib/ExtraActions.svelte index a292827..9163aa9 100644 --- a/src/lib/ExtraActions.svelte +++ b/src/lib/ExtraActions.svelte @@ -1,8 +1,4 @@ -
+
- + {#if icon}
{/if} - +

- + @@ -39,11 +50,16 @@ import { parseISO } from 'date-fns'; import { Stack, Icon } from 'sheodox-ui'; import Image from '$lib/Image.svelte'; + import { getAppContext } from '$lib/app-context'; const dateFormatter = new Intl.DateTimeFormat('en', { dateStyle: 'medium' }); + const { screenDimensions } = getAppContext(); + export let published: string; export let icon: string; + + $: narrow = $screenDimensions.width < 600; diff --git a/src/lib/feeds/posts/PostLayout.svelte b/src/lib/feeds/posts/PostLayout.svelte index 68e55e6..af73ef5 100644 --- a/src/lib/feeds/posts/PostLayout.svelte +++ b/src/lib/feeds/posts/PostLayout.svelte @@ -67,7 +67,7 @@ console.log({ postView })} /> - + {/if} From f9911b91b6735d1d301897029b85570af8b7215b Mon Sep 17 00:00:00 2001 From: sheodox Date: Thu, 31 Aug 2023 16:29:22 -0500 Subject: [PATCH 2/8] Add a modlog, times on reports, hide nsfw community icons --- package-lock.json | 4 +- package.json | 2 +- src/lib/CommunityLink.svelte | 22 +- src/lib/CommunitySidebar.svelte | 40 +- src/lib/ModlogLink.svelte | 31 ++ src/lib/NameAtInstance.svelte | 2 +- src/lib/UserLink.svelte | 6 +- src/lib/content-views.ts | 371 ++++++++++++++++++ src/lib/instance/InstanceSidebar.svelte | 10 + src/lib/settings-context.ts | 8 +- src/routes/(app)/[instance]/+layout.svelte | 9 +- .../[communityName]/post/[postId]/+layout.ts | 19 - .../(app)/[instance]/modlog/+layout.svelte | 7 + src/routes/(app)/[instance]/modlog/+layout.ts | 25 ++ .../(app)/[instance]/modlog/+page.svelte | 33 ++ .../[instance]/modlog/view/+layout.svelte | 30 ++ .../(app)/[instance]/modlog/view/+page.svelte | 130 ++++++ .../(app)/[instance]/modlog/view/+page.ts | 47 +++ .../modlog/view/ModlogAction.svelte | 255 ++++++++++++ .../(app)/[instance]/reports/Report.svelte | 4 + .../[instance]/reports/ReportedComment.svelte | 1 + .../[instance]/reports/ReportedPost.svelte | 1 + .../(app)/[instance]/settings/+page.svelte | 13 +- .../[instance]/u/[username]/+page.svelte | 50 ++- 24 files changed, 1058 insertions(+), 62 deletions(-) create mode 100644 src/lib/ModlogLink.svelte delete mode 100644 src/routes/(app)/[instance]/c/[communityName]/post/[postId]/+layout.ts create mode 100644 src/routes/(app)/[instance]/modlog/+layout.svelte create mode 100644 src/routes/(app)/[instance]/modlog/+layout.ts create mode 100644 src/routes/(app)/[instance]/modlog/+page.svelte create mode 100644 src/routes/(app)/[instance]/modlog/view/+layout.svelte create mode 100644 src/routes/(app)/[instance]/modlog/view/+page.svelte create mode 100644 src/routes/(app)/[instance]/modlog/view/+page.ts create mode 100644 src/routes/(app)/[instance]/modlog/view/ModlogAction.svelte diff --git a/package-lock.json b/package-lock.json index 3b527c8..c595b0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "Alexandrite", - "version": "0.8.3", + "version": "0.8.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "Alexandrite", - "version": "0.8.3", + "version": "0.8.4", "dependencies": { "@fortawesome/fontawesome-free": "^6.4.0", "date-fns": "^2.30.0", diff --git a/package.json b/package.json index 910c82d..6c23961 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Alexandrite", - "version": "0.8.3", + "version": "0.8.4", "private": true, "scripts": { "dev": "vite dev", diff --git a/src/lib/CommunityLink.svelte b/src/lib/CommunityLink.svelte index 3f77a68..b010a98 100644 --- a/src/lib/CommunityLink.svelte +++ b/src/lib/CommunityLink.svelte @@ -32,7 +32,7 @@
- {#if community.icon && $profile.settings.show_avatars} + {#if community.icon && showIcon}
@@ -46,7 +46,7 @@ {/if}
- {#if community.icon && $profile.settings.show_avatars} + {#if community.icon && showIcon}
@@ -54,12 +54,14 @@ - + {#if showBadges} + + {/if}
{:else} - {#if $profile.settings.show_avatars} + {#if $profile.settings.show_avatars && showIcon} {/if} @@ -79,6 +81,7 @@ import NameAtInstance from './NameAtInstance.svelte'; import EllipsisText from './EllipsisText.svelte'; import { profile } from './profiles/profiles'; + import { getSettingsContext } from './settings-context'; export let community: Community; export let inlineLink = true; @@ -86,6 +89,17 @@ // this is used for links to crossposts, where the community should be the visible part of the link, but the // link should actually go to the post in that community export let href: string | null = null; + export let showBadges = true; + + const { nsfwImageHandling } = getSettingsContext(); $: communityName = nameAtInstance(community); + + // hide community avatars if the community is nsfw and the user doesn't want to explicitly see nsfw content, + // as the community avatar often will also be nsfw + $: nsfwShowable = !community.nsfw || $nsfwImageHandling === 'SHOW'; + + // just in case, don't show icons for communities that were removed for some reason + $: showIcon = + $profile.settings.show_avatars && community.icon && !community.deleted && !community.removed && nsfwShowable; diff --git a/src/lib/CommunitySidebar.svelte b/src/lib/CommunitySidebar.svelte index 712c5f8..0846c48 100644 --- a/src/lib/CommunitySidebar.svelte +++ b/src/lib/CommunitySidebar.svelte @@ -1,6 +1,6 @@
- + @@ -14,20 +14,28 @@ View on {communityInstance} +
- {#if moderators} - - Moderators ({moderators.length}) - - {#each moderators as mod} - - {/each} - - - {/if} + + {#if moderators} + + Moderators ({moderators.length}) + + {#each moderators as mod} + + {/each} + + + {/if} +
@@ -37,14 +45,24 @@ import Sidebar from '$lib/Sidebar.svelte'; import NameAtInstance from '$lib/NameAtInstance.svelte'; import CommunityCounts from './CommunityCounts.svelte'; + import ModlogLink from './ModlogLink.svelte'; import UserLink from './UserLink.svelte'; import type { CommunityView, Community, CommunityModeratorView } from 'lemmy-js-client'; import { nameAtInstance } from './nav-utils'; import { profile } from '$lib/profiles/profiles'; + import { getAppContext } from './app-context'; + import { getSettingsContext } from './settings-context'; export let community: Community; export let communityView: CommunityView | null = null; export let moderators: CommunityModeratorView[] | null = null; + const { siteMeta } = getAppContext(); + const { showModlogWarning, showModlogWarningModerated } = getSettingsContext(); + + $: communityHref = `/${$profile.instance}/c/${nameAtInstance(community)}`; + $: userModerates = $siteMeta.my_user?.moderates.some((moderates) => moderates.community.id === community.id); + $: warnModlog = userModerates ? $showModlogWarningModerated : $showModlogWarning; + $: communityInstance = new URL(community.actor_id).host; diff --git a/src/lib/ModlogLink.svelte b/src/lib/ModlogLink.svelte new file mode 100644 index 0000000..c342faa --- /dev/null +++ b/src/lib/ModlogLink.svelte @@ -0,0 +1,31 @@ + {label} + + diff --git a/src/lib/NameAtInstance.svelte b/src/lib/NameAtInstance.svelte index 9e0510c..b16f876 100644 --- a/src/lib/NameAtInstance.svelte +++ b/src/lib/NameAtInstance.svelte @@ -3,7 +3,7 @@ diff --git a/src/lib/content-views.ts b/src/lib/content-views.ts index 6a20b19..968d1ce 100644 --- a/src/lib/content-views.ts +++ b/src/lib/content-views.ts @@ -1,16 +1,38 @@ import { writable, type Writable } from 'svelte/store'; import type { + AdminPurgeCommentView, + AdminPurgeCommunityView, + AdminPurgePersonView, + AdminPurgePostView, + Comment, CommentReplyView, CommentReportView, CommentView, + Community, CommunityView, + GetModlogResponse, + ModAddCommunityView, + ModAddView, + ModBanFromCommunityView, + ModBanView, + ModFeaturePostView, + ModHideCommunityView, + ModLockPostView, + ModRemoveCommentView, + ModRemoveCommunityView, + ModRemovePostView, + ModTransferCommunityView, + Person, PersonMentionView, PersonView, + Post, PostReportView, PostView, PrivateMessageView } from 'lemmy-js-client'; import { getContext } from 'svelte'; +import { profile } from './profiles/profiles'; +import { nameAtInstance } from './nav-utils'; /* * ContentViewProvider should be used to store and manage data shown in a feed. @@ -96,6 +118,23 @@ export type ContentView = | ContentViewReply | ContentViewMessage; +export type ContentViewModlog = + | ContentViewModRemovePost + | ContentViewModLockPost + | ContentViewModFeaturePost + | ContentViewModRemoveComment + | ContentViewModRemoveCommunity + | ContentViewModBanFromCommunity + | ContentViewModBan + | ContentViewModAddCommunity + | ContentViewModTransferCommunity + | ContentViewModAdd + | ContentViewAdminPurgePerson + | ContentViewAdminPurgeCommunity + | ContentViewAdminPurgePost + | ContentViewAdminPurgeComment + | ContentViewModHideCommunity; + export type ContentViewType = ContentView['type']; export const postViewToContentView = (view: PostView): ContentViewPost => { @@ -264,3 +303,335 @@ export const getContentViewStore = () => { return ctx; }; + +export const modlogToContentView = (modlogs: GetModlogResponse): ContentViewModlog[] => { + return [ + modlogs.removed_posts.map(modRemovePostViewToContentView), + modlogs.locked_posts.map(modLockPostViewToContentView), + modlogs.featured_posts.map(modFeaturePostViewToContentView), + modlogs.removed_comments.map(modRemoveCommentViewToContentView), + modlogs.removed_communities.map(modRemovedCommunitiesViewToContentView), + modlogs.banned_from_community.map(modBannedFromCommunityViewToContentView), + modlogs.banned.map(modBanViewToContentView), + modlogs.added_to_community.map(modAddCommunityViewToContentView), + modlogs.transferred_to_community.map(modTransferCommunityViewToContentView), + modlogs.added.map(modAddViewToContentView), + modlogs.admin_purged_persons.map(adminPurgePersonViewToContentView), + modlogs.admin_purged_communities.map(adminPurgeCommunityViewToContentView), + modlogs.admin_purged_posts.map(adminPurgePostViewToContentView), + modlogs.admin_purged_comments.map(adminPurgeCommentViewToContentView), + modlogs.hidden_communities.map(modHideCommunityViewToContentView) + ] + .flat() + .sort((a, b) => b.when.localeCompare(a.when)); +}; + +interface ModerationTarget { + icon?: string; + text: string; + href?: string; +} + +interface ModLogContentView { + id: number; + moderator?: Person; + target: ModerationTarget; + when: string; + reason?: string; + expires?: string; + removed?: boolean; + locked?: boolean; + featured?: boolean; + featureType?: 'community' | 'instance'; + bannedInstance?: boolean; + bannedCommunity?: boolean; + community?: Community; +} + +interface ContentViewModRemovePost extends ModLogContentView { + type: 'mod-remove-post'; + view: ModRemovePostView; +} +interface ContentViewModLockPost extends ModLogContentView { + type: 'mod-lock-post'; + view: ModLockPostView; +} +interface ContentViewModFeaturePost extends ModLogContentView { + type: 'mod-feature-post'; + view: ModFeaturePostView; +} +interface ContentViewModRemoveComment extends ModLogContentView { + type: 'mod-remove-comment'; + view: ModRemoveCommentView; +} +interface ContentViewModRemoveCommunity extends ModLogContentView { + type: 'mod-remove-community'; + view: ModRemoveCommunityView; +} +interface ContentViewModBanFromCommunity extends ModLogContentView { + type: 'mod-ban-community'; + view: ModBanFromCommunityView; +} +interface ContentViewModBan extends ModLogContentView { + type: 'mod-ban'; + view: ModBanView; +} +interface ContentViewModAddCommunity extends ModLogContentView { + type: 'mod-add-community'; + view: ModAddCommunityView; +} +interface ContentViewModTransferCommunity extends ModLogContentView { + type: 'mod-transfer-community'; + view: ModTransferCommunityView; +} +interface ContentViewModAdd extends ModLogContentView { + type: 'mod-add'; + view: ModAddView; +} +interface ContentViewAdminPurgePerson extends ModLogContentView { + type: 'admin-purge-person'; + view: AdminPurgePersonView; +} +interface ContentViewAdminPurgeCommunity extends ModLogContentView { + type: 'admin-purge-community'; + view: AdminPurgeCommunityView; +} +interface ContentViewAdminPurgePost extends ModLogContentView { + type: 'admin-purge-post'; + view: AdminPurgePostView; +} +interface ContentViewAdminPurgeComment extends ModLogContentView { + type: 'admin-purge-comment'; + view: AdminPurgeCommentView; +} +interface ContentViewModHideCommunity extends ModLogContentView { + type: 'admin-mod-hide-community'; + view: ModHideCommunityView; +} + +let inst = ''; +profile.subscribe((prof) => { + inst = prof.instance; +}); + +function makePostTarget(post: Post) { + return { + icon: 'file', + text: 'Post: ' + post.name, + href: `/${inst}/post/${post.id}` + }; +} + +export function modRemovePostViewToContentView(view: ModRemovePostView): ContentViewModRemovePost { + return { + type: 'mod-remove-post', + id: view.mod_remove_post.id, + view, + moderator: view.moderator, + when: view.mod_remove_post.when_, + reason: view.mod_remove_post.reason, + removed: view.mod_remove_post.removed, + target: makePostTarget(view.post), + community: view.community + }; +} +export function modLockPostViewToContentView(view: ModLockPostView): ContentViewModLockPost { + return { + type: 'mod-lock-post', + id: view.mod_lock_post.id, + view, + moderator: view.moderator, + locked: view.mod_lock_post.locked, + when: view.mod_lock_post.when_, + target: makePostTarget(view.post), + community: view.community + }; +} +export function modFeaturePostViewToContentView(view: ModFeaturePostView): ContentViewModFeaturePost { + return { + type: 'mod-feature-post', + id: view.mod_feature_post.id, + view, + featureType: view.mod_feature_post.is_featured_community ? 'community' : 'instance', + moderator: view.moderator, + when: view.mod_feature_post.when_, + featured: view.mod_feature_post.featured, + target: makePostTarget(view.post), + community: view.community + }; +} + +function makeCommentTarget(comment: Comment) { + return { + icon: 'comments', + text: 'Comment: ' + comment.content, + href: `/${inst}/comment/${comment.id}` + }; +} +export function modRemoveCommentViewToContentView(view: ModRemoveCommentView): ContentViewModRemoveComment { + return { + type: 'mod-remove-comment', + id: view.mod_remove_comment.id, + view, + moderator: view.moderator, + when: view.mod_remove_comment.when_, + removed: view.mod_remove_comment.removed, + reason: view.mod_remove_comment.reason, + target: makeCommentTarget(view.comment), + community: view.community + }; +} +export function modRemovedCommunitiesViewToContentView(view: ModRemoveCommunityView): ContentViewModRemoveCommunity { + return { + type: 'mod-remove-community', + id: view.mod_remove_community.id, + view, + moderator: view.moderator, + when: view.mod_remove_community.when_, + removed: view.mod_remove_community.removed, + reason: view.mod_remove_community.reason, + target: { + icon: 'users', + text: '/c/' + (view.community.title || view.community.name), + href: `/${inst}/c/${nameAtInstance(view.community)}` + }, + community: view.community + }; +} + +function makePersonTarget(person: Person) { + return { + icon: 'user', + text: '/u/' + (person.display_name || person.name), + href: `/${inst}/u/${nameAtInstance(person)}` + }; +} + +export function modBannedFromCommunityViewToContentView(view: ModBanFromCommunityView): ContentViewModBanFromCommunity { + return { + type: 'mod-ban-community', + id: view.mod_ban_from_community.id, + view, + moderator: view.moderator, + when: view.mod_ban_from_community.when_, + reason: view.mod_ban_from_community.reason, + expires: view.mod_ban_from_community.expires, + bannedCommunity: view.mod_ban_from_community.banned, + target: makePersonTarget(view.banned_person), + community: view.community + }; +} +export function modBanViewToContentView(view: ModBanView): ContentViewModBan { + return { + type: 'mod-ban', + id: view.mod_ban.id, + view, + moderator: view.moderator, + when: view.mod_ban.when_, + reason: view.mod_ban.reason, + bannedInstance: view.mod_ban.banned, + target: makePersonTarget(view.banned_person) + }; +} +export function modAddCommunityViewToContentView(view: ModAddCommunityView): ContentViewModAddCommunity { + return { + type: 'mod-add-community', + id: view.mod_add_community.id, + view, + moderator: view.moderator, + when: view.mod_add_community.when_, + removed: view.mod_add_community.removed, + target: makePersonTarget(view.modded_person), + community: view.community + }; +} +export function modTransferCommunityViewToContentView(view: ModTransferCommunityView): ContentViewModTransferCommunity { + return { + type: 'mod-transfer-community', + id: view.mod_transfer_community.id, + view, + moderator: view.moderator, + when: view.mod_transfer_community.when_, + target: makePersonTarget(view.modded_person), + community: view.community + }; +} +export function modAddViewToContentView(view: ModAddView): ContentViewModAdd { + return { + type: 'mod-add', + id: view.mod_add.id, + view, + moderator: view.moderator, + when: view.mod_add.when_, + removed: view.mod_add.removed, + target: makePersonTarget(view.modded_person) + }; +} +export function adminPurgePersonViewToContentView(view: AdminPurgePersonView): ContentViewAdminPurgePerson { + return { + type: 'admin-purge-person', + id: view.admin_purge_person.id, + view, + moderator: view.admin, + when: view.admin_purge_person.when_, + reason: view.admin_purge_person.reason, + target: { + text: 'Purged a user' + } + }; +} +export function adminPurgeCommunityViewToContentView(view: AdminPurgeCommunityView): ContentViewAdminPurgeCommunity { + return { + type: 'admin-purge-community', + id: view.admin_purge_community.id, + view, + moderator: view.admin, + when: view.admin_purge_community.when_, + reason: view.admin_purge_community.reason, + target: { + text: 'Purged a community' + } + }; +} +export function adminPurgePostViewToContentView(view: AdminPurgePostView): ContentViewAdminPurgePost { + return { + type: 'admin-purge-post', + id: view.admin_purge_post.id, + view, + moderator: view.admin, + when: view.admin_purge_post.when_, + reason: view.admin_purge_post.reason, + target: { + text: 'Purged a post' + }, + community: view.community + }; +} +export function adminPurgeCommentViewToContentView(view: AdminPurgeCommentView): ContentViewAdminPurgeComment { + return { + type: 'admin-purge-comment', + id: view.admin_purge_comment.id, + view, + moderator: view.admin, + when: view.admin_purge_comment.when_, + reason: view.admin_purge_comment.reason, + target: { + text: 'Purged a comment' + } + }; +} +export function modHideCommunityViewToContentView(view: ModHideCommunityView): ContentViewModHideCommunity { + return { + type: 'admin-mod-hide-community', + id: view.mod_hide_community.id, + view, + moderator: view.admin, + when: view.mod_hide_community.when_, + reason: view.mod_hide_community.reason, + removed: view.mod_hide_community.hidden, + target: { + text: view.community.title ?? view.community.name + }, + community: view.community + }; +} diff --git a/src/lib/instance/InstanceSidebar.svelte b/src/lib/instance/InstanceSidebar.svelte index 83e700c..6e12e40 100644 --- a/src/lib/instance/InstanceSidebar.svelte +++ b/src/lib/instance/InstanceSidebar.svelte @@ -12,18 +12,28 @@ {/if}
+
+ +
diff --git a/src/routes/(app)/[instance]/modlog/+layout.ts b/src/routes/(app)/[instance]/modlog/+layout.ts new file mode 100644 index 0000000..1338d5a --- /dev/null +++ b/src/routes/(app)/[instance]/modlog/+layout.ts @@ -0,0 +1,25 @@ +import { profile } from '$lib/profiles/profiles'; +import { get } from 'svelte/store'; +import type { LayoutLoad } from './$types'; +import type { GetCommunityResponse } from 'lemmy-js-client'; +import { nameAtInstance } from '$lib/nav-utils'; + +export const load = (async ({ url }) => { + const { client, jwt } = get(profile), + communityId = url.searchParams.get('community'); + + let cv: GetCommunityResponse | null = null; + + if (communityId) { + cv = await client.getCommunity({ + id: +communityId, + auth: jwt + }); + } + + return { + communityName: cv ? nameAtInstance(cv.community_view.community) : null, + communityView: cv?.community_view, + communityModerators: cv?.moderators.map((m) => m.moderator) + }; +}) satisfies LayoutLoad; diff --git a/src/routes/(app)/[instance]/modlog/+page.svelte b/src/routes/(app)/[instance]/modlog/+page.svelte new file mode 100644 index 0000000..0215d77 --- /dev/null +++ b/src/routes/(app)/[instance]/modlog/+page.svelte @@ -0,0 +1,33 @@ + + + + +

Modlog

+ + + Are you sure you want to view the modlog{#if data.communityName} + {' '}for /c/{data.communityName}{/if}? Some deleted posts may contain disturbing or adult + material. Proceed with caution. + + + No + Yes + +

+ You can disable this warning in the settings. +

+
+
+ + diff --git a/src/routes/(app)/[instance]/modlog/view/+layout.svelte b/src/routes/(app)/[instance]/modlog/view/+layout.svelte new file mode 100644 index 0000000..d657306 --- /dev/null +++ b/src/routes/(app)/[instance]/modlog/view/+layout.svelte @@ -0,0 +1,30 @@ + + + + + + diff --git a/src/routes/(app)/[instance]/modlog/view/+page.svelte b/src/routes/(app)/[instance]/modlog/view/+page.svelte new file mode 100644 index 0000000..59d0385 --- /dev/null +++ b/src/routes/(app)/[instance]/modlog/view/+page.svelte @@ -0,0 +1,130 @@ + + + + +

Modlog

+ {#if showSecondaryHeader} +

+ {#if data.communityView} + /c/{data.communityView.community.title} + {:else if data.targetUser} + Actions on /u/ + {/if} +

+ {/if} + +
+ {#if data.query.communityId} + + {/if} + {#if data.query.targetId} + + {/if} + + + + {#if data.communityModerators} + + {/if} + +
+ + + + + + + {#if !$siteMeta.site_view.local_site.hide_modlog_mod_names} + + {/if} + + {#if showCommunity} + + {/if} + + + + {#each data.modlogs as modlog} + + {:else} + + + + {/each} + +
TimeActionModDetailsCommunity
No more logs
+ + data.modlogs.length ? 0 : 1)} + makeHref={makePageHref} + as="a" + /> +
+
+ + diff --git a/src/routes/(app)/[instance]/modlog/view/+page.ts b/src/routes/(app)/[instance]/modlog/view/+page.ts new file mode 100644 index 0000000..4b77915 --- /dev/null +++ b/src/routes/(app)/[instance]/modlog/view/+page.ts @@ -0,0 +1,47 @@ +import { profile } from '$lib/profiles/profiles'; +import { get } from 'svelte/store'; +import type { PageLoad } from './$types'; +import { modlogToContentView } from '$lib/content-views'; +import type { ModlogActionType } from 'lemmy-js-client'; + +const id = (idStr: string | null) => (idStr ? +idStr : undefined); + +export const load = (async ({ url }) => { + const { client, jwt } = get(profile), + communityId = id(url.searchParams.get('community')), + moderatorId = id(url.searchParams.get('moderator')), + targetId = id(url.searchParams.get('target')), + page = url.searchParams.get('page'), + pageNum = page ? +page : 1, + action = (url.searchParams.get('action') as ModlogActionType) || 'All', + limit = 20; + + return { + query: { + action, + user: url.searchParams.get('number'), + communityId, + moderatorId, + targetId, + page: pageNum, + limit + }, + targetUser: targetId + ? client.getPersonDetails({ + person_id: targetId, + limit: 0 + }) + : null, + modlogs: client + .getModlog({ + auth: jwt, + community_id: communityId, + mod_person_id: moderatorId, + other_person_id: targetId, + page: pageNum, + limit, + type_: action + }) + .then(modlogToContentView) + }; +}) satisfies PageLoad; diff --git a/src/routes/(app)/[instance]/modlog/view/ModlogAction.svelte b/src/routes/(app)/[instance]/modlog/view/ModlogAction.svelte new file mode 100644 index 0000000..734b294 --- /dev/null +++ b/src/routes/(app)/[instance]/modlog/view/ModlogAction.svelte @@ -0,0 +1,255 @@ + + + + + + + + + + {action.text} + + + {#if modlog.moderator} + + + + + + + {/if} + + + {#if modlog.target} +
+ {#if modlog.target.href} + + {#if modlog.target.icon} + + {/if} + {modlog.target.text} + {:else} + + {#if modlog.target.icon} + + {/if} + {modlog.target.text} + {/if} +
+ {/if} + + {#if modlog.reason} +
+ Reason: + {modlog.reason} +
+ {/if} + {#if modlog.expires} + Expires: {parseExpiration(modlog.expires)} + {/if} +
+ + + {#if showCommunity} + + {#if modlog.community} + + + + + {/if} + + {/if} + + + diff --git a/src/routes/(app)/[instance]/reports/Report.svelte b/src/routes/(app)/[instance]/reports/Report.svelte index 6213003..6e30e33 100644 --- a/src/routes/(app)/[instance]/reports/Report.svelte +++ b/src/routes/(app)/[instance]/reports/Report.svelte @@ -15,6 +15,7 @@
Report by + @@ -64,6 +65,7 @@ import { Stack, MenuButton, Icon } from 'sheodox-ui'; import BusyButton from '$lib/BusyButton.svelte'; import BanButton from './BanButton.svelte'; + import RelativeTime from '$lib/RelativeTime.svelte'; import UserLink from '$lib/UserLink.svelte'; import UserBadges from '$lib/feeds/posts/UserBadges.svelte'; import type { Person } from 'lemmy-js-client'; @@ -79,6 +81,8 @@ export let creator: Person; export let reason: string; export let busy: boolean; + export let reportedAt: string; + $: bannedFromCommunity = $bannedUsers.get(creator.id) ?? false; $: banStateKnown = $bannedUsers.has(creator.id); diff --git a/src/routes/(app)/[instance]/reports/ReportedComment.svelte b/src/routes/(app)/[instance]/reports/ReportedComment.svelte index cb0a087..aec9c61 100644 --- a/src/routes/(app)/[instance]/reports/ReportedComment.svelte +++ b/src/routes/(app)/[instance]/reports/ReportedComment.svelte @@ -70,6 +70,7 @@ resolved={view.comment_report.resolved} resolver={view.resolver} communityId={view.community.id} + reportedAt={view.comment_report.published} on:ban={onBan} /> diff --git a/src/routes/(app)/[instance]/reports/ReportedPost.svelte b/src/routes/(app)/[instance]/reports/ReportedPost.svelte index cfb3c76..16feb3c 100644 --- a/src/routes/(app)/[instance]/reports/ReportedPost.svelte +++ b/src/routes/(app)/[instance]/reports/ReportedPost.svelte @@ -54,6 +54,7 @@ $toggleResolvedState.submit(e.detail)} creator={view.creator} reason={view.post_report.reason} diff --git a/src/routes/(app)/[instance]/settings/+page.svelte b/src/routes/(app)/[instance]/settings/+page.svelte index 00d5dd9..0d2797a 100644 --- a/src/routes/(app)/[instance]/settings/+page.svelte +++ b/src/routes/(app)/[instance]/settings/+page.svelte @@ -2,6 +2,12 @@
Keep navigation sidebar open Load images as .webp + Show modlog content warning + {#if isModerator} + Show modlog content warning (for communities you moderate) + {/if}
@@ -20,8 +26,11 @@ import ThemeSettings from './ThemeSettings.svelte'; import { getAppContext } from '$lib/app-context'; - const { navSidebarOpen } = getAppContext(); - const { nsfwImageHandling, navSidebarDocked, loadImagesAsWebp } = getSettingsContext(); + const { navSidebarOpen, siteMeta } = getAppContext(); + const { nsfwImageHandling, navSidebarDocked, loadImagesAsWebp, showModlogWarning, showModlogWarningModerated } = + getSettingsContext(); + + $: isModerator = !!$siteMeta.my_user?.moderates.length; function onSidebarDockChange() { // if they want it to always show, they probably want it open right now diff --git a/src/routes/(app)/[instance]/u/[username]/+page.svelte b/src/routes/(app)/[instance]/u/[username]/+page.svelte index ad1620d..fa89731 100644 --- a/src/routes/(app)/[instance]/u/[username]/+page.svelte +++ b/src/routes/(app)/[instance]/u/[username]/+page.svelte @@ -15,26 +15,37 @@ pageBaseUrl={data.pageBaseUrl} >
-
-

Stats

- - - {#if data.personView.person.bio} -
- -
- {/if} -
- {#if data.moderates && data.moderates.length} +
-

Moderates

- - {#each data.moderates as mod} - - {/each} - +

Stats

+ + + {#if data.personView.person.bio} +
+ +
+ {/if}
- {/if} + + + + {#if data.moderates && data.moderates.length} +
+

Moderates

+ + {#each data.moderates as mod} + + {/each} + +
+ {/if} +

@@ -54,9 +65,12 @@ import { loadFeedData } from '$lib/feed-query.js'; import type { PageData } from './$types'; import { createContentViewStore, type ContentView } from '$lib/content-views'; + import ModlogLink from '$lib/ModlogLink.svelte'; + import { getSettingsContext } from '$lib/settings-context'; export let data; + const { showModlogWarning } = getSettingsContext(); const cvStore = createContentViewStore(); let loader: ReturnType; From 070a9b3641fe2b76b9d39ebe1e56bd7a794e6ba2 Mon Sep 17 00:00:00 2001 From: sheodox Date: Thu, 31 Aug 2023 21:57:43 -0500 Subject: [PATCH 3/8] Re-add necessary layout load --- .../c/[communityName]/post/[postId]/+layout.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/routes/(app)/[instance]/c/[communityName]/post/[postId]/+layout.ts diff --git a/src/routes/(app)/[instance]/c/[communityName]/post/[postId]/+layout.ts b/src/routes/(app)/[instance]/c/[communityName]/post/[postId]/+layout.ts new file mode 100644 index 0000000..e478d80 --- /dev/null +++ b/src/routes/(app)/[instance]/c/[communityName]/post/[postId]/+layout.ts @@ -0,0 +1,17 @@ +import { profile } from '$lib/profiles/profiles'; +import { get } from 'svelte/store'; +import type { LayoutLoad } from './$types'; + +export const load = (async ({ params }) => { + const { client, jwt } = get(profile); + + const cv = await client.getCommunity({ + name: params.communityName, + auth: jwt + }); + + return { + communityName: params.communityName, + communityView: cv.community_view + }; +}) satisfies LayoutLoad; From e643f86dbb01cfc445ac54f1e9c2b18da25c0dae Mon Sep 17 00:00:00 2001 From: sheodox Date: Thu, 31 Aug 2023 22:49:02 -0500 Subject: [PATCH 4/8] More complete community hiding modlog support I'm guessing at this, I can't actually test this as I'm not an instance admin. --- src/lib/content-views.ts | 18 ++++++++++-------- .../[instance]/modlog/view/ModlogAction.svelte | 15 +++++++++++---- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/lib/content-views.ts b/src/lib/content-views.ts index 968d1ce..95255da 100644 --- a/src/lib/content-views.ts +++ b/src/lib/content-views.ts @@ -481,6 +481,14 @@ export function modRemoveCommentViewToContentView(view: ModRemoveCommentView): C community: view.community }; } + +function makeCommunityTarget(community: Community) { + return { + icon: 'users', + text: '/c/' + (community.title || community.name), + href: `/${inst}/c/${nameAtInstance(community)}` + }; +} export function modRemovedCommunitiesViewToContentView(view: ModRemoveCommunityView): ContentViewModRemoveCommunity { return { type: 'mod-remove-community', @@ -490,11 +498,7 @@ export function modRemovedCommunitiesViewToContentView(view: ModRemoveCommunityV when: view.mod_remove_community.when_, removed: view.mod_remove_community.removed, reason: view.mod_remove_community.reason, - target: { - icon: 'users', - text: '/c/' + (view.community.title || view.community.name), - href: `/${inst}/c/${nameAtInstance(view.community)}` - }, + target: makeCommunityTarget(view.community), community: view.community }; } @@ -629,9 +633,7 @@ export function modHideCommunityViewToContentView(view: ModHideCommunityView): C when: view.mod_hide_community.when_, reason: view.mod_hide_community.reason, removed: view.mod_hide_community.hidden, - target: { - text: view.community.title ?? view.community.name - }, + target: makeCommunityTarget(view.community), community: view.community }; } diff --git a/src/routes/(app)/[instance]/modlog/view/ModlogAction.svelte b/src/routes/(app)/[instance]/modlog/view/ModlogAction.svelte index 734b294..6af37a6 100644 --- a/src/routes/(app)/[instance]/modlog/view/ModlogAction.svelte +++ b/src/routes/(app)/[instance]/modlog/view/ModlogAction.svelte @@ -241,10 +241,17 @@ text: 'Community Transferred' }; } else if (modlog.type === 'admin-mod-hide-community') { - return { - icon: 'eye-slash', - text: 'Community Hidden' - }; + return modlog.removed + ? { + color: 'orange', + icon: 'eye-slash', + text: 'Community Hidden' + } + : { + color: 'orange', + icon: 'eye', + text: 'Community Unhidden' + }; } return { From b5444130be7680ef5225f2754bd18bcff5c9ffbe Mon Sep 17 00:00:00 2001 From: sheodox Date: Thu, 31 Aug 2023 22:49:44 -0500 Subject: [PATCH 5/8] Fade out the bottom of post bodies that are long This should help indicate that there's probably more post to see if you open it --- src/lib/feeds/posts/previews/PostBody.svelte | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/feeds/posts/previews/PostBody.svelte b/src/lib/feeds/posts/previews/PostBody.svelte index 552a8f9..d471a26 100644 --- a/src/lib/feeds/posts/previews/PostBody.svelte +++ b/src/lib/feeds/posts/previews/PostBody.svelte @@ -5,9 +5,13 @@ .preview { max-height: 10rem; overflow: hidden; + color: transparent; + --post-body-color: var(--sx-text-color); + background: linear-gradient(to bottom, var(--post-body-color), var(--post-body-color) 7rem, transparent 10rem); + background-clip: text; } div.read { - color: var(--sx-muted); + --post-body-color: var(--sx-muted); } .fr { float: right; From 610c9cbcb6f13c18dfff66c424efa88cf4bf728e Mon Sep 17 00:00:00 2001 From: sheodox Date: Thu, 31 Aug 2023 23:01:37 -0500 Subject: [PATCH 6/8] Update sheodox-ui for danger button styling on links --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index c595b0f..80e08e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "prettier": "^3.0.0", "prettier-plugin-svelte": "^3.0.0", "sass": "^1.64.1", - "sheodox-ui": "^0.20.1", + "sheodox-ui": "^0.20.2", "svelte": "^4.1.1", "svelte-check": "^3.4.6", "tslib": "^2.6.1", @@ -3533,9 +3533,9 @@ } }, "node_modules/sheodox-ui": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/sheodox-ui/-/sheodox-ui-0.20.1.tgz", - "integrity": "sha512-83X7ZGaTdGfdsXwMQur+ai597F70VQGNs6ZoRAIzHab+EkVM8BXIlP4PCRBJZkcH+O91H4MQVVjjTw7Qg34kMA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/sheodox-ui/-/sheodox-ui-0.20.2.tgz", + "integrity": "sha512-kS/Rev5rAOeGzfRS2U/QLdTgCJMBfX8g8lF/xY0KW9YMuL5FRP5O929+zmlnvLyOQOooNwwyyzS290ljTA3U0Q==", "dev": true, "dependencies": { "@floating-ui/dom": "^1.2.9", diff --git a/package.json b/package.json index 6c23961..9296471 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "prettier": "^3.0.0", "prettier-plugin-svelte": "^3.0.0", "sass": "^1.64.1", - "sheodox-ui": "^0.20.1", + "sheodox-ui": "^0.20.2", "svelte": "^4.1.1", "svelte-check": "^3.4.6", "tslib": "^2.6.1", From 2017bf6ae3636e7a18ae5f73108f72362462abbf Mon Sep 17 00:00:00 2001 From: sheodox Date: Thu, 31 Aug 2023 23:15:56 -0500 Subject: [PATCH 7/8] Censor the moderator name similarly to lemmy-ui Lemmy-ui will hide moderator names and just say "mod" or "admin" if you're not an admin, or the moderator of the community. This matches that behavior. --- .../(app)/[instance]/modlog/view/+page.svelte | 10 +++++++--- .../[instance]/modlog/view/ModlogAction.svelte | 18 ++++++++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/routes/(app)/[instance]/modlog/view/+page.svelte b/src/routes/(app)/[instance]/modlog/view/+page.svelte index 59d0385..d54bc06 100644 --- a/src/routes/(app)/[instance]/modlog/view/+page.svelte +++ b/src/routes/(app)/[instance]/modlog/view/+page.svelte @@ -36,7 +36,7 @@ - {#if data.communityModerators} + {#if data.communityModerators && !censorMods}