]> Untitled Git - lemmy-ui.git/commitdiff
Merge branch 'main' into breakout-role-utils
authorAlec Armbruster <35377827+alectrocute@users.noreply.github.com>
Fri, 16 Jun 2023 21:33:46 +0000 (17:33 -0400)
committerGitHub <noreply@github.com>
Fri, 16 Jun 2023 21:33:46 +0000 (17:33 -0400)
40 files changed:
src/client/index.tsx
src/shared/components/app/navbar.tsx
src/shared/components/comment/comment-node.tsx
src/shared/components/common/markdown-textarea.tsx
src/shared/components/community/communities.tsx
src/shared/components/community/community.tsx
src/shared/components/community/sidebar.tsx
src/shared/components/home/home.tsx
src/shared/components/home/login.tsx
src/shared/components/home/signup.tsx
src/shared/components/modlog.tsx
src/shared/components/person/profile.tsx
src/shared/components/person/reports.tsx
src/shared/components/post/create-post.tsx
src/shared/components/post/post-listing.tsx
src/shared/components/post/post.tsx
src/shared/components/search.tsx
src/shared/env.ts
src/shared/services/UserService.ts
src/shared/utils.ts
src/shared/utils/browser/can-share.ts [new file with mode: 0644]
src/shared/utils/browser/is-browser.ts [new file with mode: 0644]
src/shared/utils/browser/share.ts [new file with mode: 0644]
src/shared/utils/helpers/get-query-params.ts [new file with mode: 0644]
src/shared/utils/helpers/get-query-string.ts [new file with mode: 0644]
src/shared/utils/helpers/group-by.ts [new file with mode: 0644]
src/shared/utils/helpers/poll.ts [new file with mode: 0644]
src/shared/utils/helpers/sleep.ts [new file with mode: 0644]
src/shared/utils/roles/am-admin.ts [new file with mode: 0644]
src/shared/utils/roles/am-community-creator.ts [new file with mode: 0644]
src/shared/utils/roles/am-mod.ts [new file with mode: 0644]
src/shared/utils/roles/am-site-creator.ts [new file with mode: 0644]
src/shared/utils/roles/am-top-mod.ts [new file with mode: 0644]
src/shared/utils/roles/can-admin.ts [new file with mode: 0644]
src/shared/utils/roles/can-create-community.ts [new file with mode: 0644]
src/shared/utils/roles/can-mod.ts [new file with mode: 0644]
src/shared/utils/roles/is-admin.ts [new file with mode: 0644]
src/shared/utils/roles/is-banned.ts [new file with mode: 0644]
src/shared/utils/roles/is-mod.ts [new file with mode: 0644]
src/shared/utils/types/query-params.ts [new file with mode: 0644]

index 7b6b6b1cd662932736a83e4daca2b355d77f2196..860c07565dbad01861283893d209de7aad2099a4 100644 (file)
@@ -1,14 +1,13 @@
 import { hydrate } from "inferno-hydrate";
 import { Router } from "inferno-router";
 import { App } from "../shared/components/app/app";
+import { HistoryService } from "../shared/services/HistoryService";
 import { initializeSite } from "../shared/utils";
 
 import "bootstrap/js/dist/collapse";
 import "bootstrap/js/dist/dropdown";
-import { HistoryService } from "../shared/services/HistoryService";
 
