mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-22 20:31:13 +00:00
Merge pull request #1332 from alectrocute/breakout-role-utils
Organize `utils.ts` into folder, update imports
This commit is contained in:
commit
94bc9092ee
52 changed files with 376 additions and 303 deletions
|
@ -121,6 +121,7 @@
|
||||||
"sortpack": "^2.3.4",
|
"sortpack": "^2.3.4",
|
||||||
"style-loader": "^3.3.2",
|
"style-loader": "^3.3.2",
|
||||||
"terser": "^5.17.3",
|
"terser": "^5.17.3",
|
||||||
|
"tsconfig-paths-webpack-plugin": "^4.0.1",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"webpack-dev-server": "4.15.0"
|
"webpack-dev-server": "4.15.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import { hydrate } from "inferno-hydrate";
|
import { hydrate } from "inferno-hydrate";
|
||||||
import { Router } from "inferno-router";
|
import { Router } from "inferno-router";
|
||||||
import { App } from "../shared/components/app/app";
|
import { App } from "../shared/components/app/app";
|
||||||
|
import { HistoryService } from "../shared/services/HistoryService";
|
||||||
import { initializeSite } from "../shared/utils";
|
import { initializeSite } from "../shared/utils";
|
||||||
|
|
||||||
import "bootstrap/js/dist/collapse";
|
import "bootstrap/js/dist/collapse";
|
||||||
import "bootstrap/js/dist/dropdown";
|
import "bootstrap/js/dist/dropdown";
|
||||||
import { HistoryService } from "../shared/services/HistoryService";
|
|
||||||
|
|
||||||
const site = window.isoData.site_res;
|
initializeSite(window.isoData.site_res);
|
||||||
initializeSite(site);
|
|
||||||
|
|
||||||
const wrapper = (
|
const wrapper = (
|
||||||
<Router history={HistoryService.history}>
|
<Router history={HistoryService.history}>
|
||||||
|
@ -17,6 +16,7 @@ const wrapper = (
|
||||||
);
|
);
|
||||||
|
|
||||||
const root = document.getElementById("root");
|
const root = document.getElementById("root");
|
||||||
|
|
||||||
if (root) {
|
if (root) {
|
||||||
hydrate(wrapper, root);
|
hydrate(wrapper, root);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import { isBrowser } from "@utils/browser";
|
||||||
|
import { poll } from "@utils/helpers";
|
||||||
|
import { amAdmin, canCreateCommunity } from "@utils/roles";
|
||||||
import { Component, createRef, linkEvent } from "inferno";
|
import { Component, createRef, linkEvent } from "inferno";
|
||||||
import { NavLink } from "inferno-router";
|
import { NavLink } from "inferno-router";
|
||||||
import {
|
import {
|
||||||
|
@ -10,13 +13,9 @@ import { i18n } from "../../i18next";
|
||||||
import { UserService } from "../../services";
|
import { UserService } from "../../services";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
amAdmin,
|
|
||||||
canCreateCommunity,
|
|
||||||
donateLemmyUrl,
|
donateLemmyUrl,
|
||||||
isBrowser,
|
|
||||||
myAuth,
|
myAuth,
|
||||||
numToSI,
|
numToSI,
|
||||||
poll,
|
|
||||||
showAvatars,
|
showAvatars,
|
||||||
toast,
|
toast,
|
||||||
updateUnreadCountsInterval,
|
updateUnreadCountsInterval,
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
import {
|
||||||
|
amCommunityCreator,
|
||||||
|
canAdmin,
|
||||||
|
canMod,
|
||||||
|
isAdmin,
|
||||||
|
isBanned,
|
||||||
|
isMod,
|
||||||
|
} from "@utils/roles";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { Component, InfernoNode, linkEvent } from "inferno";
|
import { Component, InfernoNode, linkEvent } from "inferno";
|
||||||
import { Link } from "inferno-router";
|
import { Link } from "inferno-router";
|
||||||
|
@ -40,16 +48,10 @@ import {
|
||||||
} from "../../interfaces";
|
} from "../../interfaces";
|
||||||
import { UserService } from "../../services";
|
import { UserService } from "../../services";
|
||||||
import {
|
import {
|
||||||
amCommunityCreator,
|
|
||||||
canAdmin,
|
|
||||||
canMod,
|
|
||||||
colorList,
|
colorList,
|
||||||
commentTreeMaxDepth,
|
commentTreeMaxDepth,
|
||||||
futureDaysToUnixTime,
|
futureDaysToUnixTime,
|
||||||
getCommentParentId,
|
getCommentParentId,
|
||||||
isAdmin,
|
|
||||||
isBanned,
|
|
||||||
isMod,
|
|
||||||
mdToHtml,
|
mdToHtml,
|
||||||
mdToHtmlNoImages,
|
mdToHtmlNoImages,
|
||||||
myAuth,
|
myAuth,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { isBrowser } from "@utils/browser";
|
||||||
import autosize from "autosize";
|
import autosize from "autosize";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { NoOptionI18nKeys } from "i18next";
|
import { NoOptionI18nKeys } from "i18next";
|
||||||
|
@ -8,7 +9,6 @@ import { HttpService, UserService } from "../../services";
|
||||||
import {
|
import {
|
||||||
concurrentImageUpload,
|
concurrentImageUpload,
|
||||||
customEmojisLookup,
|
customEmojisLookup,
|
||||||
isBrowser,
|
|
||||||
markdownFieldCharacterLimit,
|
markdownFieldCharacterLimit,
|
||||||
markdownHelpUrl,
|
markdownHelpUrl,
|
||||||
maxUploadImages,
|
maxUploadImages,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { getQueryParams, getQueryString } from "@utils/helpers";
|
||||||
|
import type { QueryParams } from "@utils/types";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommunityResponse,
|
CommunityResponse,
|
||||||
|
@ -11,12 +13,9 @@ import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { FirstLoadService } from "../../services/FirstLoadService";
|
import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
QueryParams,
|
|
||||||
RouteDataResponse,
|
RouteDataResponse,
|
||||||
editCommunity,
|
editCommunity,
|
||||||
getPageFromString,
|
getPageFromString,
|
||||||
getQueryParams,
|
|
||||||
getQueryString,
|
|
||||||
myAuth,
|
myAuth,
|
||||||
myAuthRequired,
|
myAuthRequired,
|
||||||
numToSI,
|
numToSI,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { getQueryParams, getQueryString } from "@utils/helpers";
|
||||||
|
import type { QueryParams } from "@utils/types";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { RouteComponentProps } from "inferno-router/dist/Route";
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
import {
|
import {
|
||||||
|
@ -62,7 +64,6 @@ import { UserService } from "../../services";
|
||||||
import { FirstLoadService } from "../../services/FirstLoadService";
|
import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
QueryParams,
|
|
||||||
RouteDataResponse,
|
RouteDataResponse,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
communityRSSUrl,
|
communityRSSUrl,
|
||||||
|
@ -75,8 +76,6 @@ import {
|
||||||
getCommentParentId,
|
getCommentParentId,
|
||||||
getDataTypeString,
|
getDataTypeString,
|
||||||
getPageFromString,
|
getPageFromString,
|
||||||
getQueryParams,
|
|
||||||
getQueryString,
|
|
||||||
myAuth,
|
myAuth,
|
||||||
postToCommentSortType,
|
postToCommentSortType,
|
||||||
relTags,
|
relTags,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { amAdmin, amMod, amTopMod } from "@utils/roles";
|
||||||
import { Component, InfernoNode, linkEvent } from "inferno";
|
import { Component, InfernoNode, linkEvent } from "inferno";
|
||||||
import { T } from "inferno-i18next-dess";
|
import { T } from "inferno-i18next-dess";
|
||||||
import { Link } from "inferno-router";
|
import { Link } from "inferno-router";
|
||||||
|
@ -16,15 +17,7 @@ import {
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService } from "../../services";
|
import { UserService } from "../../services";
|
||||||
import {
|
import { getUnixTime, hostname, mdToHtml, myAuthRequired } from "../../utils";
|
||||||
amAdmin,
|
|
||||||
amMod,
|
|
||||||
amTopMod,
|
|
||||||
getUnixTime,
|
|
||||||
hostname,
|
|
||||||
mdToHtml,
|
|
||||||
myAuthRequired,
|
|
||||||
} from "../../utils";
|
|
||||||
import { Badges } from "../common/badges";
|
import { Badges } from "../common/badges";
|
||||||
import { BannerIconHeader } from "../common/banner-icon-header";
|
import { BannerIconHeader } from "../common/banner-icon-header";
|
||||||
import { Icon, PurgeWarning, Spinner } from "../common/icon";
|
import { Icon, PurgeWarning, Spinner } from "../common/icon";
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
import { getQueryParams, getQueryString } from "@utils/helpers";
|
||||||
|
import { canCreateCommunity } from "@utils/roles";
|
||||||
|
import type { QueryParams } from "@utils/types";
|
||||||
import { NoOptionI18nKeys } from "i18next";
|
import { NoOptionI18nKeys } from "i18next";
|
||||||
import { Component, linkEvent, MouseEventHandler } from "inferno";
|
import { Component, MouseEventHandler, linkEvent } from "inferno";
|
||||||
import { T } from "inferno-i18next-dess";
|
import { T } from "inferno-i18next-dess";
|
||||||
import { Link } from "inferno-router";
|
import { Link } from "inferno-router";
|
||||||
import {
|
import {
|
||||||
|
@ -57,7 +60,7 @@ import { UserService } from "../../services";
|
||||||
import { FirstLoadService } from "../../services/FirstLoadService";
|
import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
canCreateCommunity,
|
RouteDataResponse,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
editComment,
|
editComment,
|
||||||
editPost,
|
editPost,
|
||||||
|
@ -68,16 +71,12 @@ import {
|
||||||
getCommentParentId,
|
getCommentParentId,
|
||||||
getDataTypeString,
|
getDataTypeString,
|
||||||
getPageFromString,
|
getPageFromString,
|
||||||
getQueryParams,
|
|
||||||
getQueryString,
|
|
||||||
getRandomFromList,
|
getRandomFromList,
|
||||||
mdToHtml,
|
mdToHtml,
|
||||||
myAuth,
|
myAuth,
|
||||||
postToCommentSortType,
|
postToCommentSortType,
|
||||||
QueryParams,
|
|
||||||
relTags,
|
relTags,
|
||||||
restoreScrollPosition,
|
restoreScrollPosition,
|
||||||
RouteDataResponse,
|
|
||||||
saveScrollPosition,
|
saveScrollPosition,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
import { isBrowser } from "@utils/browser";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { GetSiteResponse, LoginResponse } from "lemmy-js-client";
|
import { GetSiteResponse, LoginResponse } from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService } from "../../services";
|
import { UserService } from "../../services";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import { isBrowser, myAuth, setIsoData, toast, validEmail } from "../../utils";
|
import { myAuth, setIsoData, toast, validEmail } from "../../utils";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { isBrowser } from "@utils/browser";
|
||||||
import { Options, passwordStrength } from "check-password-strength";
|
import { Options, passwordStrength } from "check-password-strength";
|
||||||
import { NoOptionI18nKeys } from "i18next";
|
import { NoOptionI18nKeys } from "i18next";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
|
@ -13,7 +14,6 @@ import { i18n } from "../../i18next";
|
||||||
import { UserService } from "../../services";
|
import { UserService } from "../../services";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
isBrowser,
|
|
||||||
joinLemmyUrl,
|
joinLemmyUrl,
|
||||||
mdToHtml,
|
mdToHtml,
|
||||||
myAuth,
|
myAuth,
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import { debounce, getQueryParams, getQueryString } from "@utils/helpers";
|
||||||
|
import { amAdmin, amMod } from "@utils/roles";
|
||||||
|
import type { QueryParams } from "@utils/types";
|
||||||
import { NoOptionI18nKeys } from "i18next";
|
import { NoOptionI18nKeys } from "i18next";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { T } from "inferno-i18next-dess";
|
import { T } from "inferno-i18next-dess";
|
||||||
|
@ -34,17 +37,11 @@ import { FirstLoadService } from "../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../services/HttpService";
|
import { HttpService, RequestState } from "../services/HttpService";
|
||||||
import {
|
import {
|
||||||
Choice,
|
Choice,
|
||||||
QueryParams,
|
|
||||||
RouteDataResponse,
|
RouteDataResponse,
|
||||||
amAdmin,
|
|
||||||
amMod,
|
|
||||||
debounce,
|
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
fetchUsers,
|
fetchUsers,
|
||||||
getIdFromString,
|
getIdFromString,
|
||||||
getPageFromString,
|
getPageFromString,
|
||||||
getQueryParams,
|
|
||||||
getQueryString,
|
|
||||||
getUpdatedSearchId,
|
getUpdatedSearchId,
|
||||||
myAuth,
|
myAuth,
|
||||||
personToChoice,
|
personToChoice,
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import { getQueryParams, getQueryString } from "@utils/helpers";
|
||||||
|
import { canMod, isAdmin, isBanned } from "@utils/roles";
|
||||||
|
import type { QueryParams } from "@utils/types";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { NoOptionI18nKeys } from "i18next";
|
import { NoOptionI18nKeys } from "i18next";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
|
@ -53,9 +56,7 @@ import { UserService } from "../../services";
|
||||||
import { FirstLoadService } from "../../services/FirstLoadService";
|
import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../../services/HttpService";
|
import { HttpService, RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
QueryParams,
|
|
||||||
RouteDataResponse,
|
RouteDataResponse,
|
||||||
canMod,
|
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
editComment,
|
editComment,
|
||||||
editPost,
|
editPost,
|
||||||
|
@ -66,10 +67,6 @@ import {
|
||||||
futureDaysToUnixTime,
|
futureDaysToUnixTime,
|
||||||
getCommentParentId,
|
getCommentParentId,
|
||||||
getPageFromString,
|
getPageFromString,
|
||||||
getQueryParams,
|
|
||||||
getQueryString,
|
|
||||||
isAdmin,
|
|
||||||
isBanned,
|
|
||||||
mdToHtml,
|
mdToHtml,
|
||||||
myAuth,
|
myAuth,
|
||||||
myAuthRequired,
|
myAuthRequired,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { amAdmin } from "@utils/roles";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommentReportResponse,
|
CommentReportResponse,
|
||||||
|
@ -24,7 +25,6 @@ import { FirstLoadService } from "../../services/FirstLoadService";
|
||||||
import { RequestState } from "../../services/HttpService";
|
import { RequestState } from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
RouteDataResponse,
|
RouteDataResponse,
|
||||||
amAdmin,
|
|
||||||
editCommentReport,
|
editCommentReport,
|
||||||
editPostReport,
|
editPostReport,
|
||||||
editPrivateMessageReport,
|
editPrivateMessageReport,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { debounce } from "@utils/helpers";
|
||||||
import { NoOptionI18nKeys } from "i18next";
|
import { NoOptionI18nKeys } from "i18next";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
|
@ -18,7 +19,6 @@ import {
|
||||||
Choice,
|
Choice,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
communityToChoice,
|
communityToChoice,
|
||||||
debounce,
|
|
||||||
elementUrl,
|
elementUrl,
|
||||||
emDash,
|
emDash,
|
||||||
fetchCommunities,
|
fetchCommunities,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { getQueryParams } from "@utils/helpers";
|
||||||
|
import type { QueryParams } from "@utils/types";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { RouteComponentProps } from "inferno-router/dist/Route";
|
import { RouteComponentProps } from "inferno-router/dist/Route";
|
||||||
import {
|
import {
|
||||||
|
@ -17,12 +19,10 @@ import {
|
||||||
} from "../../services/HttpService";
|
} from "../../services/HttpService";
|
||||||
import {
|
import {
|
||||||
Choice,
|
Choice,
|
||||||
QueryParams,
|
|
||||||
RouteDataResponse,
|
RouteDataResponse,
|
||||||
enableDownvotes,
|
enableDownvotes,
|
||||||
enableNsfw,
|
enableNsfw,
|
||||||
getIdFromString,
|
getIdFromString,
|
||||||
getQueryParams,
|
|
||||||
myAuth,
|
myAuth,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { debounce } from "@utils/helpers";
|
||||||
import autosize from "autosize";
|
import autosize from "autosize";
|
||||||
import { Component, InfernoNode, linkEvent } from "inferno";
|
import { Component, InfernoNode, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
|
@ -18,7 +19,6 @@ import {
|
||||||
archiveTodayUrl,
|
archiveTodayUrl,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
communityToChoice,
|
communityToChoice,
|
||||||
debounce,
|
|
||||||
fetchCommunities,
|
fetchCommunities,
|
||||||
getIdFromString,
|
getIdFromString,
|
||||||
ghostArchiveUrl,
|
ghostArchiveUrl,
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
|
import { canShare, share } from "@utils/browser";
|
||||||
|
import {
|
||||||
|
amAdmin,
|
||||||
|
amCommunityCreator,
|
||||||
|
amMod,
|
||||||
|
canAdmin,
|
||||||
|
canMod,
|
||||||
|
isAdmin,
|
||||||
|
isBanned,
|
||||||
|
isMod,
|
||||||
|
} from "@utils/roles";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { Link } from "inferno-router";
|
import { Link } from "inferno-router";
|
||||||
|
@ -28,18 +39,9 @@ import { i18n } from "../../i18next";
|
||||||
import { BanType, PostFormParams, PurgeType, VoteType } from "../../interfaces";
|
import { BanType, PostFormParams, PurgeType, VoteType } from "../../interfaces";
|
||||||
import { UserService } from "../../services";
|
import { UserService } from "../../services";
|
||||||
import {
|
import {
|
||||||
amAdmin,
|
|
||||||
amCommunityCreator,
|
|
||||||
amMod,
|
|
||||||
canAdmin,
|
|
||||||
canMod,
|
|
||||||
canShare,
|
|
||||||
futureDaysToUnixTime,
|
futureDaysToUnixTime,
|
||||||
hostname,
|
hostname,
|
||||||
isAdmin,
|
|
||||||
isBanned,
|
|
||||||
isImage,
|
isImage,
|
||||||
isMod,
|
|
||||||
isVideo,
|
isVideo,
|
||||||
mdNoImages,
|
mdNoImages,
|
||||||
mdToHtml,
|
mdToHtml,
|
||||||
|
@ -49,7 +51,6 @@ import {
|
||||||
numToSI,
|
numToSI,
|
||||||
relTags,
|
relTags,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
share,
|
|
||||||
showScores,
|
showScores,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { Icon, PurgeWarning, Spinner } from "../common/icon";
|
import { Icon, PurgeWarning, Spinner } from "../common/icon";
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { isBrowser } from "@utils/browser";
|
||||||
|
import { debounce } from "@utils/helpers";
|
||||||
import autosize from "autosize";
|
import autosize from "autosize";
|
||||||
import { Component, createRef, linkEvent, RefObject } from "inferno";
|
import { Component, createRef, linkEvent, RefObject } from "inferno";
|
||||||
import {
|
import {
|
||||||
|
@ -64,7 +66,6 @@ import {
|
||||||
buildCommentsTree,
|
buildCommentsTree,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
commentTreeMaxDepth,
|
commentTreeMaxDepth,
|
||||||
debounce,
|
|
||||||
editComment,
|
editComment,
|
||||||
editWith,
|
editWith,
|
||||||
enableDownvotes,
|
enableDownvotes,
|
||||||
|
@ -73,7 +74,6 @@ import {
|
||||||
getCommentParentId,
|
getCommentParentId,
|
||||||
getDepthFromComment,
|
getDepthFromComment,
|
||||||
getIdFromProps,
|
getIdFromProps,
|
||||||
isBrowser,
|
|
||||||
isImage,
|
isImage,
|
||||||
myAuth,
|
myAuth,
|
||||||
restoreScrollPosition,
|
restoreScrollPosition,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { debounce, getQueryParams, getQueryString } from "@utils/helpers";
|
||||||
|
import type { QueryParams } from "@utils/types";
|
||||||
import type { NoOptionI18nKeys } from "i18next";
|
import type { NoOptionI18nKeys } from "i18next";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
|
@ -26,12 +28,10 @@ import { FirstLoadService } from "../services/FirstLoadService";
|
||||||
import { HttpService, RequestState } from "../services/HttpService";
|
import { HttpService, RequestState } from "../services/HttpService";
|
||||||
import {
|
import {
|
||||||
Choice,
|
Choice,
|
||||||
QueryParams,
|
|
||||||
RouteDataResponse,
|
RouteDataResponse,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
communityToChoice,
|
communityToChoice,
|
||||||
debounce,
|
|
||||||
enableDownvotes,
|
enableDownvotes,
|
||||||
enableNsfw,
|
enableNsfw,
|
||||||
fetchCommunities,
|
fetchCommunities,
|
||||||
|
@ -39,8 +39,6 @@ import {
|
||||||
fetchUsers,
|
fetchUsers,
|
||||||
getIdFromString,
|
getIdFromString,
|
||||||
getPageFromString,
|
getPageFromString,
|
||||||
getQueryParams,
|
|
||||||
getQueryString,
|
|
||||||
getUpdatedSearchId,
|
getUpdatedSearchId,
|
||||||
myAuth,
|
myAuth,
|
||||||
numToSI,
|
numToSI,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { isBrowser } from "./utils";
|
import { isBrowser } from "@utils/browser";
|
||||||
|
|
||||||
const testHost = "0.0.0.0:8536";
|
const testHost = "0.0.0.0:8536";
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { isBrowser } from "@utils/browser";
|
||||||
import i18next, { i18nTyped, Resource } from "i18next";
|
import i18next, { i18nTyped, Resource } from "i18next";
|
||||||
import { UserService } from "./services";
|
import { UserService } from "./services";
|
||||||
import { ar } from "./translations/ar";
|
import { ar } from "./translations/ar";
|
||||||
|
@ -31,7 +32,6 @@ import { sv } from "./translations/sv";
|
||||||
import { vi } from "./translations/vi";
|
import { vi } from "./translations/vi";
|
||||||
import { zh } from "./translations/zh";
|
import { zh } from "./translations/zh";
|
||||||
import { zh_Hant } from "./translations/zh_Hant";
|
import { zh_Hant } from "./translations/zh_Hant";
|
||||||
import { isBrowser } from "./utils";
|
|
||||||
|
|
||||||
export const languages = [
|
export const languages = [
|
||||||
{ resource: ar, code: "ar", name: "العربية" },
|
{ resource: ar, code: "ar", name: "العربية" },
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
// import Cookies from 'js-cookie';
|
// import Cookies from 'js-cookie';
|
||||||
|
import { isBrowser } from "@utils/browser";
|
||||||
import IsomorphicCookie from "isomorphic-cookie";
|
import IsomorphicCookie from "isomorphic-cookie";
|
||||||
import jwt_decode from "jwt-decode";
|
import jwt_decode from "jwt-decode";
|
||||||
import { LoginResponse, MyUserInfo } from "lemmy-js-client";
|
import { LoginResponse, MyUserInfo } from "lemmy-js-client";
|
||||||
import { isHttps } from "../env";
|
import { isHttps } from "../env";
|
||||||
import { i18n } from "../i18next";
|
import { i18n } from "../i18next";
|
||||||
import { isAuthPath, isBrowser, toast } from "../utils";
|
import { isAuthPath, toast } from "../utils";
|
||||||
|
|
||||||
interface Claims {
|
interface Claims {
|
||||||
sub: number;
|
sub: number;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { isBrowser } from "@utils/browser";
|
||||||
|
import { debounce, groupBy } from "@utils/helpers";
|
||||||
import { Picker } from "emoji-mart";
|
import { Picker } from "emoji-mart";
|
||||||
import emojiShortName from "emoji-short-name";
|
import emojiShortName from "emoji-short-name";
|
||||||
import {
|
import {
|
||||||
|
@ -9,7 +11,6 @@ import {
|
||||||
CommentReportView,
|
CommentReportView,
|
||||||
CommentSortType,
|
CommentSortType,
|
||||||
CommentView,
|
CommentView,
|
||||||
CommunityModeratorView,
|
|
||||||
CommunityView,
|
CommunityView,
|
||||||
CustomEmojiView,
|
CustomEmojiView,
|
||||||
GetSiteMetadata,
|
GetSiteMetadata,
|
||||||
|
@ -17,7 +18,6 @@ import {
|
||||||
Language,
|
Language,
|
||||||
LemmyHttp,
|
LemmyHttp,
|
||||||
MyUserInfo,
|
MyUserInfo,
|
||||||
Person,
|
|
||||||
PersonMentionView,
|
PersonMentionView,
|
||||||
PersonView,
|
PersonView,
|
||||||
PostReportView,
|
PostReportView,
|
||||||
|
@ -235,92 +235,6 @@ export function futureDaysToUnixTime(days?: number): number | undefined {
|
||||||
: 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 imageRegex = /(http)?s?:?(\/\/[^"']*\.(?:jpg|jpeg|gif|png|svg|webp))/;
|
||||||
const videoRegex = /(http)?s?:?(\/\/[^"']*\.(?:mp4|webm))/;
|
const videoRegex = /(http)?s?:?(\/\/[^"']*\.(?:mp4|webm))/;
|
||||||
const tldRegex = /([a-z0-9]+\.)*[a-z0-9]+\.[a-z]+/;
|
const tldRegex = /([a-z0-9]+\.)*[a-z0-9]+\.[a-z]+/;
|
||||||
|
@ -371,51 +285,6 @@ export function getDataTypeString(dt: DataType) {
|
||||||
return dt === DataType.Post ? "Post" : "Comment";
|
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[]> {
|
export async function fetchThemeList(): Promise<string[]> {
|
||||||
return fetch("/css/themelist").then(res => res.json());
|
return fetch("/css/themelist").then(res => res.json());
|
||||||
}
|
}
|
||||||
|
@ -1153,10 +1022,6 @@ export function siteBannerCss(banner: string): string {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isBrowser() {
|
|
||||||
return typeof window !== "undefined";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setIsoData<T extends RouteData>(context: any): IsoData<T> {
|
export function setIsoData<T extends RouteData>(context: any): IsoData<T> {
|
||||||
// If its the browser, you need to deserialize the data from the window
|
// If its the browser, you need to deserialize the data from the window
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
|
@ -1286,21 +1151,6 @@ export function numToSI(value: number): string {
|
||||||
return SHORTNUM_SI_FORMAT.format(value);
|
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 {
|
export function myAuth(): string | undefined {
|
||||||
return UserService.Instance.auth();
|
return UserService.Instance.auth();
|
||||||
}
|
}
|
||||||
|
@ -1332,15 +1182,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(
|
export function isPostBlocked(
|
||||||
pv: PostView,
|
pv: PostView,
|
||||||
myUserInfo: MyUserInfo | undefined = UserService.Instance.myUserInfo
|
myUserInfo: MyUserInfo | undefined = UserService.Instance.myUserInfo
|
||||||
|
@ -1421,64 +1262,12 @@ interface EmojiMartSkin {
|
||||||
src: string;
|
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) {
|
export function isAuthPath(pathname: string) {
|
||||||
return /create_.*|inbox|settings|admin|reports|registration_applications/g.test(
|
return /create_.*|inbox|settings|admin|reports|registration_applications/g.test(
|
||||||
pathname
|
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 {
|
export function newVote(voteType: VoteType, myVote?: number): number {
|
||||||
if (voteType == VoteType.Upvote) {
|
if (voteType == VoteType.Upvote) {
|
||||||
return myVote == 1 ? 0 : 1;
|
return myVote == 1 ? 0 : 1;
|
||||||
|
@ -1490,18 +1279,3 @@ export function newVote(voteType: VoteType, myVote?: number): number {
|
||||||
export type RouteDataResponse<T extends Record<string, any>> = {
|
export type RouteDataResponse<T extends Record<string, any>> = {
|
||||||
[K in keyof T]: RequestState<T[K]>;
|
[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);
|
|
||||||
}
|
|
||||||
|
|
5
src/shared/utils/browser/can-share.ts
Normal file
5
src/shared/utils/browser/can-share.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { isBrowser } from "@utils/browser";
|
||||||
|
|
||||||
|
export default function canShare() {
|
||||||
|
return isBrowser() && !!navigator.canShare;
|
||||||
|
}
|
5
src/shared/utils/browser/index.ts
Normal file
5
src/shared/utils/browser/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import canShare from "./can-share";
|
||||||
|
import isBrowser from "./is-browser";
|
||||||
|
import share from "./share";
|
||||||
|
|
||||||
|
export { canShare, isBrowser, share };
|
3
src/shared/utils/browser/is-browser.ts
Normal file
3
src/shared/utils/browser/is-browser.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default function isBrowser() {
|
||||||
|
return typeof window !== "undefined";
|
||||||
|
}
|
7
src/shared/utils/browser/share.ts
Normal file
7
src/shared/utils/browser/share.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { isBrowser } from "@utils/browser";
|
||||||
|
|
||||||
|
export default function share(shareData: ShareData) {
|
||||||
|
if (isBrowser()) {
|
||||||
|
navigator.share(shareData);
|
||||||
|
}
|
||||||
|
}
|
24
src/shared/utils/helpers/debounce.ts
Normal file
24
src/shared/utils/helpers/debounce.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
export default 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;
|
||||||
|
}
|
21
src/shared/utils/helpers/get-query-params.ts
Normal file
21
src/shared/utils/helpers/get-query-params.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { isBrowser } from "@utils/browser";
|
||||||
|
|
||||||
|
export default 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;
|
||||||
|
}
|
10
src/shared/utils/helpers/get-query-string.ts
Normal file
10
src/shared/utils/helpers/get-query-string.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export default 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}`,
|
||||||
|
"?"
|
||||||
|
);
|
||||||
|
}
|
8
src/shared/utils/helpers/group-by.ts
Normal file
8
src/shared/utils/helpers/group-by.ts
Normal file
|
@ -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[] });
|
8
src/shared/utils/helpers/index.ts
Normal file
8
src/shared/utils/helpers/index.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import debounce from "./debounce";
|
||||||
|
import getQueryParams from "./get-query-params";
|
||||||
|
import getQueryString from "./get-query-string";
|
||||||
|
import { groupBy } from "./group-by";
|
||||||
|
import poll from "./poll";
|
||||||
|
import sleep from "./sleep";
|
||||||
|
|
||||||
|
export { debounce, getQueryParams, getQueryString, groupBy, poll, sleep };
|
12
src/shared/utils/helpers/poll.ts
Normal file
12
src/shared/utils/helpers/poll.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import sleep from "./sleep";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polls / repeatedly runs a promise, every X milliseconds
|
||||||
|
*/
|
||||||
|
export default async function poll(promiseFn: any, millis: number) {
|
||||||
|
if (window.document.visibilityState !== "hidden") {
|
||||||
|
await promiseFn();
|
||||||
|
}
|
||||||
|
await sleep(millis);
|
||||||
|
return poll(promiseFn, millis);
|
||||||
|
}
|
3
src/shared/utils/helpers/sleep.ts
Normal file
3
src/shared/utils/helpers/sleep.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default function sleep(millis: number): Promise<void> {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, millis));
|
||||||
|
}
|
7
src/shared/utils/roles/am-admin.ts
Normal file
7
src/shared/utils/roles/am-admin.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { UserService } from "../../services";
|
||||||
|
|
||||||
|
export default function amAdmin(
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): boolean {
|
||||||
|
return myUserInfo?.local_user_view.person.admin ?? false;
|
||||||
|
}
|
12
src/shared/utils/roles/am-community-creator.ts
Normal file
12
src/shared/utils/roles/am-community-creator.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { CommunityModeratorView } from "lemmy-js-client";
|
||||||
|
import { UserService } from "../../services";
|
||||||
|
|
||||||
|
export default 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;
|
||||||
|
}
|
10
src/shared/utils/roles/am-mod.ts
Normal file
10
src/shared/utils/roles/am-mod.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { isMod } from "@utils/roles";
|
||||||
|
import { CommunityModeratorView } from "lemmy-js-client";
|
||||||
|
import { UserService } from "../../services";
|
||||||
|
|
||||||
|
export default function amMod(
|
||||||
|
mods?: CommunityModeratorView[],
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): boolean {
|
||||||
|
return myUserInfo ? isMod(myUserInfo.local_user_view.person.id, mods) : false;
|
||||||
|
}
|
11
src/shared/utils/roles/am-site-creator.ts
Normal file
11
src/shared/utils/roles/am-site-creator.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { PersonView } from "lemmy-js-client";
|
||||||
|
import { UserService } from "../../services";
|
||||||
|
|
||||||
|
export default 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;
|
||||||
|
}
|
9
src/shared/utils/roles/am-top-mod.ts
Normal file
9
src/shared/utils/roles/am-top-mod.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { CommunityModeratorView } from "lemmy-js-client";
|
||||||
|
import { UserService } from "../../services";
|
||||||
|
|
||||||
|
export default function amTopMod(
|
||||||
|
mods: CommunityModeratorView[],
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): boolean {
|
||||||
|
return mods.at(0)?.moderator.id == myUserInfo?.local_user_view.person.id;
|
||||||
|
}
|
12
src/shared/utils/roles/can-admin.ts
Normal file
12
src/shared/utils/roles/can-admin.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { canMod } from "@utils/roles";
|
||||||
|
import { PersonView } from "lemmy-js-client";
|
||||||
|
import { UserService } from "../../services";
|
||||||
|
|
||||||
|
export default function canAdmin(
|
||||||
|
creatorId: number,
|
||||||
|
admins?: PersonView[],
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo,
|
||||||
|
onSelf = false
|
||||||
|
): boolean {
|
||||||
|
return canMod(creatorId, undefined, admins, myUserInfo, onSelf);
|
||||||
|
}
|
12
src/shared/utils/roles/can-create-community.ts
Normal file
12
src/shared/utils/roles/can-create-community.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { amAdmin } from "@utils/roles";
|
||||||
|
import { GetSiteResponse } from "lemmy-js-client";
|
||||||
|
import { UserService } from "../../services";
|
||||||
|
|
||||||
|
export default 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);
|
||||||
|
}
|
31
src/shared/utils/roles/can-mod.ts
Normal file
31
src/shared/utils/roles/can-mod.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { CommunityModeratorView, PersonView } from "lemmy-js-client";
|
||||||
|
import { UserService } from "../../services";
|
||||||
|
|
||||||
|
export default 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;
|
||||||
|
}
|
||||||
|
}
|
25
src/shared/utils/roles/index.ts
Normal file
25
src/shared/utils/roles/index.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import amAdmin from "./am-admin";
|
||||||
|
import amCommunityCreator from "./am-community-creator";
|
||||||
|
import amMod from "./am-mod";
|
||||||
|
import amSiteCreator from "./am-site-creator";
|
||||||
|
import amTopMod from "./am-top-mod";
|
||||||
|
import canAdmin from "./can-admin";
|
||||||
|
import canCreateCommunity from "./can-create-community";
|
||||||
|
import canMod from "./can-mod";
|
||||||
|
import isAdmin from "./is-admin";
|
||||||
|
import isBanned from "./is-banned";
|
||||||
|
import isMod from "./is-mod";
|
||||||
|
|
||||||
|
export {
|
||||||
|
amAdmin,
|
||||||
|
amCommunityCreator,
|
||||||
|
amMod,
|
||||||
|
amSiteCreator,
|
||||||
|
amTopMod,
|
||||||
|
canAdmin,
|
||||||
|
canCreateCommunity,
|
||||||
|
canMod,
|
||||||
|
isAdmin,
|
||||||
|
isBanned,
|
||||||
|
isMod,
|
||||||
|
};
|
8
src/shared/utils/roles/is-admin.ts
Normal file
8
src/shared/utils/roles/is-admin.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { PersonView } from "lemmy-js-client";
|
||||||
|
|
||||||
|
export default function isAdmin(
|
||||||
|
creatorId: number,
|
||||||
|
admins?: PersonView[]
|
||||||
|
): boolean {
|
||||||
|
return admins?.map(a => a.person.id).includes(creatorId) ?? false;
|
||||||
|
}
|
16
src/shared/utils/roles/is-banned.ts
Normal file
16
src/shared/utils/roles/is-banned.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { Person } from "lemmy-js-client";
|
||||||
|
|
||||||
|
export default 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;
|
||||||
|
}
|
||||||
|
}
|
8
src/shared/utils/roles/is-mod.ts
Normal file
8
src/shared/utils/roles/is-mod.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { CommunityModeratorView } from "lemmy-js-client";
|
||||||
|
|
||||||
|
export default function isMod(
|
||||||
|
creatorId: number,
|
||||||
|
mods?: CommunityModeratorView[]
|
||||||
|
): boolean {
|
||||||
|
return mods?.map(m => m.moderator.id).includes(creatorId) ?? false;
|
||||||
|
}
|
3
src/shared/utils/types/index.ts
Normal file
3
src/shared/utils/types/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { QueryParams } from "./query-params";
|
||||||
|
|
||||||
|
export { QueryParams };
|
3
src/shared/utils/types/query-params.ts
Normal file
3
src/shared/utils/types/query-params.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export type QueryParams<T extends Record<string, any>> = {
|
||||||
|
[key in keyof T]?: string;
|
||||||
|
};
|
|
@ -18,7 +18,13 @@
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"paths": {
|
||||||
|
"@utils/roles": ["./shared/utils/roles/index"],
|
||||||
|
"@utils/browser": ["./shared/utils/browser/index"],
|
||||||
|
"@utils/helpers": ["./shared/utils/helpers/index"],
|
||||||
|
"@utils/types": ["./shared/utils/types/index"],
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*.ts",
|
"src/**/*.ts",
|
||||||
|
|
|
@ -5,6 +5,7 @@ const CopyPlugin = require("copy-webpack-plugin");
|
||||||
const RunNodeWebpackPlugin = require("run-node-webpack-plugin");
|
const RunNodeWebpackPlugin = require("run-node-webpack-plugin");
|
||||||
const merge = require("lodash/merge");
|
const merge = require("lodash/merge");
|
||||||
const { ServiceWorkerPlugin } = require("service-worker-webpack");
|
const { ServiceWorkerPlugin } = require("service-worker-webpack");
|
||||||
|
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
|
||||||
const banner = `
|
const banner = `
|
||||||
hash:[contentHash], chunkhash:[chunkhash], name:[name], filebase:[base], query:[query], file:[file]
|
hash:[contentHash], chunkhash:[chunkhash], name:[name], filebase:[base], query:[query], file:[file]
|
||||||
Source code: https://github.com/LemmyNet/lemmy-ui
|
Source code: https://github.com/LemmyNet/lemmy-ui
|
||||||
|
@ -19,6 +20,7 @@ const base = {
|
||||||
hashFunction: "xxhash64",
|
hashFunction: "xxhash64",
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
plugins: [new TsconfigPathsPlugin()],
|
||||||
extensions: [".js", ".jsx", ".ts", ".tsx"],
|
extensions: [".js", ".jsx", ".ts", ".tsx"],
|
||||||
},
|
},
|
||||||
performance: {
|
performance: {
|
||||||
|
|
33
yarn.lock
33
yarn.lock
|
@ -2714,7 +2714,7 @@ chalk@^2.0.0, chalk@^2.0.1:
|
||||||
escape-string-regexp "^1.0.5"
|
escape-string-regexp "^1.0.5"
|
||||||
supports-color "^5.3.0"
|
supports-color "^5.3.0"
|
||||||
|
|
||||||
chalk@^4.0.0, chalk@^4.0.2:
|
chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0:
|
||||||
version "4.1.2"
|
version "4.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||||
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
||||||
|
@ -3578,6 +3578,14 @@ enhanced-resolve@^5.14.0:
|
||||||
graceful-fs "^4.2.4"
|
graceful-fs "^4.2.4"
|
||||||
tapable "^2.2.0"
|
tapable "^2.2.0"
|
||||||
|
|
||||||
|
enhanced-resolve@^5.7.0:
|
||||||
|
version "5.15.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35"
|
||||||
|
integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==
|
||||||
|
dependencies:
|
||||||
|
graceful-fs "^4.2.4"
|
||||||
|
tapable "^2.2.0"
|
||||||
|
|
||||||
entities@^4.2.0, entities@^4.4.0:
|
entities@^4.2.0, entities@^4.4.0:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||||
|
@ -8686,6 +8694,11 @@ strip-ansi@^7.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-regex "^6.0.1"
|
ansi-regex "^6.0.1"
|
||||||
|
|
||||||
|
strip-bom@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
|
||||||
|
integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
|
||||||
|
|
||||||
strip-comments@^2.0.1:
|
strip-comments@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-2.0.1.tgz#4ad11c3fbcac177a67a40ac224ca339ca1c1ba9b"
|
resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-2.0.1.tgz#4ad11c3fbcac177a67a40ac224ca339ca1c1ba9b"
|
||||||
|
@ -8938,6 +8951,24 @@ tributejs@^5.1.3:
|
||||||
resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-5.1.3.tgz#980600fc72865be5868893078b4bfde721129eae"
|
resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-5.1.3.tgz#980600fc72865be5868893078b4bfde721129eae"
|
||||||
integrity sha512-B5CXihaVzXw+1UHhNFyAwUTMDk1EfoLP5Tj1VhD9yybZ1I8DZJEv8tZ1l0RJo0t0tk9ZhR8eG5tEsaCvRigmdQ==
|
integrity sha512-B5CXihaVzXw+1UHhNFyAwUTMDk1EfoLP5Tj1VhD9yybZ1I8DZJEv8tZ1l0RJo0t0tk9ZhR8eG5tEsaCvRigmdQ==
|
||||||
|
|
||||||
|
tsconfig-paths-webpack-plugin@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.0.1.tgz#a24651d0f69668a1abad38d3c2489855c257460d"
|
||||||
|
integrity sha512-m5//KzLoKmqu2MVix+dgLKq70MnFi8YL8sdzQZ6DblmCdfuq/y3OqvJd5vMndg2KEVCOeNz8Es4WVZhYInteLw==
|
||||||
|
dependencies:
|
||||||
|
chalk "^4.1.0"
|
||||||
|
enhanced-resolve "^5.7.0"
|
||||||
|
tsconfig-paths "^4.1.2"
|
||||||
|
|
||||||
|
tsconfig-paths@^4.1.2:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c"
|
||||||
|
integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==
|
||||||
|
dependencies:
|
||||||
|
json5 "^2.2.2"
|
||||||
|
minimist "^1.2.6"
|
||||||
|
strip-bom "^3.0.0"
|
||||||
|
|
||||||
tslib@^1.8.1:
|
tslib@^1.8.1:
|
||||||
version "1.14.1"
|
version "1.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||||
|
|
Loading…
Reference in a new issue