Skip to content

Commit

Permalink
Merge pull request #72 from sheodox/modlog
Browse files Browse the repository at this point in the history
Modlog
  • Loading branch information
sheodox authored Sep 1, 2023
2 parents c3b2462 + ce3ecd2 commit b798633
Show file tree
Hide file tree
Showing 33 changed files with 1,346 additions and 110 deletions.
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Alexandrite",
"version": "0.8.3",
"version": "0.8.4",
"private": true,
"scripts": {
"dev": "vite dev",
Expand Down Expand Up @@ -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",
Expand Down
94 changes: 94 additions & 0 deletions src/lib/ChainButtons.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<!-- we shouldn't be showing enough to wrap if our calculations work,
but using f-wrap so the element doesn't overflow the container
and make us show every button all the time -->
<div class="chain-buttons f-row f-1 gap-2 f-wrap" use:observeContainer>
{#each outside as action, index}
<div use:observeOutside={{ index }}>
<ExtraActionButton {cl} {action} />
</div>
{/each}
<!-- if we haven't measured its width yet show the overflow anyway -->
{#if overflowWidth === null || collapsed.length}
<div class="chain-overflow" use:observeOverflow>
<!-- when we don't know the size yet, give it all actions so it shows instead of hiding itself -->
<ExtraActions actions={overflowWidth !== null ? collapsed : actions} {cl} />
</div>
{/if}
</div>

<script lang="ts">
import type { ExtraAction } from './utils';
import ExtraActions from './ExtraActions.svelte';
import ExtraActionButton from './ExtraActionButton.svelte';
export let actions: ExtraAction[];
export let cl = '';
let outsideAmount = Infinity;
let availableWidth: number | null = null;
let overflowWidth: number | null = null;
// map of action indexes to button widths
const actionSizes = new Map<number, number>();
$: outside = actions.slice(0, outsideAmount);
$: collapsed = actions.slice(outsideAmount);
const buttonGap = 8; //.gap-2 is 8px wide, this is the spacing between buttons
function fit() {
let outsideWidth = 0,
canFit = -1;
if (availableWidth === null || overflowWidth === null) {
return;
}
for (let i = 0; i < actions.length; i++) {
outsideWidth += (actionSizes.get(i) ?? 0) + (i > 0 ? buttonGap : 0);
const hasMoreActionsAfterThis = i + 1 < actions.length;
if (outsideWidth + (hasMoreActionsAfterThis ? overflowWidth + buttonGap : 0) < availableWidth) {
canFit = i;
} else {
break;
}
}
outsideAmount = canFit + 1;
}
function watchSize(el: HTMLElement, updateFn: (width: number) => void) {
const obs = new ResizeObserver((entries) => {
const width = entries[0].borderBoxSize[0].inlineSize;
updateFn(width);
fit();
});
obs.observe(el);
return {
destroy: () => {
obs.disconnect();
}
};
}
function observeContainer(el: HTMLElement) {
return watchSize(el, (width) => {
availableWidth = width;
});
}
function observeOutside(el: HTMLElement, { index }: { index: number }) {
return watchSize(el, (width) => {
actionSizes.set(index, width);
});
}
function observeOverflow(el: HTMLElement) {
return watchSize(el, (width) => {
overflowWidth = width;
});
}
</script>
22 changes: 18 additions & 4 deletions src/lib/CommunityLink.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<Tooltip>
<div slot="tooltip" class="community-tooltip">
<Stack gap={2} dir="r" align="center">
{#if community.icon && $profile.settings.show_avatars}
{#if community.icon && showIcon}
<div class="community-avatar large">
<Image src={community.icon} mode="thumbnail" />
</div>
Expand All @@ -46,20 +46,22 @@
{/if}
</div>
<span class="f-row gap-1 align-items-center">
{#if community.icon && $profile.settings.show_avatars}
{#if community.icon && showIcon}
<div class="community-avatar small">
<Image src={community.icon} mode="thumbnail" thumbnailResolution={16} />
</div>
{/if}
<EllipsisText
><NameAtInstance place={community} displayName={community.title} prefix="" wrappable={false} /></EllipsisText
>
<CommunityBadges {community} />
{#if showBadges}
<CommunityBadges {community} />
{/if}
</span>
</Tooltip>
{:else}
<Stack gap={2} dir="r" align="center" cl="icon-link">
{#if $profile.settings.show_avatars}
{#if $profile.settings.show_avatars && showIcon}
<Avatar src={community.icon} size="2rem" icon="users" />
{/if}
<span>
Expand All @@ -79,13 +81,25 @@
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;
// if the link should actually go somewhere else, but still have community "branding", use that link instead.
// 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;
</script>
40 changes: 29 additions & 11 deletions src/lib/CommunitySidebar.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<article>
<Sidebar description={community.description ?? ''} bannerImageSrc={community.banner} context="Community">
<a href="/{$profile.instance}/c/{nameAtInstance(community)}" slot="name">
<a href={communityHref} slot="name">
<NameAtInstance place={community} prefix="!" />
</a>

Expand All @@ -14,20 +14,28 @@
<Icon icon="arrow-up-right-from-square" />
View on {communityInstance}
</ExternalLink>
<ModlogLink
communityId={community.id}
highlight={userModerates ?? false}
highlightColor="green"
warn={warnModlog}
/>
</Stack>
</Stack>
</div>
<div slot="end">
{#if moderators}
<Accordion buttonClasses="tertiary">
<span slot="title">Moderators ({moderators.length})</span>
<Stack dir="c" gap={2}>
{#each moderators as mod}
<UserLink user={mod.moderator} />
{/each}
</Stack>
</Accordion>
{/if}
<Stack dir="c" gap={2}>
{#if moderators}
<Accordion buttonClasses="tertiary">
<span slot="title">Moderators ({moderators.length})</span>
<Stack dir="c" gap={2}>
{#each moderators as mod}
<UserLink user={mod.moderator} />
{/each}
</Stack>
</Accordion>
{/if}
</Stack>
</div>
</Sidebar>
</article>
Expand All @@ -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;
</script>
34 changes: 34 additions & 0 deletions src/lib/ExtraActionButton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<style>
.spinner {
/* match the width of icons in dropdown menus from sheodox-ui */
width: 1.5rem;
}
</style>

{#if action.href}
<a href={action.href} class="button m-0 ws-nowrap {cl}"><Icon icon={action.icon} {variant} /> {action.text}</a>
{:else if action.click}
<button on:click={action.click} class="button m-0 ws-nowrap {cl}" disabled={action.busy ?? false} use:ripple>
<div class="f-row gap-1">
{#if action.busy}
<div class="spinner">
<Spinner />
</div>
{:else}
<Icon icon={action.icon} {variant} />
{/if}
<span>{action.text}</span>
</div>
</button>
{/if}

<script lang="ts">
import type { ExtraAction } from './utils';
import Spinner from './Spinner.svelte';
import { Icon, ripple } from 'sheodox-ui';
export let action: ExtraAction;
export let cl = '';
$: variant = action.variant || 'solid';
</script>
30 changes: 6 additions & 24 deletions src/lib/ExtraActions.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
<style>
ul .spinner {
/* match the width of icons in dropdown menus from sheodox-ui */
width: 1.5rem;
}
.extra-actions-trigger {
aspect-ratio: 1;
display: block;
Expand All @@ -13,32 +9,16 @@
{#if actions.length}
{@const text = 'Extra actions'}
<Tooltip title={text}>
<MenuButton triggerClasses="small m-0">
<MenuButton triggerClasses="{small ? 'small' : ''} m-0 {cl}">
<span slot="trigger" class="extra-actions-trigger">
<span class="sr-only">{text}</span>
<Icon icon="ellipsis-vertical" />
</span>

<ul slot="menu">
{#each actions as opt}
{@const variant = opt.variant || 'solid'}
<li>
{#if opt.href}
<a href={opt.href} class="button"><Icon icon={opt.icon} {variant} /> {opt.text}</a>
{:else if opt.click}
<button on:click={opt.click} class="button" disabled={opt.busy ?? false} use:ripple>
<div class="f-row gap-1">
{#if opt.busy}
<div class="spinner">
<Spinner />
</div>
{:else}
<Icon icon={opt.icon} {variant} />
{/if}
<span>{opt.text}</span>
</div>
</button>
{/if}
<ExtraActionButton action={opt} />
</li>
{/each}
</ul>
Expand All @@ -47,9 +27,11 @@
{/if}

<script lang="ts">
import { Tooltip, MenuButton, Icon, ripple } from 'sheodox-ui';
import { Tooltip, MenuButton, Icon } from 'sheodox-ui';
import type { ExtraAction } from './utils';
import Spinner from './Spinner.svelte';
import ExtraActionButton from './ExtraActionButton.svelte';
export let actions: ExtraAction[];
export let small = false;
export let cl = '';
</script>
Loading

0 comments on commit b798633

Please sign in to comment.