-const site = window.isoData.site_res;
-initializeSite(site);
+initializeSite(window.isoData.site_res);
 
 const wrapper = (
   <Router history={HistoryService.history}>
@@ -17,6 +16,7 @@ const wrapper = (
 );
 
 const root = document.getElementById("root");
+
 if (root) {
   hydrate(wrapper, root);
 }
index 6d310eef36d3b10455638700192f4efd3aa630a7..20d4f251411d10562300d29596b8937d306b4862 100644 (file)
@@ -10,17 +10,17 @@ import { i18n } from "../../i18next";
 import { UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import {
-  amAdmin,
-  canCreateCommunity,
   donateLemmyUrl,
-  isBrowser,
   myAuth,
   numToSI,
-  poll,
   showAvatars,
   toast,
   updateUnreadCountsInterval,
 } from "../../utils";
+import { isBrowser } from "../../utils/browser/is-browser";
+import { poll } from "../../utils/helpers/poll";
+import { amAdmin } from "../../utils/roles/am-admin";
+import { canCreateCommunity } from "../../utils/roles/can-create-community";
 import { Icon } from "../common/icon";
 import { PictrsImage } from "../common/pictrs-image";
 
index 0380a72663935e8f119e309d1e5dc159723f3d7c..10b13c1268846ec0d0993d815b5116ec6e88b265 100644 (file)
@@ -40,16 +40,10 @@ import {
 } from "../../interfaces";
 import { UserService } from "../../services";
 import {
-  amCommunityCreator,
-  canAdmin,
-  canMod,
   colorList,
   commentTreeMaxDepth,
   futureDaysToUnixTime,
   getCommentParentId,
-  isAdmin,
-  isBanned,
-  isMod,
   mdToHtml,
   mdToHtmlNoImages,
   myAuth,
@@ -59,6 +53,12 @@ import {
   setupTippy,
   showScores,
 } from "../../utils";
+import { amCommunityCreator } from "../../utils/roles/am-community-creator";
+import { canAdmin } from "../../utils/roles/can-admin";
+import { canMod } from "../../utils/roles/can-mod";
+import { isAdmin } from "../../utils/roles/is-admin";
+import { isBanned } from "../../utils/roles/is-banned";
+import { isMod } from "../../utils/roles/is-mod";
 import { Icon, PurgeWarning, Spinner } from "../common/icon";
 import { MomentTime } from "../common/moment-time";
 import { CommunityLink } from "../community/community-link";
index a4459ac0236dfef97de988603172a9f05763cc78..4e1bca11f82c9f0b434705350f34665eb83ca541 100644 (file)
@@ -7,7 +7,6 @@ import { HttpService, UserService } from "../../services";
 import {
   concurrentImageUpload,
   customEmojisLookup,
-  isBrowser,
   markdownFieldCharacterLimit,
   markdownHelpUrl,
   maxUploadImages,
@@ -20,12 +19,12 @@ import {
   setupTribute,
   toast,
 } from "../../utils";
+import { isBrowser } from "../../utils/browser/is-browser";
 import { EmojiPicker } from "./emoji-picker";
 import { Icon, Spinner } from "./icon";
 import { LanguageSelect } from "./language-select";
 import NavigationPrompt from "./navigation-prompt";
 import ProgressBar from "./progress-bar";
-
 interface MarkdownTextAreaProps {
   initialContent?: string;
   initialLanguageId?: number;
index 623269439f8642444aad0fcaa6cb8cdb0649d02d..b98bf251ca76852e88c9908294bdc773a2db7143 100644 (file)
@@ -11,17 +11,17 @@ import { InitialFetchRequest } from "../../interfaces";
 import { FirstLoadService } from "../../services/FirstLoadService";
 import { HttpService, RequestState } from "../../services/HttpService";
 import {
-  QueryParams,
   editCommunity,
   getPageFromString,
-  getQueryParams,
-  getQueryString,
   myAuth,
   myAuthRequired,
   numToSI,
   setIsoData,
   showLocal,
 } from "../../utils";
+import { getQueryParams } from "../../utils/helpers/get-query-params";
+import { getQueryString } from "../../utils/helpers/get-query-string";
+import type { QueryParams } from "../../utils/types/query-params";
 import { HtmlTags } from "../common/html-tags";
 import { Spinner } from "../common/icon";
 import { ListingTypeSelect } from "../common/listing-type-select";
index f2d7ad729a8e492b2944205314ae6e47e88c8dca..58b330f448ed9cbb6eae89edf415077b47125ad9 100644 (file)
@@ -62,7 +62,6 @@ import { UserService } from "../../services";
 import { FirstLoadService } from "../../services/FirstLoadService";
 import { HttpService, RequestState } from "../../services/HttpService";
 import {
-  QueryParams,
   commentsToFlatNodes,
   communityRSSUrl,
   editComment,
@@ -74,8 +73,6 @@ import {
   getCommentParentId,
   getDataTypeString,
   getPageFromString,
-  getQueryParams,
-  getQueryString,
   myAuth,
   postToCommentSortType,
   relTags,
@@ -88,6 +85,9 @@ import {
   updateCommunityBlock,
   updatePersonBlock,
 } from "../../utils";
+import { getQueryParams } from "../../utils/helpers/get-query-params";
+import { getQueryString } from "../../utils/helpers/get-query-string";
+import type { QueryParams } from "../../utils/types/query-params";
 import { CommentNodes } from "../comment/comment-nodes";
 import { BannerIconHeader } from "../common/banner-icon-header";
 import { DataTypeSelect } from "../common/data-type-select";
@@ -99,7 +99,6 @@ import { Sidebar } from "../community/sidebar";
 import { SiteSidebar } from "../home/site-sidebar";
 import { PostListings } from "../post/post-listings";
 import { CommunityLink } from "./community-link";
-
 interface State {
   communityRes: RequestState<GetCommunityResponse>;
   postsRes: RequestState<GetPostsResponse>;
index 56b1ef27398eda6340b614c87cae345a8598fc52..592a06ffbef83f1dea4e4c042906bca2e843a9e0 100644 (file)
@@ -17,15 +17,15 @@ import {
 import { i18n } from "../../i18next";
 import { UserService } from "../../services";
 import {
-  amAdmin,
-  amMod,
-  amTopMod,
   getUnixTime,
   hostname,
   mdToHtml,
   myAuthRequired,
   numToSI,
 } from "../../utils";
+import { amAdmin } from "../../utils/roles/am-admin";
+import { amMod } from "../../utils/roles/am-mod";
+import { amTopMod } from "../../utils/roles/am-top-mod";
 import { BannerIconHeader } from "../common/banner-icon-header";
 import { Icon, PurgeWarning, Spinner } from "../common/icon";
 import { CommunityForm } from "../community/community-form";
index 215075d8fbf3d87da9937bb18b0fa473d56d96ba..3613b0f8329a1329e9e522641ac07bfd6b7c0995 100644 (file)
@@ -57,7 +57,6 @@ import { UserService } from "../../services";
 import { FirstLoadService } from "../../services/FirstLoadService";
 import { HttpService, RequestState } from "../../services/HttpService";
 import {
-  canCreateCommunity,
   commentsToFlatNodes,
   editComment,
   editPost,
@@ -68,13 +67,10 @@ import {
   getCommentParentId,
   getDataTypeString,
   getPageFromString,
-  getQueryParams,
-  getQueryString,
   getRandomFromList,
   mdToHtml,
   myAuth,
   postToCommentSortType,
-  QueryParams,
   relTags,
   restoreScrollPosition,
   saveScrollPosition,
@@ -85,6 +81,10 @@ import {
   trendingFetchLimit,
   updatePersonBlock,
 } from "../../utils";
+import { getQueryParams } from "../../utils/helpers/get-query-params";
+import { getQueryString } from "../../utils/helpers/get-query-string";
+import { canCreateCommunity } from "../../utils/roles/can-create-community";
+import type { QueryParams } from "../../utils/types/query-params";
 import { CommentNodes } from "../comment/comment-nodes";
 import { DataTypeSelect } from "../common/data-type-select";
 import { HtmlTags } from "../common/html-tags";
index 381c13bb0ce05dac89f5b3068009f5c59940c0d7..d7e20ec7804c8fc13c0d50bd799cce63cc0f9fc7 100644 (file)
@@ -3,7 +3,8 @@ import { GetSiteResponse, LoginResponse } from "lemmy-js-client";
 import { i18n } from "../../i18next";
 import { UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
-import { isBrowser, myAuth, setIsoData, toast, validEmail } from "../../utils";
+import { myAuth, setIsoData, toast, validEmail } from "../../utils";
+import { isBrowser } from "../../utils/browser/is-browser";
 import { HtmlTags } from "../common/html-tags";
 import { Spinner } from "../common/icon";
 
index 16a3cc6d3eef61dbb4624a6acdefaf90429b3009..192393db98bee758eaab96cdc050ff13936d87df 100644 (file)
@@ -13,7 +13,6 @@ import { i18n } from "../../i18next";
 import { UserService } from "../../services";
 import { HttpService, RequestState } from "../../services/HttpService";
 import {
-  isBrowser,
   joinLemmyUrl,
   mdToHtml,
   myAuth,
@@ -21,6 +20,7 @@ import {
   toast,
   validEmail,
 } from "../../utils";
+import { isBrowser } from "../../utils/browser/is-browser";
 import { HtmlTags } from "../common/html-tags";
 import { Icon, Spinner } from "../common/icon";
 import { MarkdownTextArea } from "../common/markdown-textarea";
index d917f5f35ed675842e43169fa6da129b696335b6..cd0cfcb9cc5d1f2c4085fd578e494841651c79bc 100644 (file)
@@ -33,21 +33,21 @@ import { FirstLoadService } from "../services/FirstLoadService";
 import { HttpService, RequestState } from "../services/HttpService";
 import {
   Choice,
-  QueryParams,
-  amAdmin,
-  amMod,
   debounce,
   fetchLimit,
   fetchUsers,
   getIdFromString,
   getPageFromString,
-  getQueryParams,
-  getQueryString,
   getUpdatedSearchId,
   myAuth,
   personToChoice,
   setIsoData,
 } from "../utils";
+import { getQueryParams } from "../utils/helpers/get-query-params";
+import { getQueryString } from "../utils/helpers/get-query-string";
+import { amAdmin } from "../utils/roles/am-admin";
+import { amMod } from "../utils/roles/am-mod";
+import type { QueryParams } from "../utils/types/query-params";
 import { HtmlTags } from "./common/html-tags";
 import { Icon, Spinner } from "./common/icon";
 import { MomentTime } from "./common/moment-time";
index f80d5b907a2f7f1972dc0b62d1bebb51d06df1fe..c12114bc8f3d8e4fa2ccfb32dfdfb897b7dbad37 100644 (file)
@@ -53,8 +53,6 @@ import { UserService } from "../../services";
 import { FirstLoadService } from "../../services/FirstLoadService";
 import { HttpService, RequestState } from "../../services/HttpService";
 import {
-  QueryParams,
-  canMod,
   capitalizeFirstLetter,
   editComment,
   editPost,
@@ -65,10 +63,6 @@ import {
   futureDaysToUnixTime,
   getCommentParentId,
   getPageFromString,
-  getQueryParams,
-  getQueryString,
-  isAdmin,
-  isBanned,
   mdToHtml,
   myAuth,
   myAuthRequired,
@@ -81,6 +75,12 @@ import {
   toast,
   updatePersonBlock,
 } from "../../utils";
+import { getQueryParams } from "../../utils/helpers/get-query-params";
+import { getQueryString } from "../../utils/helpers/get-query-string";
+import { canMod } from "../../utils/roles/can-mod";
+import { isAdmin } from "../../utils/roles/is-admin";
+import { isBanned } from "../../utils/roles/is-banned";
+import type { QueryParams } from "../../utils/types/query-params";
 import { BannerIconHeader } from "../common/banner-icon-header";
 import { HtmlTags } from "../common/html-tags";
 import { Icon, Spinner } from "../common/icon";
index 29daa3ff6c92dbad9d621d8c2ed9be2e6945985c..187fe4c2e0e06d71548793e49df30728e62ec2ce 100644 (file)
@@ -23,7 +23,6 @@ import { HttpService, UserService } from "../../services";
 import { FirstLoadService } from "../../services/FirstLoadService";
 import { RequestState } from "../../services/HttpService";
 import {
-  amAdmin,
   editCommentReport,
   editPostReport,
   editPrivateMessageReport,
@@ -31,6 +30,7 @@ import {
   myAuthRequired,
   setIsoData,
 } from "../../utils";
+import { amAdmin } from "../../utils/roles/am-admin";
 import { CommentReport } from "../comment/comment-report";
 import { HtmlTags } from "../common/html-tags";
 import { Spinner } from "../common/icon";
index c7597917740486cd58eae285e54232887737b893..f01dd72ff8abd6c34bed4c7d5424bdccdedf3f44 100644 (file)
@@ -16,14 +16,14 @@ import {
 } from "../../services/HttpService";
 import {
   Choice,
-  QueryParams,
   enableDownvotes,
   enableNsfw,
   getIdFromString,
-  getQueryParams,
   myAuth,
   setIsoData,
 } from "../../utils";
+import { getQueryParams } from "../../utils/helpers/get-query-params";
+import type { QueryParams } from "../../utils/types/query-params";
 import { HtmlTags } from "../common/html-tags";
 import { Spinner } from "../common/icon";
 import { PostForm } from "./post-form";
index 60e188a33b77c942ba5e9f33cdceb29a8e07e6bb..4c36f6511cbe4120c6a165b630362ba08eda68fe 100644 (file)
@@ -28,18 +28,9 @@ import { i18n } from "../../i18next";
 import { BanType, PostFormParams, PurgeType, VoteType } from "../../interfaces";
 import { UserService } from "../../services";
 import {
-  amAdmin,
-  amCommunityCreator,
-  amMod,
-  canAdmin,
-  canMod,
-  canShare,
   futureDaysToUnixTime,
   hostname,
-  isAdmin,
-  isBanned,
   isImage,
-  isMod,
   isVideo,
   mdNoImages,
   mdToHtml,
@@ -49,9 +40,18 @@ import {
   numToSI,
   relTags,
   setupTippy,
-  share,
   showScores,
 } from "../../utils";
+import { canShare } from "../../utils/browser/can-share";
+import { share } from "../../utils/browser/share";
+import { amAdmin } from "../../utils/roles/am-admin";
+import { amCommunityCreator } from "../../utils/roles/am-community-creator";
+import { amMod } from "../../utils/roles/am-mod";
+import { canAdmin } from "../../utils/roles/can-admin";
+import { canMod } from "../../utils/roles/can-mod";
+import { isAdmin } from "../../utils/roles/is-admin";
+import { isBanned } from "../../utils/roles/is-banned";
+import { isMod } from "../../utils/roles/is-mod";
 import { Icon, PurgeWarning, Spinner } from "../common/icon";
 import { MomentTime } from "../common/moment-time";
 import { PictrsImage } from "../common/pictrs-image";
index b602f309285cab92ffcdaa40ed7029c371fd916f..05e4d9b918fdb116a193b5d4da7592f9347879a1 100644 (file)
@@ -73,7 +73,6 @@ import {
   getCommentParentId,
   getDepthFromComment,
   getIdFromProps,
-  isBrowser,
   isImage,
   myAuth,
   restoreScrollPosition,
@@ -84,6 +83,7 @@ import {
   updateCommunityBlock,
   updatePersonBlock,
 } from "../../utils";
+import { isBrowser } from "../../utils/browser/is-browser";
 import { CommentForm } from "../comment/comment-form";
 import { CommentNodes } from "../comment/comment-nodes";
 import { HtmlTags } from "../common/html-tags";
index 8097dbde433b7ac7ef3811e47cc44cd6d4ffe76b..59bbf616ca5768e65f169be4a4309bb50bbdf23a 100644 (file)
@@ -26,7 +26,6 @@ import { FirstLoadService } from "../services/FirstLoadService";
 import { HttpService, RequestState } from "../services/HttpService";
 import {
   Choice,
-  QueryParams,
   capitalizeFirstLetter,
   commentsToFlatNodes,
   communityToChoice,
@@ -38,8 +37,6 @@ import {
   fetchUsers,
   getIdFromString,
   getPageFromString,
-  getQueryParams,
-  getQueryString,
   getUpdatedSearchId,
   myAuth,
   numToSI,
@@ -49,6 +46,9 @@ import {
   setIsoData,
   showLocal,
 } from "../utils";
+import { getQueryParams } from "../utils/helpers/get-query-params";
+import { getQueryString } from "../utils/helpers/get-query-string";
+import type { QueryParams } from "../utils/types/query-params";
 import { CommentNodes } from "./comment/comment-nodes";
 import { HtmlTags } from "./common/html-tags";
 import { Spinner } from "./common/icon";
index 576c6c58e916f9d9274bd40200555a9892a8b19c..969f8761241d22e6d80141e4ff133a31434bb7aa 100644 (file)
@@ -1,4 +1,4 @@
-import { isBrowser } from "./utils";
+import { isBrowser } from "./utils/browser/is-browser";
 
 const testHost = "0.0.0.0:8536";
 
index 57c8aecf710c4717f58de4b3b1adeffe84957adb..8f553aba2395aa1c57c8985c225a36b6ce6240a3 100644 (file)
@@ -4,7 +4,8 @@ import jwt_decode from "jwt-decode";
 import { LoginResponse, MyUserInfo } from "lemmy-js-client";
 import { isHttps } from "../env";
 import { i18n } from "../i18next";
-import { isAuthPath, isBrowser, toast } from "../utils";
+import { isAuthPath, toast } from "../utils";
+import { isBrowser } from "../utils/browser/is-browser";
 
 interface Claims {
   sub: number;
index df7673a47b6a1e8e860a41b764bf70c58745b464..bc6e76f8cc71379e16bb32a8bf1eb3b46187c69a 100644 (file)
@@ -9,7 +9,6 @@ import {
   CommentReportView,
   CommentSortType,
   CommentView,
-  CommunityModeratorView,
   CommunityView,
   CustomEmojiView,
   GetSiteMetadata,
@@ -17,7 +16,6 @@ import {
   Language,
   LemmyHttp,
   MyUserInfo,
-  Person,
   PersonMentionView,
   PersonView,
   PostReportView,
@@ -45,6 +43,8 @@ import { getHttpBase } from "./env";
 import { i18n } from "./i18next";
 import { CommentNodeI, DataType, IsoData, VoteType } from "./interfaces";
 import { HttpService, UserService } from "./services";
+import { isBrowser } from "./utils/browser/is-browser";
+import { groupBy } from "./utils/helpers/group-by";
 
 let Tribute: any;
 if (isBrowser()) {
@@ -228,92 +228,6 @@ export function futureDaysToUnixTime(days?: number): number | undefined {
     : undefined;
 }
 
-export function canMod(
-  creator_id: number,
-  mods?: CommunityModeratorView[],
-  admins?: PersonView[],
-  myUserInfo = UserService.Instance.myUserInfo,
-  onSelf = false
-): boolean {
-  // You can do moderator actions only on the mods added after you.
-  let adminsThenMods =
-    admins
-      ?.map(a => a.person.id)
-      .concat(mods?.map(m => m.moderator.id) ?? []) ?? [];
-
-  if (myUserInfo) {
-    const myIndex = adminsThenMods.findIndex(
-      id => id == myUserInfo.local_user_view.person.id
-    );
-    if (myIndex == -1) {
-      return false;
-    } else {
-      // onSelf +1 on mod actions not for yourself, IE ban, remove, etc
-      adminsThenMods = adminsThenMods.slice(0, myIndex + (onSelf ? 0 : 1));
-      return !adminsThenMods.includes(creator_id);
-    }
-  } else {
-    return false;
-  }
-}
-
-export function canAdmin(
-  creatorId: number,
-  admins?: PersonView[],
-  myUserInfo = UserService.Instance.myUserInfo,
-  onSelf = false
-): boolean {
-  return canMod(creatorId, undefined, admins, myUserInfo, onSelf);
-}
-
-export function isMod(
-  creatorId: number,
-  mods?: CommunityModeratorView[]
-): boolean {
-  return mods?.map(m => m.moderator.id).includes(creatorId) ?? false;
-}
-
-export function amMod(
-  mods?: CommunityModeratorView[],
-  myUserInfo = UserService.Instance.myUserInfo
-): boolean {
-  return myUserInfo ? isMod(myUserInfo.local_user_view.person.id, mods) : false;
-}
-
-export function isAdmin(creatorId: number, admins?: PersonView[]): boolean {
-  return admins?.map(a => a.person.id).includes(creatorId) ?? false;
-}
-
-export function amAdmin(myUserInfo = UserService.Instance.myUserInfo): boolean {
-  return myUserInfo?.local_user_view.person.admin ?? false;
-}
-
-export function amCommunityCreator(
-  creator_id: number,
-  mods?: CommunityModeratorView[],
-  myUserInfo = UserService.Instance.myUserInfo
-): boolean {
-  const myId = myUserInfo?.local_user_view.person.id;
-  // Don't allow mod actions on yourself
-  return myId == mods?.at(0)?.moderator.id && myId != creator_id;
-}
-
-export function amSiteCreator(
-  creator_id: number,
-  admins?: PersonView[],
-  myUserInfo = UserService.Instance.myUserInfo
-): boolean {
-  const myId = myUserInfo?.local_user_view.person.id;
-  return myId == admins?.at(0)?.person.id && myId != creator_id;
-}
-
-export function amTopMod(
-  mods: CommunityModeratorView[],
-  myUserInfo = UserService.Instance.myUserInfo
-): boolean {
-  return mods.at(0)?.moderator.id == myUserInfo?.local_user_view.person.id;
-}
-
 const imageRegex = /(http)?s?:?(\/\/[^"']*\.(?:jpg|jpeg|gif|png|svg|webp))/;
 const videoRegex = /(http)?s?:?(\/\/[^"']*\.(?:mp4|webm))/;
 const tldRegex = /([a-z0-9]+\.)*[a-z0-9]+\.[a-z]+/;
@@ -1141,10 +1055,6 @@ export function siteBannerCss(banner: string): string {
     `;
 }
 
-export function isBrowser() {
-  return typeof window !== "undefined";
-}
-
 export function setIsoData(context: any): IsoData {
   // If its the browser, you need to deserialize the data from the window
   if (isBrowser()) {
@@ -1274,21 +1184,6 @@ export function numToSI(value: number): string {
   return SHORTNUM_SI_FORMAT.format(value);
 }
 
-export function isBanned(ps: Person): boolean {
-  const expires = ps.ban_expires;
-  // Add Z to convert from UTC date
-  // TODO this check probably isn't necessary anymore
-  if (expires) {
-    if (ps.banned && new Date(expires + "Z") > new Date()) {
-      return true;
-    } else {
-      return false;
-    }
-  } else {
-    return ps.banned;
-  }
-}
-
 export function myAuth(): string | undefined {
   return UserService.Instance.auth();
 }
@@ -1320,15 +1215,6 @@ export function postToCommentSortType(sort: SortType): CommentSortType {
   }
 }
 
-export function canCreateCommunity(
-  siteRes: GetSiteResponse,
-  myUserInfo = UserService.Instance.myUserInfo
-): boolean {
-  const adminOnly = siteRes.site_view.local_site.community_creation_admin_only;
-  // TODO: Make this check if user is logged on as well
-  return !adminOnly || amAdmin(myUserInfo);
-}
-
 export function isPostBlocked(
   pv: PostView,
   myUserInfo: MyUserInfo | undefined = UserService.Instance.myUserInfo
@@ -1409,64 +1295,12 @@ interface EmojiMartSkin {
   src: string;
 }
 
-const groupBy = <T>(
-  array: T[],
-  predicate: (value: T, index: number, array: T[]) => string
-) =>
-  array.reduce((acc, value, index, array) => {
-    (acc[predicate(value, index, array)] ||= []).push(value);
-    return acc;
-  }, {} as { [key: string]: T[] });
-
-export type QueryParams<T extends Record<string, any>> = {
-  [key in keyof T]?: string;
-};
-
-export function getQueryParams<T extends Record<string, any>>(processors: {
-  [K in keyof T]: (param: string) => T[K];
-}): T {
-  if (isBrowser()) {
-    const searchParams = new URLSearchParams(window.location.search);
-
-    return Array.from(Object.entries(processors)).reduce(
-      (acc, [key, process]) => ({
-        ...acc,
-        [key]: process(searchParams.get(key)),
-      }),
-      {} as T
-    );
-  }
-
-  return {} as T;
-}
-
-export function getQueryString<T extends Record<string, string | undefined>>(
-  obj: T
-) {
-  return Object.entries(obj)
-    .filter(([, val]) => val !== undefined && val !== null)
-    .reduce(
-      (acc, [key, val], index) => `${acc}${index > 0 ? "&" : ""}${key}=${val}`,
-      "?"
-    );
-}
-
 export function isAuthPath(pathname: string) {
   return /create_.*|inbox|settings|admin|reports|registration_applications/g.test(
     pathname
   );
 }
 
-export function canShare() {
-  return isBrowser() && !!navigator.canShare;
-}
-
-export function share(shareData: ShareData) {
-  if (isBrowser()) {
-    navigator.share(shareData);
-  }
-}
-
 export function newVote(voteType: VoteType, myVote?: number): number {
   if (voteType == VoteType.Upvote) {
     return myVote == 1 ? 0 : 1;
@@ -1474,18 +1308,3 @@ export function newVote(voteType: VoteType, myVote?: number): number {
     return myVote == -1 ? 0 : -1;
   }
 }
-
-function sleep(millis: number): Promise<void> {
-  return new Promise(resolve => setTimeout(resolve, millis));
-}
-
-/**
- * Polls / repeatedly runs a promise, every X milliseconds
- */
-export async function poll(promiseFn: any, millis: number) {
-  if (window.document.visibilityState !== "hidden") {
-    await promiseFn();
-  }
-  await sleep(millis);
-  return poll(promiseFn, millis);
-}
diff --git a/src/shared/utils/browser/can-share.ts b/src/shared/utils/browser/can-share.ts
new file mode 100644 (file)
index 0000000..bec7e80
--- /dev/null
@@ -0,0 +1,5 @@
+import { isBrowser } from "./is-browser";
+
+export function canShare() {
+  return isBrowser() && !!navigator.canShare;
+}
diff --git a/src/shared/utils/browser/is-browser.ts b/src/shared/utils/browser/is-browser.ts
new file mode 100644 (file)
index 0000000..4139b25
--- /dev/null
@@ -0,0 +1,3 @@
+export function isBrowser() {
+  return typeof window !== "undefined";
+}
diff --git a/src/shared/utils/browser/share.ts b/src/shared/utils/browser/share.ts
new file mode 100644 (file)
index 0000000..b1d1b5b
--- /dev/null
@@ -0,0 +1,7 @@
+import { isBrowser } from "./is-browser";
+
+export function share(shareData: ShareData) {
+  if (isBrowser()) {
+    navigator.share(shareData);
+  }
+}
diff --git a/src/shared/utils/helpers/get-query-params.ts b/src/shared/utils/helpers/get-query-params.ts
new file mode 100644 (file)
index 0000000..213d352
--- /dev/null
@@ -0,0 +1,19 @@
+import { isBrowser } from "../browser/is-browser";
+
+export function getQueryParams<T extends Record<string, any>>(processors: {
+  [K in keyof T]: (param: string) => T[K];
+}): T {
+  if (isBrowser()) {
+    const searchParams = new URLSearchParams(window.location.search);
+
+    return Array.from(Object.entries(processors)).reduce(
+      (acc, [key, process]) => ({
+        ...acc,
+        [key]: process(searchParams.get(key)),
+      }),
+      {} as T
+    );
+  }
+
+  return {} as T;
+}
diff --git a/src/shared/utils/helpers/get-query-string.ts b/src/shared/utils/helpers/get-query-string.ts
new file mode 100644 (file)
index 0000000..a66b5af
--- /dev/null
@@ -0,0 +1,10 @@
+export function getQueryString<T extends Record<string, string | undefined>>(
+  obj: T
+) {
+  return Object.entries(obj)
+    .filter(([, val]) => val !== undefined && val !== null)
+    .reduce(
+      (acc, [key, val], index) => `${acc}${index > 0 ? "&" : ""}${key}=${val}`,
+      "?"
+    );
+}
diff --git a/src/shared/utils/helpers/group-by.ts b/src/shared/utils/helpers/group-by.ts
new file mode 100644 (file)
index 0000000..4dd5d5d
--- /dev/null
@@ -0,0 +1,8 @@
+export const groupBy = <T>(
+  array: T[],
+  predicate: (value: T, index: number, array: T[]) => string
+) =>
+  array.reduce((acc, value, index, array) => {
+    (acc[predicate(value, index, array)] ||= []).push(value);
+    return acc;
+  }, {} as { [key: string]: T[] });
diff --git a/src/shared/utils/helpers/poll.ts b/src/shared/utils/helpers/poll.ts
new file mode 100644 (file)
index 0000000..055f17f
--- /dev/null
@@ -0,0 +1,12 @@
+import { sleep } from "./sleep";
+
+/**
+ * Polls / repeatedly runs a promise, every X milliseconds
+ */
+export async function poll(promiseFn: any, millis: number) {
+  if (window.document.visibilityState !== "hidden") {
+    await promiseFn();
+  }
+  await sleep(millis);
+  return poll(promiseFn, millis);
+}
diff --git a/src/shared/utils/helpers/sleep.ts b/src/shared/utils/helpers/sleep.ts
new file mode 100644 (file)
index 0000000..5b7c538
--- /dev/null
@@ -0,0 +1,3 @@
+export function sleep(millis: number): Promise<void> {
+  return new Promise(resolve => setTimeout(resolve, millis));
+}
diff --git a/src/shared/utils/roles/am-admin.ts b/src/shared/utils/roles/am-admin.ts
new file mode 100644 (file)
index 0000000..aadf52c
--- /dev/null
@@ -0,0 +1,5 @@
+import { UserService } from "../../services";
+
+export function amAdmin(myUserInfo = UserService.Instance.myUserInfo): boolean {
+  return myUserInfo?.local_user_view.person.admin ?? false;
+}
diff --git a/src/shared/utils/roles/am-community-creator.ts b/src/shared/utils/roles/am-community-creator.ts
new file mode 100644 (file)
index 0000000..20f9b1d
--- /dev/null
@@ -0,0 +1,12 @@
+import { CommunityModeratorView } from "lemmy-js-client";
+import { UserService } from "../../services";
+
+export function amCommunityCreator(
+  creator_id: number,
+  mods?: CommunityModeratorView[],
+  myUserInfo = UserService.Instance.myUserInfo
+): boolean {
+  const myId = myUserInfo?.local_user_view.person.id;
+  // Don't allow mod actions on yourself
+  return myId == mods?.at(0)?.moderator.id && myId != creator_id;
+}
diff --git a/src/shared/utils/roles/am-mod.ts b/src/shared/utils/roles/am-mod.ts
new file mode 100644 (file)
index 0000000..7b792b3
--- /dev/null
@@ -0,0 +1,10 @@
+import { CommunityModeratorView } from "lemmy-js-client";
+import { UserService } from "../../services";
+import { isMod } from "./is-mod";
+
+export function amMod(
+  mods?: CommunityModeratorView[],
+  myUserInfo = UserService.Instance.myUserInfo
+): boolean {
+  return myUserInfo ? isMod(myUserInfo.local_user_view.person.id, mods) : false;
+}
diff --git a/src/shared/utils/roles/am-site-creator.ts b/src/shared/utils/roles/am-site-creator.ts
new file mode 100644 (file)
index 0000000..323ac0a
--- /dev/null
@@ -0,0 +1,11 @@
+import { PersonView } from "lemmy-js-client";
+import { UserService } from "../../services";
+
+export function amSiteCreator(
+  creator_id: number,
+  admins?: PersonView[],
+  myUserInfo = UserService.Instance.myUserInfo
+): boolean {
+  const myId = myUserInfo?.local_user_view.person.id;
+  return myId == admins?.at(0)?.person.id && myId != creator_id;
+}
diff --git a/src/shared/utils/roles/am-top-mod.ts b/src/shared/utils/roles/am-top-mod.ts
new file mode 100644 (file)
index 0000000..4b942da
--- /dev/null
@@ -0,0 +1,9 @@
+import { CommunityModeratorView } from "lemmy-js-client";
+import { UserService } from "../../services";
+
+export function amTopMod(
+  mods: CommunityModeratorView[],
+  myUserInfo = UserService.Instance.myUserInfo
+): boolean {
+  return mods.at(0)?.moderator.id == myUserInfo?.local_user_view.person.id;
+}
diff --git a/src/shared/utils/roles/can-admin.ts b/src/shared/utils/roles/can-admin.ts
new file mode 100644 (file)
index 0000000..080c7ac
--- /dev/null
@@ -0,0 +1,12 @@
+import { PersonView } from "lemmy-js-client";
+import { UserService } from "../../services";
+import { canMod } from "./can-mod";
+
+export function canAdmin(
+  creatorId: number,
+  admins?: PersonView[],
+  myUserInfo = UserService.Instance.myUserInfo,
+  onSelf = false
+): boolean {
+  return canMod(creatorId, undefined, admins, myUserInfo, onSelf);
+}
diff --git a/src/shared/utils/roles/can-create-community.ts b/src/shared/utils/roles/can-create-community.ts
new file mode 100644 (file)
index 0000000..202290d
--- /dev/null
@@ -0,0 +1,12 @@
+import { GetSiteResponse } from "lemmy-js-client";
+import { UserService } from "../../services";
+import { amAdmin } from "./am-admin";
+
+export function canCreateCommunity(
+  siteRes: GetSiteResponse,
+  myUserInfo = UserService.Instance.myUserInfo
+): boolean {
+  const adminOnly = siteRes.site_view.local_site.community_creation_admin_only;
+  // TODO: Make this check if user is logged on as well
+  return !adminOnly || amAdmin(myUserInfo);
+}
diff --git a/src/shared/utils/roles/can-mod.ts b/src/shared/utils/roles/can-mod.ts
new file mode 100644 (file)
index 0000000..2892304
--- /dev/null
@@ -0,0 +1,31 @@
+import { CommunityModeratorView, PersonView } from "lemmy-js-client";
+import { UserService } from "../../services";
+
+export function canMod(
+  creator_id: number,
+  mods?: CommunityModeratorView[],
+  admins?: PersonView[],
+  myUserInfo = UserService.Instance.myUserInfo,
+  onSelf = false
+): boolean {
+  // You can do moderator actions only on the mods added after you.
+  let adminsThenMods =
+    admins
+      ?.map(a => a.person.id)
+      .concat(mods?.map(m => m.moderator.id) ?? []) ?? [];
+
+  if (myUserInfo) {
+    const myIndex = adminsThenMods.findIndex(
+      id => id == myUserInfo.local_user_view.person.id
+    );
+    if (myIndex == -1) {
+      return false;
+    } else {
+      // onSelf +1 on mod actions not for yourself, IE ban, remove, etc
+      adminsThenMods = adminsThenMods.slice(0, myIndex + (onSelf ? 0 : 1));
+      return !adminsThenMods.includes(creator_id);
+    }
+  } else {
+    return false;
+  }
+}
diff --git a/src/shared/utils/roles/is-admin.ts b/src/shared/utils/roles/is-admin.ts
new file mode 100644 (file)
index 0000000..fbf662b
--- /dev/null
@@ -0,0 +1,5 @@
+import { PersonView } from "lemmy-js-client";
+
+export function isAdmin(creatorId: number, admins?: PersonView[]): boolean {
+  return admins?.map(a => a.person.id).includes(creatorId) ?? false;
+}
diff --git a/src/shared/utils/roles/is-banned.ts b/src/shared/utils/roles/is-banned.ts
new file mode 100644 (file)
index 0000000..dd5ffe6
--- /dev/null
@@ -0,0 +1,16 @@
+import { Person } from "lemmy-js-client";
+
+export function isBanned(ps: Person): boolean {
+  const expires = ps.ban_expires;
+  // Add Z to convert from UTC date
+  // TODO this check probably isn't necessary anymore
+  if (expires) {
+    if (ps.banned && new Date(expires + "Z") > new Date()) {
+      return true;
+    } else {
+      return false;
+    }
+  } else {
+    return ps.banned;
+  }
+}
diff --git a/src/shared/utils/roles/is-mod.ts b/src/shared/utils/roles/is-mod.ts
new file mode 100644 (file)
index 0000000..8731107
--- /dev/null
@@ -0,0 +1,8 @@
+import { CommunityModeratorView } from "lemmy-js-client";
+
+export function isMod(
+  creatorId: number,
+  mods?: CommunityModeratorView[]
+): boolean {
+  return mods?.map(m => m.moderator.id).includes(creatorId) ?? false;
+}
diff --git a/src/shared/utils/types/query-params.ts b/src/shared/utils/types/query-params.ts
new file mode 100644 (file)
index 0000000..37705bd
--- /dev/null
@@ -0,0 +1,3 @@
+export type QueryParams<T extends Record<string, any>> = {
+  [key in keyof T]?: string;
+};