From 48290ab5404ae8eadad214f533750f2c78fb1da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 19 May 2022 15:22:15 +0200 Subject: [PATCH 1/2] Convert import/export data to TypeScript, use FileInput component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/actions/export_data.js | Bin 3759 -> 0 bytes app/soapbox/actions/export_data.ts | 113 ++++++++++++++++++ .../{import_data.js => import_data.ts} | Bin 2472 -> 3028 bytes app/soapbox/actions/snackbar.js | Bin 603 -> 0 bytes app/soapbox/actions/snackbar.ts | 39 ++++++ app/soapbox/components/ui/form/form.tsx | 2 + .../export_data/components/csv_exporter.js | Bin 1384 -> 0 bytes .../export_data/components/csv_exporter.tsx | 47 ++++++++ .../export_data/{index.js => index.tsx} | Bin 2339 -> 1862 bytes .../import_data/components/csv_importer.js | Bin 1919 -> 0 bytes .../import_data/components/csv_importer.tsx | 66 ++++++++++ .../import_data/{index.js => index.tsx} | Bin 2355 -> 2045 bytes 12 files changed, 267 insertions(+) delete mode 100644 app/soapbox/actions/export_data.js create mode 100644 app/soapbox/actions/export_data.ts rename app/soapbox/actions/{import_data.js => import_data.ts} (65%) delete mode 100644 app/soapbox/actions/snackbar.js create mode 100644 app/soapbox/actions/snackbar.ts delete mode 100644 app/soapbox/features/export_data/components/csv_exporter.js create mode 100644 app/soapbox/features/export_data/components/csv_exporter.tsx rename app/soapbox/features/export_data/{index.js => index.tsx} (60%) delete mode 100644 app/soapbox/features/import_data/components/csv_importer.js create mode 100644 app/soapbox/features/import_data/components/csv_importer.tsx rename app/soapbox/features/import_data/{index.js => index.tsx} (61%) diff --git a/app/soapbox/actions/export_data.js b/app/soapbox/actions/export_data.js deleted file mode 100644 index 12e0bd58b35e2b5a8e188ae3afca3e6630bce18a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3759 zcmb_e+iv4F5Pi>A%tHZ5STEfIeJB!m3vcQGizdxBPOt^GMV6MRn2kiLBo)U9{O=uJ zBununZkoI};@r;69L|uJl~e|vAY(Zf?1E`c@0f-aZG+eoq-=OPdPdy% z=?Gv}$n;)M>oi5RW8@vrjv?|4xy`7dM0*+Wn$f^|p><&b|55v+^8wz7We_a#x+sAHXk`6k;d%x{Gcz_u z@fo6vqq$-^VvlT}jtzS>!&E<#Cqjd;J9lLi)lw#hJ{OgjN+3+3B1YS=>)<83!@Z zZKa$W#Y*|WcDPw}I#Y0M#%U^RVRWnw*4Xz$F;go6R4+veI9RWk#?oj8qYtiMZO0tn zxVY1pR7a24qTBQU3TA+Y5UBL^ul2FOrr=RC%;S!OgkbkH!BvvQ1x|V!*#6>i0i%(v zAR1d5*cb~e7=zdzdHnXUw(<9?~Qj)ftdl@!$sF%@6^Mm7+^hDQToxR*Y1& zp4nb?rs<-+qYb*e>fH9b!vhYSQ_@_H63u(lVkE#!p}qKHjx_?QF2QH3Qf) zzq=h@hU2byIH zH_c!=mkXFlsf}(5;Y^X`VUBiU{w=wPqd!HI;IQ)@yzPi_=!I!%UEsh8J&c>zRkOof zzez4tNt^W*H|r6P`ztE(v+ RootState) => async(apiResponse: AxiosResponse) => { + const followings = apiResponse.data; + let accounts = []; + let next = getLinks(apiResponse).refs.find(link => link.rel === 'next'); + while (next) { + apiResponse = await api(getState).get(next.uri); + next = getLinks(apiResponse).refs.find(link => link.rel === 'next'); + Array.prototype.push.apply(followings, apiResponse.data); + } + + accounts = followings.map((account: { fqn: string }) => account.fqn); + return Array.from(new Set(accounts)); +}; + +export const exportFollows = () => (dispatch: React.Dispatch, getState: () => RootState) => { + dispatch({ type: EXPORT_FOLLOWS_REQUEST }); + const me = getState().me; + return api(getState) + .get(`/api/v1/accounts/${me}/following?limit=40`) + .then(listAccounts(getState)) + .then((followings) => { + followings = followings.map(fqn => fqn + ',true'); + followings.unshift('Account address,Show boosts'); + fileExport(followings.join('\n'), 'export_followings.csv'); + + dispatch(snackbar.success(messages.followersSuccess)); + dispatch({ type: EXPORT_FOLLOWS_SUCCESS }); + }).catch(error => { + dispatch({ type: EXPORT_FOLLOWS_FAIL, error }); + }); +}; + +export const exportBlocks = () => (dispatch: React.Dispatch, getState: () => RootState) => { + dispatch({ type: EXPORT_BLOCKS_REQUEST }); + return api(getState) + .get('/api/v1/blocks?limit=40') + .then(listAccounts(getState)) + .then((blocks) => { + fileExport(blocks.join('\n'), 'export_block.csv'); + + dispatch(snackbar.success(messages.blocksSuccess)); + dispatch({ type: EXPORT_BLOCKS_SUCCESS }); + }).catch(error => { + dispatch({ type: EXPORT_BLOCKS_FAIL, error }); + }); +}; + +export const exportMutes = () => (dispatch: React.Dispatch, getState: () => RootState) => { + dispatch({ type: EXPORT_MUTES_REQUEST }); + return api(getState) + .get('/api/v1/mutes?limit=40') + .then(listAccounts(getState)) + .then((mutes) => { + fileExport(mutes.join('\n'), 'export_mutes.csv'); + + dispatch(snackbar.success(messages.mutesSuccess)); + dispatch({ type: EXPORT_MUTES_SUCCESS }); + }).catch(error => { + dispatch({ type: EXPORT_MUTES_FAIL, error }); + }); +}; diff --git a/app/soapbox/actions/import_data.js b/app/soapbox/actions/import_data.ts similarity index 65% rename from app/soapbox/actions/import_data.js rename to app/soapbox/actions/import_data.ts index 7bde21a4abe0e8e2b1157875838dcd7328bcb722..43de9f85c69580daefe331a8b615f0fee429bdb3 100644 GIT binary patch literal 3028 zcmd5--*4J55Pr{Jac>C{3-8(J)KZYpKy0LFkH`dvMor?#Hd+zmf1e#YS=_`~nS@jx z5azzS`|fv=6@+p)!yd;;j+dA*bifQQ5L1$YOEHT0rzGd8``K|4g=RU5UUrCT3?r!6 zk=KzxCM0L0O5D&uMWVsJKN63Ih@T29{D<1+eQBij?>DV!_gZ632oE{pxCU257$+1< z7x=Y|E)uLb%$MI+>!(Ul-SrmELM|mUF1!H#kU5vshfg+?KW|(-N$>md75_7HvYHxRhTwunAT;eBge1`+HVbM8*c~*=(xGl--YU0S z|C_;~(8iP!`ZWNQpX5_aT$~)ln{k@t2j?R4V@|l^sHx1VjWd3;f;^e~T}q-C79OKW zzyh%*l6?TK+5_9YD3#BY%vVTvD6ODl%Jn7_=!enRayF|h_4><>9KXv~3GDK-loon?Tn2B~pNe1cldbI)h@wm+bw+3=yM6%UJCO@- zpRfY)=sAG3T>k!}et&5V$lVYQSnc2dJgE=$QZ8SzgaDO6@2qF3d*6cOEb$ccBh5ja z39m9gC%z&mltbf z=9T2=C=?_XCFT}uDpYeRC={iZlosVFXryEo7bKP>XXq%Tr%n#z(41VuHd_~@TrVxZ zC^xahH?_DpF+Eiy7eW^Ug+WH978M7VCMN?0H76UeZ*Z&S;!3Rm*{Fc#0;in(?}APG~)P@>lSfHCmQ z2qja#!=g)sJVh{`KzL^Wrat57gBIJjR&B#i{X4ZCTwTXwP4u2AucU{9khu!mUSFNs z#82X;AT$+8ZzK$yF8IMi!kTUo9aL9YX3h8EzNEd;NTjF_*bD!~?lI?8VNb=b&uz?q R$kV(FeNX>pk0WMH!XAm+%rXD~ diff --git a/app/soapbox/actions/snackbar.ts b/app/soapbox/actions/snackbar.ts new file mode 100644 index 0000000000..d1cda0d943 --- /dev/null +++ b/app/soapbox/actions/snackbar.ts @@ -0,0 +1,39 @@ +import { ALERT_SHOW } from './alerts'; + +import type { MessageDescriptor } from 'react-intl'; + +type SnackbarActionSeverity = 'info' | 'success' | 'error' + +type SnackbarMessage = string | MessageDescriptor + +export type SnackbarAction = { + type: typeof ALERT_SHOW + message: SnackbarMessage + actionLabel?: string + actionLink?: string + severity: SnackbarActionSeverity +} + +export const show = (severity: SnackbarActionSeverity, message: SnackbarMessage, actionLabel?: string, actionLink?: string): SnackbarAction => ({ + type: ALERT_SHOW, + message, + actionLabel, + actionLink, + severity, +}); + +export const info = (message: SnackbarMessage, actionLabel?: string, actionLink?: string) => + show('info', message, actionLabel, actionLink); + +export const success = (message: SnackbarMessage, actionLabel?: string, actionLink?: string) => + show('success', message, actionLabel, actionLink); + +export const error = (message: SnackbarMessage, actionLabel?: string, actionLink?: string) => + show('error', message, actionLabel, actionLink); + +export default { + info, + success, + error, + show, +}; diff --git a/app/soapbox/components/ui/form/form.tsx b/app/soapbox/components/ui/form/form.tsx index 59ca180b1f..3b02634fac 100644 --- a/app/soapbox/components/ui/form/form.tsx +++ b/app/soapbox/components/ui/form/form.tsx @@ -5,6 +5,8 @@ interface IForm { onSubmit?: (event: React.FormEvent) => void, /** Class name override for the
element. */ className?: string, + /** Prevent the form from being submitted. */ + disabled?: boolean, } /** Form element with custom styles. */ diff --git a/app/soapbox/features/export_data/components/csv_exporter.js b/app/soapbox/features/export_data/components/csv_exporter.js deleted file mode 100644 index bd47b83fbd0bbe98d984c642af535c0538992dda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1384 zcmbVMU2mK)5PauX>>D6)@SYq|(o-u%YNaTN`dS&-JKTaz{n1{E`0rgCI7sgrReeEb zJ)WH%5Ae2=%D{t?-SbO_8um)I5O(ywF|H6^2YkdlA}h_buiSdsx^G)+Skd5vRk)Lc z7bsp~H++U{I>Kzq8MKh}z{OWA&Al)U4C_W4PNg-lltK{l50t{n9tk!J?%|y^Mv4SJ zNY#$dyV7tebOO(KT+VCBxThvo<6_# za8y9#C@KwqF{Td!0BxAzB{-``>vQO-3_#YB#;fI0(!CXB%Jn0Dx16R-Jgh~n*#Y$$ zC=2J{8MNZMW2UUv(2JbT=r!mf-$2HU@)2guTz{0T;^J@xd)8=l5(fvXnW!4vHN5;r z;0S+EFk;AWrdi2QZJ>u)VKPmhP#Am+axgX5sdGvDzAzRvnkO<4BZ|$^+bD}L#Mj=S znKK#9w;UEG&fD)_K#{tNT)*o5#C`Jpy>SjeUdZ+h1)-P0LnK8=}* z=GG|}oi$$?R$vnk+sqN2Gn1n&QPV#F|BbJvmJ8iU_#}P$M%R64XZ)7w@_nyP@s_EV z5Tx+g%6lJ^RoaHv=K (dispatch: AppDispatch, getState: any) => Promise, +} + +const CSVExporter: React.FC = ({ messages, action }) => { + const dispatch = useAppDispatch(); + const intl = useIntl(); + + const [isLoading, setIsLoading] = useState(false); + + const handleClick: React.MouseEventHandler = (event) => { + setIsLoading(true); + dispatch(action()).then(() => { + setIsLoading(false); + }).catch(() => { + setIsLoading(false); + }); + }; + + return ( + + {intl.formatMessage(messages.input_label)} + {intl.formatMessage(messages.input_hint)} + + + + + + ); +}; + +export default CSVExporter; diff --git a/app/soapbox/features/export_data/index.js b/app/soapbox/features/export_data/index.tsx similarity index 60% rename from app/soapbox/features/export_data/index.js rename to app/soapbox/features/export_data/index.tsx index 4a73f700b6108611a0db491e587cfc10fb739cae..3a00b6051be1966c7663ba06a0f9c7f630854fc0 100644 GIT binary patch delta 176 zcmZ21bc}DJiqgbxA*s^hRL{JU9EDniw4(f61@)rT#N-m)OrX%jhsm3(7+G1ZT`LOm zi%MJ)OA-}q6*M#zZ0!`PxfB$V^Ye;J6hJD0!cb#0G_ARSQbnmHrA2uP8eEg_ut!a9 zTU ADF6Tf delta 636 zcmZ{h&q~8U5XMgy*@Fi!f>0PlNFx1n@K&u9S_Lmsv?njJO((jN?8fW_BBl5WmOg@K z1z*B9@Iib7XJhJLJT0^PoBh7|=4I_+eOo1|$q0s-Nk_LST8J`}fR}RJBzNKMiTkb2 zagyXD$1x7`4Eu(gG-|5g3}r}7wM3dJGtIDq<{R~a($^T$v8EViKYRF!1u7F$2#wZE z{-QFR)21$8kvqw(auT5wjEW^&rho>cZp2TND_{Wo6|xUtSg~= z0RB_}Ea47(``9m;7T@@u(p=~ey&!0aS+FMr7G-V|jAR}Y^eX56FR4KEl)hU|FTz+_ z3x{RzK#~M32=&CmKlx4tyK_EN2yP`GE9!zX4WKx^&e!T%X6%Go%AVp)t~j+uiA5Jx z7Jpd>%y=4rjBQ}%2i4a+jwP6u5}a@s=t|j^vtUcyHE{eIeC}Pmt(-4jYxPA`Yn10> ZZHLePCcu8XZ{j@B2c;7Zj`f6?iFfpI+u#5I diff --git a/app/soapbox/features/import_data/components/csv_importer.js b/app/soapbox/features/import_data/components/csv_importer.js deleted file mode 100644 index ff17cade40b579ccceedceb6d304215711039e65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1919 zcmbVNQBT`25Pt8kxVI!yo4l6>v4N0~wg~~+3qr_EuB}I6XSM@nA^&}6J82TuO=u5o z6Z`Ysci(ru^0Jc3z_pUq-D`y!HcFNdRP??ub|E;KagVt{mYHr}+2>*Fsw`{6)&*YI z3eP3s1&RrF!F$M}BFrM|K|_!RxcGsYxe}&;cARLzX=x11q!5IBhfT~G)*jn2kr#MduS;&|bcoN?DG{WnUGb!_Hqby%dEUt}r|xilk%HNA9b4J7BLd*h zR7|l_Q%r#`&hZOoSQwo+vHWJts)~FGgMw=l%zY&51ID%h!aA@^pwZmgosjx+D8{H7 zq3we997FF|Xc^AIr;4K3>`;W^s5a;l9*yi|I)rYcIJ1t0Au1)+{|4y_aw3yt?)Sns zvsY&1$t5XpPWEiAtMr*Va&HBCusg9~YKz7xrs3@k?jO9y?`5+mx`JF9Mbe`pL42F{ zPJ7k*Zl*hZ#9@V|Rsuq255US^Xpq9UQQEl7J zOoM$9zzg#2&ZNOw7I^@MZPTD&Yb=7(#xjjJl$x2Z{_=%A?U;+IHjfS&wX4Ji9p%XR z^YBJF9r{4QUV1k}zOj;67&^OH7c6^%oT;a!dw3c1I#}p*^X2_qF6GK%o*Oz z?nzER;lWFx-BcmdJcmm0lBw6= (dispatch: AppDispatch, getState: any) => Promise, +} + +const CSVImporter: React.FC = ({ messages, action }) => { + const dispatch = useAppDispatch(); + const intl = useIntl(); + + const [isLoading, setIsLoading] = useState(false); + const [file, setFile] = useState(null); + + + const handleSubmit: React.FormEventHandler = (event) => { + const params = new FormData(); + params.append('list', file!); + + setIsLoading(true); + dispatch(action(params)).then(() => { + setIsLoading(false); + }).catch(() => { + setIsLoading(false); + }); + + event.preventDefault(); + }; + + const handleFileChange: React.ChangeEventHandler = e => { + const file = e.target.files?.item(0); + setFile(file); + }; + + return ( +
+ {intl.formatMessage(messages.input_label)} + {intl.formatMessage(messages.input_hint)}} + > + + + + + +
+ ); +}; + +export default CSVImporter; diff --git a/app/soapbox/features/import_data/index.js b/app/soapbox/features/import_data/index.tsx similarity index 61% rename from app/soapbox/features/import_data/index.js rename to app/soapbox/features/import_data/index.tsx index 6a3a267e292c15dfd562b21ab01196ad0e4fb474..b03b20f292f296a7d0a9549934e8d3fd7e9b71d9 100644 GIT binary patch delta 285 zcmdli^p}64iqgbxA*s^hRL{JU9EDniw4(f61@)rT#N-m)OrX%jhdC-hS;vBc;MAPd z#ex}fqP(i delta 590 zcmZvZ!AiqG5QYz;g}n$~1fej9kOY!=Z%rw*D7{G09=v$irc+%>cH`~_Dy8%lTKXj7 zTlo%xvoWn$JS{W)v;RNed>wzBT!_3-+QLw)V)Ry^fvHwGcm>swbq%l0R_Cojo|l%7 zGaQy0dy0l6N?VIH^3+CRD~SryL}@KrpEmnKzF=wx(q^#uJ3}ops6)&lRZ^02Po;5E z&epft`zIVOb6(tAZgHesW&;TrS3=SOf6f4=$SwJs7CfQ7t_#1?#MJRUA*qlldO;Af zMbKspXBC49PI;MG=+-IxJ*$9qgS(Fpk`j^$5Q974Hq~P%W z;7-7W1`tG|^?Qa`jh#^}%pJa#f(V3_Y$|r^?M_x}onS%R0BDMV+7IflX&qx>URi`Y zKEyZy`z%cC!kB8%jS@TwKK-&;9X)KKMsst}2Wo0fqRUX1eu8b|% EH%Xhw=>Px# From 86c17682e9827665bf409418fe2cb60e410ad7f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 19 May 2022 16:46:32 +0200 Subject: [PATCH 2/2] forms can't be disabled, actually MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/components/ui/form/form.tsx | 2 -- app/soapbox/features/import_data/components/csv_importer.tsx | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/soapbox/components/ui/form/form.tsx b/app/soapbox/components/ui/form/form.tsx index 3b02634fac..59ca180b1f 100644 --- a/app/soapbox/components/ui/form/form.tsx +++ b/app/soapbox/components/ui/form/form.tsx @@ -5,8 +5,6 @@ interface IForm { onSubmit?: (event: React.FormEvent) => void, /** Class name override for the
element. */ className?: string, - /** Prevent the form from being submitted. */ - disabled?: boolean, } /** Form element with custom styles. */ diff --git a/app/soapbox/features/import_data/components/csv_importer.tsx b/app/soapbox/features/import_data/components/csv_importer.tsx index 95ae99aba2..aa5937af90 100644 --- a/app/soapbox/features/import_data/components/csv_importer.tsx +++ b/app/soapbox/features/import_data/components/csv_importer.tsx @@ -43,7 +43,7 @@ const CSVImporter: React.FC = ({ messages, action }) => { }; return ( - + {intl.formatMessage(messages.input_label)} {intl.formatMessage(messages.input_hint)}}