From 388d8875831d3a8276c44507d9626751674a7622 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 4 Jul 2022 12:53:25 -0500 Subject: [PATCH 1/4] Upgrade axios to v1.0.0-alpha.1, remove custom toFormData implementation --- app/soapbox/actions/me.ts | 6 +++++- app/soapbox/features/edit_profile/index.tsx | 22 +------------------ package.json | 2 +- yarn.lock | 24 +++++++++++++-------- 4 files changed, 22 insertions(+), 32 deletions(-) diff --git a/app/soapbox/actions/me.ts b/app/soapbox/actions/me.ts index 4cb4b2350..f6a79ab85 100644 --- a/app/soapbox/actions/me.ts +++ b/app/soapbox/actions/me.ts @@ -67,7 +67,11 @@ const patchMe = (params: Record) => dispatch(patchMeRequest()); return api(getState) - .patch('/api/v1/accounts/update_credentials', params) + .patch('/api/v1/accounts/update_credentials', params, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }) .then(response => { persistAuthAccount(response.data, params); dispatch(patchMeSuccess(response.data)); diff --git a/app/soapbox/features/edit_profile/index.tsx b/app/soapbox/features/edit_profile/index.tsx index e32473505..1eaa74e62 100644 --- a/app/soapbox/features/edit_profile/index.tsx +++ b/app/soapbox/features/edit_profile/index.tsx @@ -25,25 +25,6 @@ const hidesNetwork = (account: Account): boolean => { return Boolean(hide_followers && hide_follows && hide_followers_count && hide_follows_count); }; -/** Converts JSON objects to FormData. */ -// https://stackoverflow.com/a/60286175/8811886 -// @ts-ignore -const toFormData = (f => f(f))(h => f => f(x => h(h)(f)(x)))(f => fd => pk => d => { - if (d instanceof Object) { - // eslint-disable-next-line consistent-return - Object.keys(d).forEach(k => { - const v = d[k]; - if (pk) k = `${pk}[${k}]`; - if (v instanceof Object && !(v instanceof Date) && !(v instanceof File)) { - return f(fd)(k)(v); - } else { - fd.append(k, v); - } - }); - } - return fd; -})(new FormData())(); - const messages = defineMessages({ heading: { id: 'column.edit_profile', defaultMessage: 'Edit profile' }, header: { id: 'edit_profile.header', defaultMessage: 'Edit Profile' }, @@ -205,9 +186,8 @@ const EditProfile: React.FC = () => { const handleSubmit: React.FormEventHandler = (event) => { const promises = []; - const formData = toFormData(data); - promises.push(dispatch(patchMe(formData))); + promises.push(dispatch(patchMe(data))); if (features.muteStrangers) { promises.push( diff --git a/package.json b/package.json index 47098b26b..8650f051c 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "@types/uuid": "^8.3.4", "array-includes": "^3.1.5", "autoprefixer": "^10.4.2", - "axios": "^0.27.2", + "axios": "^1.0.0-alpha.1", "axios-mock-adapter": "^1.21.1", "babel-loader": "^8.2.5", "babel-plugin-lodash": "^3.3.4", diff --git a/yarn.lock b/yarn.lock index 5db73d9ab..2fc8466ee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3506,13 +3506,14 @@ axios-mock-adapter@^1.21.1: fast-deep-equal "^3.1.3" is-buffer "^2.0.5" -axios@^0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" - integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== +axios@^1.0.0-alpha.1: + version "1.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.0.0-alpha.1.tgz#ce69c17ca7605d01787ca754dd906e6fccdf71ee" + integrity sha512-p+meG161943WT+K7sJYquHR46xxi/z0tk7vnSmEf/LrfEAyiP+0uTMMYk1OEo1IRF18oGRhnFxN1y8fLcXaTMw== dependencies: - follow-redirects "^1.14.9" + follow-redirects "^1.15.0" form-data "^4.0.0" + proxy-from-env "^1.1.0" axobject-query@^2.2.0: version "2.2.0" @@ -5827,10 +5828,10 @@ follow-redirects@^1.0.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== -follow-redirects@^1.14.9: - version "1.14.9" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" - integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== +follow-redirects@^1.15.0: + version "1.15.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== foreach@^2.0.5: version "2.0.5" @@ -9501,6 +9502,11 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" From d168302e7201366515643f9f6c8007053fe310ff Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 4 Jul 2022 13:17:01 -0500 Subject: [PATCH 2/4] EditProfile: reenable birthdays with TextInput (for now) --- app/soapbox/features/edit_profile/index.tsx | 25 ++++++++++++++------- app/soapbox/utils/features.ts | 4 +--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/app/soapbox/features/edit_profile/index.tsx b/app/soapbox/features/edit_profile/index.tsx index 1eaa74e62..9d5c3b622 100644 --- a/app/soapbox/features/edit_profile/index.tsx +++ b/app/soapbox/features/edit_profile/index.tsx @@ -4,9 +4,19 @@ import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { updateNotificationSettings } from 'soapbox/actions/accounts'; import { patchMe } from 'soapbox/actions/me'; import snackbar from 'soapbox/actions/snackbar'; -import BirthdayInput from 'soapbox/components/birthday_input'; import List, { ListItem } from 'soapbox/components/list'; -import { Button, Column, Form, FormActions, FormGroup, Input, Textarea, HStack, Toggle, FileInput } from 'soapbox/components/ui'; +import { + Button, + Column, + FileInput, + Form, + FormActions, + FormGroup, + HStack, + Input, + Textarea, + Toggle, +} from 'soapbox/components/ui'; import Streamfield, { StreamfieldComponent } from 'soapbox/components/ui/streamfield/streamfield'; import { useAppSelector, useAppDispatch, useOwnAccount, useFeatures } from 'soapbox/hooks'; import { normalizeAccount } from 'soapbox/normalizers'; @@ -222,10 +232,6 @@ const EditProfile: React.FC = () => { }; }; - const handleBirthdayChange = (date: string) => { - updateData('birthday', date); - }; - const handleHideNetworkChange: React.ChangeEventHandler = e => { const hide = e.target.checked; @@ -309,9 +315,12 @@ const EditProfile: React.FC = () => { } > - )} diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index 6f4a77ddf..206eaf067 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -148,9 +148,7 @@ const getInstanceFeatures = (instance: Instance) => { * @see POST /api/v1/accounts * @see PATCH /api/v1/accounts/update_credentials */ - // birthdays: v.software === PLEROMA && gte(v.version, '2.4.50'), - // FIXME: temporarily disabled until they can be deleted on the backend. - birthdays: false, + birthdays: v.software === PLEROMA && v.build === SOAPBOX && gte(v.version, '2.4.50'), /** Whether people who blocked you are visible through the API. */ blockersVisible: features.includes('blockers_visible'), From cef4b16a6966e01d98601d40027e319d0f022f09 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 4 Jul 2022 13:26:06 -0500 Subject: [PATCH 3/4] Account normalizer: use '' as default birthday --- app/soapbox/normalizers/__tests__/account.test.ts | 7 +++++++ app/soapbox/normalizers/account.ts | 9 ++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/soapbox/normalizers/__tests__/account.test.ts b/app/soapbox/normalizers/__tests__/account.test.ts index b78017e06..761b2fac5 100644 --- a/app/soapbox/normalizers/__tests__/account.test.ts +++ b/app/soapbox/normalizers/__tests__/account.test.ts @@ -47,6 +47,13 @@ describe('normalizeAccount()', () => { expect(result.birthday).toEqual('1993-07-03'); }); + it('normalizes undefined birthday to empty string', () => { + const account = require('soapbox/__fixtures__/mastodon-account.json'); + const result = normalizeAccount(account); + + expect(result.birthday).toEqual(''); + }); + it('normalizes Pleroma legacy fields', () => { const account = require('soapbox/__fixtures__/pleroma-2.2.2-account.json'); const result = normalizeAccount(account); diff --git a/app/soapbox/normalizers/account.ts b/app/soapbox/normalizers/account.ts index 784ba08aa..1a519b8a8 100644 --- a/app/soapbox/normalizers/account.ts +++ b/app/soapbox/normalizers/account.ts @@ -24,7 +24,7 @@ export const AccountRecord = ImmutableRecord({ acct: '', avatar: '', avatar_static: '', - birthday: undefined as string | undefined, + birthday: '', bot: false, created_at: new Date(), discoverable: false, @@ -261,6 +261,12 @@ const normalizeDiscoverable = (account: ImmutableMap) => { return account.set('discoverable', discoverable); }; +/** Normalize undefined/null birthday to empty string. */ +const fixBirthday = (account: ImmutableMap) => { + const birthday = account.get('birthday'); + return account.set('birthday', birthday || ''); +}; + export const normalizeAccount = (account: Record) => { return AccountRecord( ImmutableMap(fromJS(account)).withMutations(account => { @@ -280,6 +286,7 @@ export const normalizeAccount = (account: Record) => { addStaffFields(account); fixUsername(account); fixDisplayName(account); + fixBirthday(account); addInternalFields(account); }), ); From 9ce9a3f951d26f6d3f6315f068a4b477ed386d86 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 5 Jul 2022 21:04:42 -0500 Subject: [PATCH 4/4] patchMe(): use multipart/form-data only optionally --- app/soapbox/actions/me.ts | 14 +++++++------- app/soapbox/features/edit_profile/index.tsx | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/soapbox/actions/me.ts b/app/soapbox/actions/me.ts index f6a79ab85..074f5d4cc 100644 --- a/app/soapbox/actions/me.ts +++ b/app/soapbox/actions/me.ts @@ -6,7 +6,7 @@ import api from '../api'; import { loadCredentials } from './auth'; import { importFetchedAccount } from './importer'; -import type { AxiosError } from 'axios'; +import type { AxiosError, AxiosRequestHeaders } from 'axios'; import type { AppDispatch, RootState } from 'soapbox/store'; import type { APIEntity } from 'soapbox/types/entities'; @@ -62,16 +62,16 @@ const persistAuthAccount = (account: APIEntity, params: Record) => } }; -const patchMe = (params: Record) => +const patchMe = (params: Record, isFormData = false) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(patchMeRequest()); + const headers: AxiosRequestHeaders = isFormData ? { + 'Content-Type': 'multipart/form-data', + } : {}; + return api(getState) - .patch('/api/v1/accounts/update_credentials', params, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - }) + .patch('/api/v1/accounts/update_credentials', params, { headers }) .then(response => { persistAuthAccount(response.data, params); dispatch(patchMeSuccess(response.data)); diff --git a/app/soapbox/features/edit_profile/index.tsx b/app/soapbox/features/edit_profile/index.tsx index 9d5c3b622..a2fc13b92 100644 --- a/app/soapbox/features/edit_profile/index.tsx +++ b/app/soapbox/features/edit_profile/index.tsx @@ -197,7 +197,7 @@ const EditProfile: React.FC = () => { const handleSubmit: React.FormEventHandler = (event) => { const promises = []; - promises.push(dispatch(patchMe(data))); + promises.push(dispatch(patchMe(data, true))); if (features.muteStrangers) { promises.push(