From 7038d6a844b18e10b81297bcfd8d95cacaf1a613 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 24 Apr 2022 14:28:07 -0500 Subject: [PATCH 1/3] Convert a bunch of files to TypeScript --- .../{base_polyfills.js => base_polyfills.ts} | Bin 1022 -> 1104 bytes app/soapbox/components/sidebar-navigation.tsx | 2 +- ...{extra_polyfills.js => extra_polyfills.ts} | Bin 147 -> 146 bytes .../steps/avatar-selection-step.tsx | 4 +- .../steps/cover-photo-selection-step.tsx | 4 +- app/soapbox/{globals.js => globals.ts} | Bin 552 -> 677 bytes app/soapbox/is_mobile.js | Bin 670 -> 0 bytes app/soapbox/is_mobile.ts | 34 ++++++++++++++ app/soapbox/{precheck.js => precheck.ts} | Bin 411 -> 671 bytes app/soapbox/reducers/{meta.js => meta.ts} | Bin 417 -> 469 bytes app/soapbox/{rtl.js => rtl.ts} | Bin 903 -> 971 bytes app/soapbox/{settings.js => settings.ts} | Bin 1008 -> 1187 bytes app/soapbox/store.ts | 2 + app/soapbox/utils/accounts.ts | 5 +- ...{favicon_service.js => favicon_service.ts} | Bin 2391 -> 2900 bytes .../utils/{greentext.js => greentext.ts} | Bin 727 -> 745 bytes app/soapbox/utils/{html.js => html.ts} | Bin 681 -> 907 bytes app/soapbox/utils/numbers.js | Bin 492 -> 0 bytes app/soapbox/utils/numbers.tsx | 19 ++++++++ app/soapbox/utils/quirks.js | Bin 522 -> 0 bytes app/soapbox/utils/quirks.ts | 38 +++++++++++++++ .../{resize_image.js => resize_image.ts} | Bin 6650 -> 7570 bytes .../{rich_content.js => rich_content.ts} | Bin 873 -> 916 bytes ...ox_prop_types.js => soapbox_prop_types.ts} | Bin app/soapbox/utils/state.js | Bin 1327 -> 0 bytes app/soapbox/utils/state.ts | 44 ++++++++++++++++++ app/soapbox/utils/static.js | Bin 265 -> 0 bytes app/soapbox/utils/static.ts | 13 ++++++ app/soapbox/utils/status.ts | 9 ++-- ...ocessor.js => tiny_post_html_processor.ts} | Bin 2902 -> 2905 bytes package.json | 2 + yarn.lock | 10 ++++ 32 files changed, 177 insertions(+), 9 deletions(-) rename app/soapbox/{base_polyfills.js => base_polyfills.ts} (87%) rename app/soapbox/{extra_polyfills.js => extra_polyfills.ts} (65%) rename app/soapbox/{globals.js => globals.ts} (57%) delete mode 100644 app/soapbox/is_mobile.js create mode 100644 app/soapbox/is_mobile.ts rename app/soapbox/{precheck.js => precheck.ts} (61%) rename app/soapbox/reducers/{meta.js => meta.ts} (75%) rename app/soapbox/{rtl.js => rtl.ts} (89%) rename app/soapbox/{settings.js => settings.ts} (73%) rename app/soapbox/utils/{favicon_service.js => favicon_service.ts} (73%) rename app/soapbox/utils/{greentext.js => greentext.ts} (92%) rename app/soapbox/utils/{html.js => html.ts} (61%) delete mode 100644 app/soapbox/utils/numbers.js create mode 100644 app/soapbox/utils/numbers.tsx delete mode 100644 app/soapbox/utils/quirks.js create mode 100644 app/soapbox/utils/quirks.ts rename app/soapbox/utils/{resize_image.js => resize_image.ts} (72%) rename app/soapbox/utils/{rich_content.js => rich_content.ts} (65%) rename app/soapbox/utils/{soapbox_prop_types.js => soapbox_prop_types.ts} (100%) delete mode 100644 app/soapbox/utils/state.js create mode 100644 app/soapbox/utils/state.ts delete mode 100644 app/soapbox/utils/static.js create mode 100644 app/soapbox/utils/static.ts rename app/soapbox/utils/{tiny_post_html_processor.js => tiny_post_html_processor.ts} (74%) diff --git a/app/soapbox/base_polyfills.js b/app/soapbox/base_polyfills.ts similarity index 87% rename from app/soapbox/base_polyfills.js rename to app/soapbox/base_polyfills.ts index e4744fee37df1a721a6bee559850410702c76f50..53146d222b5fe365a5ffd683d469ec685d99b692 100644 GIT binary patch delta 108 zcmeyzet}~`qPV`kf@sE8%;>1ezi8~xN3ossIoP3)}cJp7RU`7C%#|oza diff --git a/app/soapbox/components/sidebar-navigation.tsx b/app/soapbox/components/sidebar-navigation.tsx index bb8f47e4dd..00215c8e39 100644 --- a/app/soapbox/components/sidebar-navigation.tsx +++ b/app/soapbox/components/sidebar-navigation.tsx @@ -22,7 +22,7 @@ const SidebarNavigation = () => { const followRequestsCount = useAppSelector((state) => state.user_lists.getIn(['follow_requests', 'items'], ImmutableOrderedSet()).count()); // const dashboardCount = useAppSelector((state) => state.admin.openReports.count() + state.admin.awaitingApproval.count()); - const baseURL = account ? getBaseURL(ImmutableMap(account)) : ''; + const baseURL = account ? getBaseURL(account) : ''; const features = getFeatures(instance); const makeMenu = (): Menu => { diff --git a/app/soapbox/extra_polyfills.js b/app/soapbox/extra_polyfills.ts similarity index 65% rename from app/soapbox/extra_polyfills.js rename to app/soapbox/extra_polyfills.ts index c2da75f7cfde20532abbe7bcd8b6034588256069..47a1b6e338f3959f7b4b323df3e6f4834d8ed396 100644 GIT binary patch delta 9 QcmbQtIEisW%EVMX01!(9KmY&$ delta 9 QcmbQlIGJ%m%EV-S01!q4KL7v# diff --git a/app/soapbox/features/onboarding/steps/avatar-selection-step.tsx b/app/soapbox/features/onboarding/steps/avatar-selection-step.tsx index 3844943946..ccc000816d 100644 --- a/app/soapbox/features/onboarding/steps/avatar-selection-step.tsx +++ b/app/soapbox/features/onboarding/steps/avatar-selection-step.tsx @@ -37,7 +37,9 @@ const AvatarSelectionStep = ({ onNext }: { onNext: () => void }) => { const handleFileChange = (event: React.ChangeEvent) => { const maxPixels = 400 * 400; - const [rawFile] = event.target.files || [] as any; + const rawFile = event.target.files?.item(0); + + if (!rawFile) return; resizeImage(rawFile, maxPixels).then((file) => { const url = file ? URL.createObjectURL(file) : account?.avatar as string; diff --git a/app/soapbox/features/onboarding/steps/cover-photo-selection-step.tsx b/app/soapbox/features/onboarding/steps/cover-photo-selection-step.tsx index 079fd04a1c..1652244692 100644 --- a/app/soapbox/features/onboarding/steps/cover-photo-selection-step.tsx +++ b/app/soapbox/features/onboarding/steps/cover-photo-selection-step.tsx @@ -38,7 +38,9 @@ const CoverPhotoSelectionStep = ({ onNext }: { onNext: () => void }) => { const handleFileChange = (event: React.ChangeEvent) => { const maxPixels = 1920 * 1080; - const [rawFile] = event.target.files || [] as any; + const rawFile = event.target.files?.item(0); + + if (!rawFile) return; resizeImage(rawFile, maxPixels).then((file) => { const url = file ? URL.createObjectURL(file) : account?.header as string; diff --git a/app/soapbox/globals.js b/app/soapbox/globals.ts similarity index 57% rename from app/soapbox/globals.js rename to app/soapbox/globals.ts index 08e773695377a7da3fd9e007238ca7563a210dd6..f2c1eb35b28537c1d62cf676bab88c542ff0cd61 100644 GIT binary patch delta 174 zcmZ3%vXphgtgy`7g8ZTqg_6pGRE27V;FA2JRE1iFw4(f61@+?m#Db*!3jJb`sJb;5 zm%f&kf@4aGLNG)^Aw4HQDKV#5p(J0SBqLR!JTosPzg$m2OMl`_IX(@rHYr7kd&XFlbV<}S(|Y;yFy~ILSkO!IPPqt+=m|VapKY0e@?#WS1s%+(%c`5njllz#& E0iRD0NB{r; diff --git a/app/soapbox/is_mobile.js b/app/soapbox/is_mobile.js deleted file mode 100644 index 12591242172d1bfbbc06ebf092077fb761421e6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 670 zcmbV~%WlFj5JmU-is^=c#G_KBE=W;DB^IdAL=7xhRc<_hrNoZx2~?u|d+lHluT@vE zGM+Q{-f>tN1VdV66h=WHic)EU9hjYS^ zDQO5M!$mxweZ75-hVMx{{+Pi4o}V6V9e-;6vXU7IB|(^}`W87hw}R8U1-k%1BUM^L z{RdtKx3nnOA18@8wd|fq82=T?T1m7Y#d}>sLruvxV#P@5u63!0E9;>JjRrhyh2(16 zou(;iWQ7%$BZ0d#Q&qMWa%HJYmK%(`rCb<7i8?NwZez>$6MD7T`B?fux-|A-1s zzR8W5cxLn9`)a&vj%*xWtHwh4J)HW&8e@oQ3fpkOY{Qk}R-KxMG0y9a{+D!TkMpze Xydl5Hb6>TH+ea{t)5{*aS9kRfX=dur diff --git a/app/soapbox/is_mobile.ts b/app/soapbox/is_mobile.ts new file mode 100644 index 0000000000..fe90f9a974 --- /dev/null +++ b/app/soapbox/is_mobile.ts @@ -0,0 +1,34 @@ +'use strict'; + +import { supportsPassiveEvents } from 'detect-passive-events'; + +/** Breakpoint at which the application is considered "mobile". */ +const LAYOUT_BREAKPOINT = 630; + +/** Check if the width is small enough to be considered "mobile". */ +export function isMobile(width: number) { + return width <= LAYOUT_BREAKPOINT; +} + +/** Whether the device is iOS (best guess). */ +const iOS: boolean = /iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as any).MSStream; + +let userTouching = false; +const listenerOptions = supportsPassiveEvents ? { passive: true } as EventListenerOptions : false; + +function touchListener(): void { + userTouching = true; + window.removeEventListener('touchstart', touchListener, listenerOptions); +} + +window.addEventListener('touchstart', touchListener, listenerOptions); + +/** Whether the user has touched the screen since the page loaded. */ +export function isUserTouching(): boolean { + return userTouching; +} + +/** Whether the device is iOS (best guess). */ +export function isIOS(): boolean { + return iOS; +} diff --git a/app/soapbox/precheck.js b/app/soapbox/precheck.ts similarity index 61% rename from app/soapbox/precheck.js rename to app/soapbox/precheck.ts index 9b03a3f76b176f93f5452ef45f9420f03f1f2a61..3be1bb1b9b96f71de8816f6a8ca4a562ce8c96b4 100644 GIT binary patch delta 283 zcma)%F$%&!5JeHu!qy8+rv_B84>q=f7{PjEM>CM@hS|aBEtHfN;!WKcgkWX=|Kq(! z_u0E07xO5BeM*{=078K$0%gqzO7y_ilPDP|SyM=o?F^#%o#MFtDukv&){umuK}Dbo zd#&HFMz78|JMwUJlB0eAuOuG)OW0&2F2^6hu6NK2RgHYF` a9j*|H&LmUM=Bv&21mh4lspYCwXa5Fw6<{a; delta 31 ncmbQwI-7Yy+QgYQtjYO##U+yqnM5b+GFnVt%osS?lPL@Uv~dcX diff --git a/app/soapbox/reducers/meta.js b/app/soapbox/reducers/meta.ts similarity index 75% rename from app/soapbox/reducers/meta.js rename to app/soapbox/reducers/meta.ts index eb4f40486bde82a0ed85a47b1e254b0d62dbc0eb..cdcdaf5803a5cd40b17d2f118dfe6909c0e99311 100644 GIT binary patch delta 58 zcmZ3;e3f}ZhiYbSL4Hw*LP=#oszSAbV_v0Wa!F=>oN!mR>e1 Ku;gSO#;E{+o)q{1 delta 13 Vcmcc0ypVZ9$Ha}DlU*4n001e{1<3#a diff --git a/app/soapbox/rtl.js b/app/soapbox/rtl.ts similarity index 89% rename from app/soapbox/rtl.js rename to app/soapbox/rtl.ts index c557e3fd99a7a5a7bf438f7f615a005d6a1460f3..4c3599cb6a740fa3bf9f47dc0dad91d9c92d3bed 100644 GIT binary patch delta 86 zcmZo?Kh3^DfXPT-OH08yBQ-f&Au~;(B(jB@_-T diff --git a/app/soapbox/settings.js b/app/soapbox/settings.ts similarity index 73% rename from app/soapbox/settings.js rename to app/soapbox/settings.ts index 1acaeee912943772ccec2d0003ab910a6cbafa65..70efc0e8e0c0c615249504db24c8446f19dcd90b 100644 GIT binary patch delta 257 zcmeyszL;}@kz96arBh;Ys+B@+44H=2W86}D73Z=z~>8W5f8<-sc;$B(n delta 86 zcmZ3?`GI|c(ZmqpiDfnucZIU)D5NBoBu-{$a+sXNI2TA4iFyV5`{@-V78R#z6eX6s p0Hrmpxh5AfOH97cD8*Tnnwwvis*#y8`4Q8-$#0k=Ca1920{{tp9z6g6 diff --git a/app/soapbox/store.ts b/app/soapbox/store.ts index 5e557ac54b..170b316c99 100644 --- a/app/soapbox/store.ts +++ b/app/soapbox/store.ts @@ -17,6 +17,8 @@ export const store = createStore( ), ); +export type Store = typeof store; + // Infer the `RootState` and `AppDispatch` types from the store itself // https://redux.js.org/usage/usage-with-typescript export type RootState = ReturnType; diff --git a/app/soapbox/utils/accounts.ts b/app/soapbox/utils/accounts.ts index 2c942eeaae..2ea90bc2b2 100644 --- a/app/soapbox/utils/accounts.ts +++ b/app/soapbox/utils/accounts.ts @@ -16,10 +16,9 @@ export const getDomain = (account: Account): string => { return domain ? domain : getDomainFromURL(account); }; -export const getBaseURL = (account: ImmutableMap): string => { +export const getBaseURL = (account: Account): string => { try { - const url = account.get('url'); - return new URL(url).origin; + return new URL(account.url).origin; } catch { return ''; } diff --git a/app/soapbox/utils/favicon_service.js b/app/soapbox/utils/favicon_service.ts similarity index 73% rename from app/soapbox/utils/favicon_service.js rename to app/soapbox/utils/favicon_service.ts index d009bca601d1fe9a43527183190a2f967b97ef04..ac8a1341becd98114ed98a800f3bf5786383e0a9 100644 GIT binary patch delta 628 zcmZva!A=uV6o%7OT-2Qlw){(&m{c(#W~3}A8c3=n;7(W^dQaOEruUZKLy-V+;R{Hb z@Cd9h5qK37_r3&M@0|;#HLPYb=g$A1@0G|RsPGJv=B1NU< z6xI^E|1az8ZLL<%GHmlOEXPm>Go&MsH8>m?QU<{~m5#JY#aR#I6oEo}92rWYtbTsA z^elfM{JeYqXBTCx(om<4`un1+K3=ZiO^%ThgT_kf1PEAOL!sf|OURVk-?An19snLXJJ>3MM=LR(<(8 z7HqNSX@=AiREc_s6%{JrHOdNDKdW<36V~{8=GD!Ui;AR}K|#^*gtruQB-#r`jZcuo4|M-8t&$160VeDnMc!V|Tvq=AoGjH< delta 59 zcmV-B0L1^)7S|Gxt&@%cHIvQ(U6W1%9h0pA2$OjN4YQ*H_W_f@1R4P;llBBKlUxNH Rvwj7~0h5gf8?&wlrURu_6_fw~ diff --git a/app/soapbox/utils/greentext.js b/app/soapbox/utils/greentext.ts similarity index 92% rename from app/soapbox/utils/greentext.js rename to app/soapbox/utils/greentext.ts index f60ee7c3c6a590bcd1db051b7de56a3d0e109ce5..52a25e2be61fe6180a727aedb5f2b14aaf9e29f6 100644 GIT binary patch delta 33 icmcc4`jT}*xR^#pNp6mnLUBn^W?s4`jIlAgh6w=DQw%x) delta 15 WcmaFKdYyGbI7>!JZqCNs8YTcT_yz|6 diff --git a/app/soapbox/utils/html.js b/app/soapbox/utils/html.ts similarity index 61% rename from app/soapbox/utils/html.js rename to app/soapbox/utils/html.ts index 9eddcf876aecd6205d9c99df666948de6b03bd01..0b19ef819ccb3ce45943b8b86a9232145d4d8872 100644 GIT binary patch delta 257 zcmX}nze>bF5C(8kh>%J_EG*>fPNSIKD@717h2FiuUYIkB*_~l`5+j^XV58(I?0kuP z6CdREgtMDJFu(b}_dfRT%c8&y`<{a0ZeBe=83xiC>y-vYAPE%7t5M7T5`SIjt$)UA zW0j(qnyyfk`RDBL8c{=9o$rdT`MSbtg~?=^zfbdgt38q9o=~%sbaP=_t8Z{7ymW#k z2Q0~GmrTsq$btvNPK1NTtumfw7sqFfx^_qWTVs_q2W5%Jj+G!BHGMMvWG6>ycsodI PYpZZ6N(vIkb@saflaN`2 delta 52 zcmeBXU&%VrE0|Gp;<}@()?BsLT$2wlYO`dN zDGC4Q>w8M+aLxoN)=83tnCLhn)8%vD^SUE8IL?ST+gSJrsx>W`y3J+wKOgbzIYQ(; z7oFHDTQ+ax9k002Y*?c typeof number === 'number' && !isNaN(number); + +/** Display a number nicely for the UI, eg 1000 becomes 1K. */ +export const shortNumberFormat = (number: any): React.ReactNode => { + if (!isNumber(number)) return '•'; + + if (number < 1000) { + return ; + } else { + return K; + } +}; + +/** Check if an entity ID is an integer (eg not a FlakeId). */ +export const isIntegerId = (id: string): boolean => new RegExp(/^-?[0-9]+$/g).test(id); diff --git a/app/soapbox/utils/quirks.js b/app/soapbox/utils/quirks.js deleted file mode 100644 index c64631078a9b8c5c993e4725fb32aa3013b0ff69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 522 zcmZ{h&1%Ci5QOi3in+-KW8V|p;zJ98{52`PlpYj!`DhNxizY^mhP=mIX*3k!9r;LuVoW86UvpmLZ^1jNi zlS#T|V#9!LHcj!$SZDUF(j_)c>98SxAkxT}6EJ&E6=^R$LtQ|}-dDkAkCOabqwLNh zyYU%*27qFX)CD1X!hdVw@Q^Sd6z#>Zh@v?7iV?O^H*jKrCHATNV@G45r|+dKmG0e) z&?e4o{XtsZk!9D!JPK%&*R{LT^ku|ILO%gXF)G2w6@MwhTdbWWSxC&DvCZK)L)L1^B parseVersion(instance.version), +], (v) => { + return { + /** + * The `next` and `prev` Link headers are backwards for blocks and mutes. + * @see GET /api/v1/blocks + * @see GET /api/v1/mutes + */ + invertedPagination: v.software === PLEROMA, + + /** + * Apps are not supported by the API, and should not be created during login or registration. + * @see POST /api/v1/apps + * @see POST /oauth/token + */ + noApps: v.software === MITRA, + + /** + * There is no OAuth form available for login. + * @see GET /oauth/authorize + */ + noOAuthForm: v.software === MITRA, + }; +}); + +/** Shortcut for inverted pagination quirk. */ +export const getNextLinkName = (getState: () => RootState) => + getQuirks(getState().instance).invertedPagination ? 'prev' : 'next'; diff --git a/app/soapbox/utils/resize_image.js b/app/soapbox/utils/resize_image.ts similarity index 72% rename from app/soapbox/utils/resize_image.js rename to app/soapbox/utils/resize_image.ts index 4d0040078a91cd4aa391a2568f78f66274ac4577..268954fe7fdd5b5e0e622b20fbaa0484b10a6d2e 100644 GIT binary patch delta 1291 zcma)6O-~a+7*4AeXat0S5TeN|G3ke8>j$R-t&xBq!N{jEF&?JdX*;kx)67mk1PbvV zpnEhSp1c`j_UhGx2TeTd#UJ58PrkF;hC(qh+3a?onZD2Syz{*K=hmy4gSAP<6_rI| zQn({U-Jx=x@EUPes!VPkS{C?Mo+<(R3Oh>AsCDhwW7HzM?h$TqDck(|r?yWyu@;5I8 zHM8+JN)c9#ra;n7YN~{LFxu-b4Y0nahGC#w=b8I);IYwGr5j8etO!Cf6h#yh9Ew8v zo%i)>ZSK(ErQr;mq;sbRulBZ6;I0gQy4lrB3hU1+bj_5kqM$&FjAI*-0vlYHf|m?$ z=)yv1ALGgFQa6Lnt&TH5ofTABzXFsgE0tC7yF+#WD5S&I#eD7Q3NOV1oD>~>Un5ygx0~weQ?G%rZH8OJa}um-$%Be;KDyU zeqwsG6`M4cS%X^6k>yi-pbc+tv>0B~@mlpeeUy6~V4XIs#O$iw`lf&=Ld7ra&j4*y b1dcrhh3J9moMZR*7=AkRG-ti3@h_pjBWa!J delta 414 zcmbPa{mXblut0KtUU7*+d{R+RMk@>0tc0*dl;GmBF zGTDPYVDfBsKeqDxqU>V5$v@dmCinA7Z1(1u$~3uETx{}pF3rh`+=iQjxi2#6K+H`~ zE%D4vOiv9h%2BXY$jmD!Epf}tNkuarXpGe62fVV3yd{Z6KrMPjsl}x^C6i_ObtV__ z3rXP9H~9j8=HwH6Jd@o80;S=W`WI!U=9MItWaj4q?bXQ4O`rTlKxMLm;L*)8LJo|n zKmi?vYK8L5l#&b`g^bk9^o$Z6g?xlA9fgw00-!q;)H6Zu*DuIRSJzR=>04=oL#bbd}*>gIeAVaCn7M1`0pvr3dsz9S>WkImz6Bz%}A7f4G^ekH>_ Nd6u;5=A+VQ83CWbk#zt7 diff --git a/app/soapbox/utils/rich_content.js b/app/soapbox/utils/rich_content.ts similarity index 65% rename from app/soapbox/utils/rich_content.js rename to app/soapbox/utils/rich_content.ts index c5de1ea1e9402855e7ae63275572f57364a53c37..1fe3f5c79028f6a07d3e0dc137a7bbba1b483dff 100644 GIT binary patch delta 93 zcmaFKHiey8UrTEulRdM7mi|OXd0s08j}TuU*PPVc)Vz|33Cf&S3Q76-IjMp%Ug3PfWhQOCh^H;jH-)4s?XduLY-?5#LK#_KX zlK4Fze$IQy#^Vu;Vdjz9+YR=;OoFx91qf|Y#Y4(rjIciq8dN#`3(v!Ci~{?lbI10P(zKLWk!R(}4Q=mZSz< zV^lYZDJj#WPn?kEa9+1S!o&PD7igUkaRf7Axzv`hoi)={@P3$<%zjnr??liAnBtgB zAgLwNNSS}E(6OAk@Z6UPC#G1{S_&@ECa-NOq+Z-J)=S;{Rw;x?V>lu1t zopx1t@u~>w { + return getSoapboxConfig(state).displayFqn; +}; + +/** Whether the instance exposes instance blocks through the API. */ +export const federationRestrictionsDisclosed = (state: RootState): boolean => { + return state.instance.pleroma.hasIn(['metadata', 'federation', 'mrf_policies']); +}; + +/** + * Determine whether Soapbox FE is running in standalone mode. + * Standalone mode runs separately from any backend and can login anywhere. + */ +export const isStandalone = (state: RootState): boolean => { + const instanceFetchFailed = state.meta.instance_fetch_failed; + return isURL(BuildConfig.BACKEND_URL) ? false : (!isPrerendered && instanceFetchFailed); +}; + +const getHost = (url: any): string => { + try { + return new URL(url).origin; + } catch { + return ''; + } +}; + +/** Get the baseURL of the instance. */ +export const getBaseURL = (state: RootState): string => { + const account = state.accounts.get(state.me); + return isURL(BuildConfig.BACKEND_URL) ? BuildConfig.BACKEND_URL : getHost(account?.url); +}; diff --git a/app/soapbox/utils/static.js b/app/soapbox/utils/static.js deleted file mode 100644 index fd5dee9d82d2535a1b1a4c5de9c7b92c5bd03a74..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmZ9H$qK?i5Jd0u6?IdCBzJ=%Zs19AIe3yR6K$DHhfEI$;=h|j6!F^i>J=RYfkJ@2 zBtqE;g}0dyTZdYkM05^exGqFNM!T@3rzPj!XgE%)l<$!jGA?>!O&%#lWyQ7Nfg6*> z6GhD>dQ~FVUi_1;mgoI(wpj0$^TTF$`hp*E>dEBijBP=ewy5u&wmCTI)LWk#%C?Oc gWAwu?Y$x0R;|U%L%@g8l+mZdhts%a2te&xY0lauth5!Hn diff --git a/app/soapbox/utils/static.ts b/app/soapbox/utils/static.ts new file mode 100644 index 0000000000..79ba469316 --- /dev/null +++ b/app/soapbox/utils/static.ts @@ -0,0 +1,13 @@ +/** + * Static: functions related to static files. + * @module soapbox/utils/static + */ + +import { join } from 'path'; + +import * as BuildConfig from 'soapbox/build_config'; + +/** Gets the path to a file with build configuration being considered. */ +export const joinPublicPath = (...paths: string[]): string => { + return join(BuildConfig.FE_SUBDIRECTORY, ...paths); +}; diff --git a/app/soapbox/utils/status.ts b/app/soapbox/utils/status.ts index 7f2bfe42f6..b735fb75d9 100644 --- a/app/soapbox/utils/status.ts +++ b/app/soapbox/utils/status.ts @@ -2,7 +2,8 @@ import { isIntegerId } from 'soapbox/utils/numbers'; import type { Status as StatusEntity } from 'soapbox/types/entities'; -export const getFirstExternalLink = (status: StatusEntity) => { +/** Grab the first external link from a status. */ +export const getFirstExternalLink = (status: StatusEntity): HTMLAnchorElement | null => { try { // Pulled from Pleroma's media parser const selector = 'a:not(.mention,.hashtag,.attachment,[rel~="tag"])'; @@ -14,11 +15,13 @@ export const getFirstExternalLink = (status: StatusEntity) => { } }; -export const shouldHaveCard = (status: StatusEntity) => { +/** Whether the status is expected to have a Card after it loads. */ +export const shouldHaveCard = (status: StatusEntity): boolean => { return Boolean(getFirstExternalLink(status)); }; +/** Whether the media IDs on this status have integer IDs (opposed to FlakeIds). */ // https://gitlab.com/soapbox-pub/soapbox-fe/-/merge_requests/1087 -export const hasIntegerMediaIds = (status: StatusEntity) => { +export const hasIntegerMediaIds = (status: StatusEntity): boolean => { return status.media_attachments.some(({ id }) => isIntegerId(id)); }; diff --git a/app/soapbox/utils/tiny_post_html_processor.js b/app/soapbox/utils/tiny_post_html_processor.ts similarity index 74% rename from app/soapbox/utils/tiny_post_html_processor.js rename to app/soapbox/utils/tiny_post_html_processor.ts index 288d53c0793e60eb3214ac0662b1bd86f68183f2..5c740ced322db2c6dc9bfaf712655ec71052c806 100644 GIT binary patch delta 243 zcmca6c2jJ^QmvB8f>ecoqWt94;^O=w1zQD;jFQ|OD}~~cqRhN>O$A#!DARi4;th;? zlY<$3fTSansya-KjzR%UyA{GDO_=oLBBn)LFiv#rW)Wr?MhO@nu1cXsA+IziXR;y7 zL^dmhvi!`H&4*Yx82OPDf&?d@W-Z4Mb7m_>7u&p@O@@iPBr)ADF*j8~O-*6*WsYVh E02WtLO#lD@ delta 238 zcmca9c1>);(uo(>O>SWHspnGA0wM*6g2bZ4T!m`?q^#8Bl3In#yn@ma1zjkel30?6 ztV*M}q$o2lT~ooxZs z%*jbjQ2+|3mZcU|D&%D5r7DAsElMpZEy`1)SU5n;<*emEQkSimQFHTpHW{YP*EpJ)0B99bk^lez diff --git a/package.json b/package.json index 128e3cceae..abf5cba9fc 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,8 @@ "@types/http-link-header": "^1.0.3", "@types/jest": "^27.4.1", "@types/lodash": "^4.14.180", + "@types/object-assign": "^4.0.30", + "@types/object-fit-images": "^3.2.3", "@types/qrcode.react": "^1.0.2", "@types/react-datepicker": "^4.4.0", "@types/react-helmet": "^6.1.5", diff --git a/yarn.lock b/yarn.lock index d7cd7c4db1..94f03c4afa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2144,6 +2144,16 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== +"@types/object-assign@^4.0.30": + version "4.0.30" + resolved "https://registry.yarnpkg.com/@types/object-assign/-/object-assign-4.0.30.tgz#8949371d5a99f4381ee0f1df0a9b7a187e07e652" + integrity sha1-iUk3HVqZ9Dge4PHfCpt6GH4H5lI= + +"@types/object-fit-images@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@types/object-fit-images/-/object-fit-images-3.2.3.tgz#aa17a1cb4ac113ba81ce62f901177c9ccd5194f5" + integrity sha512-kpBPy4HIzbM1o3v+DJrK4V5NgUpcUg/ayzjixOVHQNukpdEUYDIaeDrnYJUSemQXWX5mKeEnxDRU1nACAWYnvg== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" From 57e5d81e33a5b26a4d6310790a22fb62a9b8b161 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 24 Apr 2022 14:40:14 -0500 Subject: [PATCH 2/3] Convert Redux custom middleware to TypeScript --- app/soapbox/middleware/errors.js | Bin 609 -> 0 bytes app/soapbox/middleware/errors.ts | 29 ++++++++++++++++++ .../middleware/{sounds.js => sounds.ts} | Bin 1182 -> 1543 bytes 3 files changed, 29 insertions(+) delete mode 100644 app/soapbox/middleware/errors.js create mode 100644 app/soapbox/middleware/errors.ts rename app/soapbox/middleware/{sounds.js => sounds.ts} (63%) diff --git a/app/soapbox/middleware/errors.js b/app/soapbox/middleware/errors.js deleted file mode 100644 index b5d9a36a3fda0d6d7de19345a0b102460b360350..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 609 zcma))!HR=05QgtQ#q?4m6#D?uLJJBD+l#xf$0e&HhDIYvq+P^!-$`P-?Vi?4aPsG$ zZ~jDdgVGizFs-^ABx+kIoolT$%+P4nL7b*rURj}fv*k`;;w*|P`n4cT!G(PP9uRh5 z{j&FKihXT9gl$>;SsY$o<0QKuDY3)u3-v#amiZ|^9rN<;v6!^nlxPNGaE;O9?)Gq0 zN+R!BD6H3EC)JCT7?$%i>?rFu8aa%*12%c?4VbeSd=8t%cTRw;rwBl!9d!?@y9B7k z14`(dxaw~(%*EnsD?!lDqle8bnu!dr{w8W{_$VzjWA8%`6y_xATH=LkWXU58ao55O z+*U0F(jKqY5r;dwu>*((*q^ys&?LbT=(~A2<@0k`w5iWFp(vevj{LF&<2v6g-9Ak< FM?ctC%cuYV diff --git a/app/soapbox/middleware/errors.ts b/app/soapbox/middleware/errors.ts new file mode 100644 index 0000000000..b87c502498 --- /dev/null +++ b/app/soapbox/middleware/errors.ts @@ -0,0 +1,29 @@ +import { showAlertForError } from '../actions/alerts'; + +import type { AnyAction } from 'redux'; +import type { ThunkMiddleware } from 'redux-thunk'; + +/** Whether the action is considered a failure. */ +const isFailType = (type: string): boolean => type.endsWith('_FAIL'); + +/** Whether the action is a failure to fetch from browser storage. */ +const isRememberFailType = (type: string): boolean => type.endsWith('_REMEMBER_FAIL'); + +/** Whether the error contains an Axios response. */ +const hasResponse = (error: any): boolean => Boolean(error && error.response); + +/** Whether the error should be shown to the user. */ +const shouldShowError = ({ type, skipAlert, error }: AnyAction): boolean => { + return !skipAlert && hasResponse(error) && isFailType(type) && !isRememberFailType(type); +}; + +/** Middleware to display Redux errors to the user. */ +export default function errorsMiddleware(): ThunkMiddleware { + return ({ dispatch }) => next => action => { + if (shouldShowError(action)) { + dispatch(showAlertForError(action.error)); + } + + return next(action); + }; +} diff --git a/app/soapbox/middleware/sounds.js b/app/soapbox/middleware/sounds.ts similarity index 63% rename from app/soapbox/middleware/sounds.js rename to app/soapbox/middleware/sounds.ts index 6950e76188ec8532fa02830e85c7c3f0398f8577..94ba153131e3d784bb06756282eacbecad21dc01 100644 GIT binary patch delta 496 zcmZXRzfQw25XKe6Lc~KHFa*k=w2EkY@xsA@lP-cbF7VNA*n8SEd=Ffd8M2fFW zBUJFHRR)npQBIGoSN%}C)q;^?#e`C0`m~SXdaVa{&eJ? z3*YcxsFtvSR-)txy>b8cY=tCeLnOg0BRW^PvA{Jli&tcvz`pCz98K(2a<+H(2QhI- z@1~exZXxN=GF-i%es}%)gOtrycI2^!FTCWqbXk+-@GnY=dN=sG@uao5tO6P)q5+wM zMpL+*5uC(PTjpv*A0{wvrBl3b{SzGa;zsyBz2(8gyJmdik{cpdT3q-abcr@D?R@TS M{06~gy|Y>U0g9!jEC2ui delta 124 zcmZqYna8Q6URs>0P+U@!nOvf7&Bc|RpI2O>kX)3SSd!{knv$8XV5?A^Us{x$T0HT~ zGa-m{K~7>NP&^SNKUtpX&}0o}Em4JPE(HaMJWxYkigRLeM(XBS% Date: Sun, 24 Apr 2022 17:05:11 -0500 Subject: [PATCH 3/3] SidebarNavigation: remove unused ImmutableMap import --- app/soapbox/components/sidebar-navigation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/soapbox/components/sidebar-navigation.tsx b/app/soapbox/components/sidebar-navigation.tsx index 00215c8e39..132b439df2 100644 --- a/app/soapbox/components/sidebar-navigation.tsx +++ b/app/soapbox/components/sidebar-navigation.tsx @@ -1,4 +1,4 @@ -import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable'; +import { OrderedSet as ImmutableOrderedSet } from 'immutable'; import React from 'react'; import { FormattedMessage } from 'react-intl';