]> Untitled Git - lemmy-ui.git/commitdiff
Merge branch 'main' into breakout-role-utils
authorDessalines <dessalines@users.noreply.github.com>
Mon, 19 Jun 2023 14:04:19 +0000 (10:04 -0400)
committerGitHub <noreply@github.com>
Mon, 19 Jun 2023 14:04:19 +0000 (10:04 -0400)
44 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/person/settings.tsx
src/shared/components/post/create-post.tsx
src/shared/components/post/post-form.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/i18next.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/debounce.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 a19ea5c5880c81e3dc4c29a79cd6131f3a432cfa..d0943af2d56b072e1f78c5652d13abd25e7861e9 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 806d2c2f8e78faec10909bd5ee27769e133b1fda..ebf7cb2e0ffc4c1fd548e7192e53961f8fb05357 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 01fa6155603cc43db989a654657f29ca9e3fe82d..c03c68ebecd5443a5c3e53248e25434fa4258416 100644 (file)
@@ -8,7 +8,6 @@ import { HttpService, UserService } from "../../services";
 import {
   concurrentImageUpload,
   customEmojisLookup,
-  isBrowser,
   markdownFieldCharacterLimit,
   markdownHelpUrl,
   maxUploadImages,
@@ -21,6 +20,7 @@ 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";
index 3eb7bd3aa76cde879b0808b95ceeca618ffc4fe2..9ce4f492cffe3c11a2c2ec833cb1886e233a5bd1 100644 (file)
@@ -11,18 +11,17 @@ import { InitialFetchRequest } from "../../interfaces";
 import { FirstLoadService } from "../../services/FirstLoadService";
 import { HttpService, RequestState } from "../../services/HttpService";
 import {
-  QueryParams,
-  RouteDataResponse,
   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 6f3c9112f782231e9af853b199414b2174541a10..54ecfcd7aa9b40e3fe0238e3bd81cc02ed8a26ed 100644 (file)
@@ -62,7 +62,6 @@ import { UserService } from "../../services";
 import { FirstLoadService } from "../../services/FirstLoadService";
 import { HttpService, RequestState } from "../../services/HttpService";
 import {
-  QueryParams,
   RouteDataResponse,
   commentsToFlatNodes,
   communityRSSUrl,
@@ -75,8 +74,6 @@ import {
   getCommentParentId,
   getDataTypeString,
   getPageFromString,
-  getQueryParams,
-  getQueryString,
   myAuth,
   postToCommentSortType,
   relTags,
@@ -89,6 +86,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";
index 720e596fb6a13f0b876cd046c2d2bf9b8fef4b8b..508e5a0d7cc86ac55a808b6913f4601ac6d3debc 100644 (file)
@@ -17,14 +17,14 @@ import {
 import { i18n } from "../../i18next";
 import { UserService } from "../../services";
 import {
-  amAdmin,
-  amMod,
-  amTopMod,
   getUnixTime,
   hostname,
   mdToHtml,
   myAuthRequired,
 } 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 { Badges } from "../common/badges";
 import { BannerIconHeader } from "../common/banner-icon-header";
 import { Icon, PurgeWarning, Spinner } from "../common/icon";
index 4f9b9694cf501d857fdb6ba978d174294d6d7f1c..39753c48e698ee53f4729d827297815fa352edbe 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,
   RouteDataResponse,
@@ -86,6 +82,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 841a3d2b2c59f32bc134183f0860eaaa576305f2..6a270899d6c5acf67e3030eba22bde83b4c1e29c 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 71398a8dbf221bdb0619fe43190a34fac9b4d83b..c7306950b515df029f8dde68fb3e6d43a33e74bb 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 b3f1fff1542c44b440c166aa1d6a62b1870945bf..2db3d4b10d251225bff135b4cebe66edc8110642 100644 (file)
@@ -34,22 +34,21 @@ import { FirstLoadService } from "../services/FirstLoadService";
 import { HttpService, RequestState } from "../services/HttpService";
 import {
   Choice,
-  QueryParams,
-  RouteDataResponse,
-  amAdmin,
-  amMod,
-  debounce,
   fetchLimit,
   fetchUsers,
   getIdFromString,
   getPageFromString,
-  getQueryParams,
-  getQueryString,
   getUpdatedSearchId,
   myAuth,
   personToChoice,
   setIsoData,
 } from "../utils";
+import { debounce } from "../utils/helpers/debounce";
+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 5466bc5fcb746b2d388a7642725208e007201857..b6a3200d62bf833dd0316878711e160b2eceeee2 100644 (file)
@@ -53,9 +53,6 @@ import { UserService } from "../../services";
 import { FirstLoadService } from "../../services/FirstLoadService";
 import { HttpService, RequestState } from "../../services/HttpService";
 import {
-  QueryParams,
-  RouteDataResponse,
-  canMod,
   capitalizeFirstLetter,
   editComment,
   editPost,
@@ -66,10 +63,6 @@ import {
   futureDaysToUnixTime,
   getCommentParentId,
   getPageFromString,
-  getQueryParams,
-  getQueryString,
-  isAdmin,
-  isBanned,
   mdToHtml,
   myAuth,
   myAuthRequired,
@@ -82,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 99a0333645fe4e719b031eefbba68c5e53862780..0be753791d4bb192456a74f5188319d960852041 100644 (file)
@@ -24,7 +24,6 @@ import { FirstLoadService } from "../../services/FirstLoadService";
 import { RequestState } from "../../services/HttpService";
 import {
   RouteDataResponse,
-  amAdmin,
   editCommentReport,
   editPostReport,
   editPrivateMessageReport,
@@ -32,6 +31,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 34303cb2775f56ecd32067b29578451de7f904f4..971ec8911a63d9915c5abcd1f791d17e5a4ca434 100644 (file)
@@ -18,7 +18,6 @@ import {
   Choice,
   capitalizeFirstLetter,
   communityToChoice,
-  debounce,
   elementUrl,
   emDash,
   fetchCommunities,
@@ -36,6 +35,7 @@ import {
   updateCommunityBlock,
   updatePersonBlock,
 } from "../../utils";
+import { debounce } from "../../utils/helpers/debounce";
 import { HtmlTags } from "../common/html-tags";
 import { Icon, Spinner } from "../common/icon";
 import { ImageUploadForm } from "../common/image-upload-form";
index 278977977bb9a2df8511cb71d942c71dd82461ed..7df628b2b2b0461439eb2846285c993ef6cf5c7d 100644 (file)
@@ -17,15 +17,15 @@ import {
 } from "../../services/HttpService";
 import {
   Choice,
-  QueryParams,
   RouteDataResponse,
   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 4640922d022e4b0e214421fc1c12b4fa8113ee3b..c21a6e2b8e7c189e654f232fa3b4662f8f6cdb26 100644 (file)
@@ -18,7 +18,6 @@ import {
   archiveTodayUrl,
   capitalizeFirstLetter,
   communityToChoice,
-  debounce,
   fetchCommunities,
   getIdFromString,
   ghostArchiveUrl,
@@ -33,6 +32,7 @@ import {
   validURL,
   webArchiveUrl,
 } from "../../utils";
+import { debounce } from "../../utils/helpers/debounce";
 import { Icon, Spinner } from "../common/icon";
 import { LanguageSelect } from "../common/language-select";
 import { MarkdownTextArea } from "../common/markdown-textarea";
index 80da4b327dd633fdeb97b28c852181c8ed5d38c0..922fa61efbb72c23508d7e3f80168ecd02dd0365 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 a7365e0d00337f69b96cb700769f38f89b082565..3dee31a7710c79440baef4123ff76d98ff790d57 100644 (file)
@@ -64,7 +64,6 @@ import {
   buildCommentsTree,
   commentsToFlatNodes,
   commentTreeMaxDepth,
-  debounce,
   editComment,
   editWith,
   enableDownvotes,
@@ -73,7 +72,6 @@ import {
   getCommentParentId,
   getDepthFromComment,
   getIdFromProps,
-  isBrowser,
   isImage,
   myAuth,
   restoreScrollPosition,
@@ -85,6 +83,8 @@ import {
   updateCommunityBlock,
   updatePersonBlock,
 } from "../../utils";
+import { isBrowser } from "../../utils/browser/is-browser";
+import { debounce } from "../../utils/helpers/debounce";
 import { CommentForm } from "../comment/comment-form";
 import { CommentNodes } from "../comment/comment-nodes";
 import { HtmlTags } from "../common/html-tags";
index 054cab016be94dd34e2b3294a1bd4825590cc75d..e56056acc01baa4abbc9b905b350f9c4acad786f 100644 (file)
@@ -26,12 +26,10 @@ import { FirstLoadService } from "../services/FirstLoadService";
 import { HttpService, RequestState } from "../services/HttpService";
 import {
   Choice,
-  QueryParams,
   RouteDataResponse,
   capitalizeFirstLetter,
   commentsToFlatNodes,
   communityToChoice,
-  debounce,
   enableDownvotes,
   enableNsfw,
   fetchCommunities,
@@ -39,8 +37,6 @@ import {
   fetchUsers,
   getIdFromString,
   getPageFromString,
-  getQueryParams,
-  getQueryString,
   getUpdatedSearchId,
   myAuth,
   numToSI,
@@ -50,6 +46,10 @@ import {
   setIsoData,
   showLocal,
 } from "../utils";
+import { debounce } from "../utils/helpers/debounce";
+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 47ca6501542b536b0a2eead58434f2c1f59eda4f..0a705ae6499460cdff0a97eaca004e0ab03e2ab0 100644 (file)
@@ -31,7 +31,7 @@ import { sv } from "./translations/sv";
 import { vi } from "./translations/vi";
 import { zh } from "./translations/zh";
 import { zh_Hant } from "./translations/zh_Hant";
-import { isBrowser } from "./utils";
+import { isBrowser } from "./utils/browser/is-browser";
 
 export const languages = [
   { resource: ar, code: "ar", name: "العربية" },
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 c7fbca6b5654db597b098c452af5dc6ed522273b..be461dcd0145ab3fabdb9405407b210ebc2d5bd3 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,
@@ -51,6 +49,9 @@ import {
   VoteType,
 } from "./interfaces";
 import { HttpService, UserService } from "./services";
+import { isBrowser } from "./utils/browser/is-browser";
+import { debounce } from "./utils/helpers/debounce";
+import { groupBy } from "./utils/helpers/group-by";
 import { RequestState } from "./services/HttpService";
 
 let Tribute: any;
@@ -235,92 +236,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]+/;
@@ -371,51 +286,6 @@ export function getDataTypeString(dt: DataType) {
   return dt === DataType.Post ? "Post" : "Comment";
 }
 
-export function debounce<T extends any[], R>(
-  func: (...e: T) => R,
-  wait = 1000,
-  immediate = false
-) {
-  // 'private' variable for instance
-  // The returned function will be able to reference this due to closure.
-  // Each call to the returned function will share this common timer.
-  let timeout: NodeJS.Timeout | null;
-
-  // Calling debounce returns a new anonymous function
-  return function () {
-    // reference the context and args for the setTimeout function
-    const args = arguments;
-
-    // Should the function be called now? If immediate is true
-    //   and not already in a timeout then the answer is: Yes
-    const callNow = immediate && !timeout;
-
-    // This is the basic debounce behavior where you can call this
-    //   function several times, but it will only execute once
-    //   [before or after imposing a delay].
-    //   Each time the returned function is called, the timer starts over.
-    clearTimeout(timeout ?? undefined);
-
-    // Set the new timeout
-    timeout = setTimeout(function () {
-      // Inside the timeout function, clear the timeout variable
-      // which will let the next execution run when in 'immediate' mode
-      timeout = null;
-
-      // Check if the function already ran with the immediate flag
-      if (!immediate) {
-        // Call the original function with apply
-        // apply lets you define the 'this' object as well as the arguments
-        //    (both captured before setTimeout)
-        func.apply(this, args);
-      }
-    }, wait);
-
-    // Immediate mode and no wait timer? Execute the function..
-    if (callNow) func.apply(this, args);
-  } as (...e: T) => R;
-}
-
 export async function fetchThemeList(): Promise<string[]> {
   return fetch("/css/themelist").then(res => res.json());
 }
@@ -1153,10 +1023,6 @@ export function siteBannerCss(banner: string): string {
     `;
 }
 
-export function isBrowser() {
-  return typeof window !== "undefined";
-}
-
 export function setIsoData<T extends RouteData>(context: any): IsoData<T> {
   // If its the browser, you need to deserialize the data from the window
   if (isBrowser()) {
@@ -1286,21 +1152,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();
 }
@@ -1332,15 +1183,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
@@ -1421,64 +1263,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;
@@ -1490,18 +1280,3 @@ export function newVote(voteType: VoteType, myVote?: number): number {
 export type RouteDataResponse<T extends Record<string, any>> = {
   [K in keyof T]: RequestState<T[K]>;
 };
-
-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/debounce.ts b/src/shared/utils/helpers/debounce.ts
new file mode 100644 (file)
index 0000000..d5cd701
--- /dev/null
@@ -0,0 +1,24 @@
+export function debounce<T extends any[], R>(
+  func: (...e: T) => R,
+  wait = 1000,
+  immediate = false
+) {
+  let timeout: NodeJS.Timeout | null;
+
+  return function () {
+    const args = arguments;
+    const callNow = immediate && !timeout;
+
+    clearTimeout(timeout ?? undefined);
+
+    timeout = setTimeout(function () {
+      timeout = null;
+
+      if (!immediate) {
+        func.apply(this, args);
+      }
+    }, wait);
+
+    if (callNow) func.apply(this, args);
+  } as (...e: T) => R;
+}
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;
+};