From 50dc9df8a44d408dd83ae4b17c407fa36c85cf8e Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 14 Nov 2019 00:18:14 +0200 Subject: adds greentext, also small fixes --- .../tiny_post_html_processor.service.js | 84 ++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/services/tiny_post_html_processor/tiny_post_html_processor.service.js (limited to 'src/services') diff --git a/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js b/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js new file mode 100644 index 00000000..c9ff81e1 --- /dev/null +++ b/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js @@ -0,0 +1,84 @@ +/** + * This is a tiny purpose-built HTML parser/processor. This basically detects any type of visual newline and + * allows it to be processed, useful for greentexting, mostly + * + * @param {Object} input - input data + * @param {(string) => string} processor - function that will be called on every line + * @return {string} processed html + */ +export const processHtml = (html, processor) => { + const handledTags = new Set(['p', 'br', 'div']) + const openCloseTags = new Set(['p', 'div']) + const tagRegex = /(?:<\/(\w+)>|<(\w+)\s?[^/]*?\/?>)/gi + + let buffer = '' // Current output buffer + const level = [] // How deep we are in tags and which tags were there + let textBuffer = '' // Current line content + let tagBuffer = null // Current tag buffer, if null = we are not currently reading a tag + + // Extracts tagname from tag, i.e. => span + const getTagName = (tag) => { + // eslint-disable-next-line no-unused-vars + const result = tagRegex.exec(tag) + return result && (result[1] || result[2]) + } + + const flush = () => { // Processes current line buffer, adds it to output buffer and clears line buffer + buffer += processor(textBuffer) + textBuffer = '' + } + + const handleBr = (tag) => { // handles single newlines/linebreaks + flush() + buffer += tag + } + + const handleOpen = (tag) => { // handles opening tags + flush() + buffer += tag + level.push(tag) + } + + const handleClose = (tag) => { // handles closing tags + flush() + buffer += tag + if (level[level.length - 1] === tag) { + level.pop() + } + } + + for (let i = 0; i < html.length; i++) { + const char = html[i] + if (char === '<' && tagBuffer !== null) { + tagBuffer = char + } else if (char !== '>' && tagBuffer !== null) { + tagBuffer += char + } else if (char === '>' && tagBuffer !== null) { + tagBuffer += char + const tagName = getTagName(tagBuffer) + if (handledTags.has(tagName)) { + if (tagName === 'br') { + handleBr(tagBuffer) + } + if (openCloseTags.has(tagBuffer)) { + if (tagBuffer[1] === '/') { + handleClose(tagBuffer) + } else { + handleOpen(tagBuffer) + } + } + } else { + textBuffer += tagBuffer + } + tagBuffer = null + } else if (char === '\n') { + handleBr(char) + } else { + textBuffer += char + } + } + + flush() + + return buffer +} -- cgit v1.2.3-70-g09d2 From 692ee0e95a852b1f803b7ae92d65cbf4f3ce3445 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 14 Nov 2019 00:41:14 +0200 Subject: Fix regex, tag detector condition --- src/components/status/status.js | 2 +- .../tiny_post_html_processor.service.js | 25 +++++++++++----------- 2 files changed, 13 insertions(+), 14 deletions(-) (limited to 'src/services') diff --git a/src/components/status/status.js b/src/components/status/status.js index 6dbb2199..416aa36a 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -43,7 +43,7 @@ const Status = { showingTall: this.inConversation && this.focused, showingLongSubject: false, error: null, - // Initial state + // not as computed because it sets the initial state which will be changed later expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject, } }, diff --git a/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js b/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js index c9ff81e1..b96c1ccf 100644 --- a/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js +++ b/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js @@ -9,17 +9,15 @@ export const processHtml = (html, processor) => { const handledTags = new Set(['p', 'br', 'div']) const openCloseTags = new Set(['p', 'div']) - const tagRegex = /(?:<\/(\w+)>|<(\w+)\s?[^/]*?\/?>)/gi let buffer = '' // Current output buffer const level = [] // How deep we are in tags and which tags were there let textBuffer = '' // Current line content let tagBuffer = null // Current tag buffer, if null = we are not currently reading a tag - // Extracts tagname from tag, i.e. => span + // Extracts tag name from tag, i.e. => span const getTagName = (tag) => { - // eslint-disable-next-line no-unused-vars - const result = tagRegex.exec(tag) + const result = /(?:<\/(\w+)>|<(\w+)\s?[^/]*?\/?>)/gi.exec(tag) return result && (result[1] || result[2]) } @@ -49,28 +47,29 @@ export const processHtml = (html, processor) => { for (let i = 0; i < html.length; i++) { const char = html[i] - if (char === '<' && tagBuffer !== null) { + if (char === '<' && tagBuffer === null) { tagBuffer = char } else if (char !== '>' && tagBuffer !== null) { tagBuffer += char } else if (char === '>' && tagBuffer !== null) { tagBuffer += char - const tagName = getTagName(tagBuffer) + const tagFull = tagBuffer + tagBuffer = null + const tagName = getTagName(tagFull) if (handledTags.has(tagName)) { if (tagName === 'br') { - handleBr(tagBuffer) + handleBr(tagFull) } - if (openCloseTags.has(tagBuffer)) { - if (tagBuffer[1] === '/') { - handleClose(tagBuffer) + if (openCloseTags.has(tagFull)) { + if (tagFull[1] === '/') { + handleClose(tagFull) } else { - handleOpen(tagBuffer) + handleOpen(tagFull) } } } else { - textBuffer += tagBuffer + textBuffer += tagFull } - tagBuffer = null } else if (char === '\n') { handleBr(char) } else { -- cgit v1.2.3-70-g09d2 From bd2a682b83743311645241fe644e853e1a359b67 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 14 Nov 2019 22:40:20 +0200 Subject: tests + updates --- .../tiny_post_html_processor.service.js | 19 ++++- .../tiny_post_html_processor.spec.js | 96 ++++++++++++++++++++++ 2 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 test/unit/specs/services/tiny_post_html_processor/tiny_post_html_processor.spec.js (limited to 'src/services') diff --git a/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js b/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js index b96c1ccf..de6f20ef 100644 --- a/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js +++ b/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js @@ -2,6 +2,8 @@ * This is a tiny purpose-built HTML parser/processor. This basically detects any type of visual newline and * allows it to be processed, useful for greentexting, mostly * + * known issue: doesn't handle CDATA so nested CDATA might not work well + * * @param {Object} input - input data * @param {(string) => string} processor - function that will be called on every line * @return {string} processed html @@ -22,11 +24,15 @@ export const processHtml = (html, processor) => { } const flush = () => { // Processes current line buffer, adds it to output buffer and clears line buffer - buffer += processor(textBuffer) + if (textBuffer.trim().length > 0) { + buffer += processor(textBuffer) + } else { + buffer += textBuffer + } textBuffer = '' } - const handleBr = (tag) => { // handles single newlines/linebreaks + const handleBr = (tag) => { // handles single newlines/linebreaks/selfclosing flush() buffer += tag } @@ -59,10 +65,12 @@ export const processHtml = (html, processor) => { if (handledTags.has(tagName)) { if (tagName === 'br') { handleBr(tagFull) - } - if (openCloseTags.has(tagFull)) { + } else if (openCloseTags.has(tagName)) { if (tagFull[1] === '/') { handleClose(tagFull) + } else if (tagFull[tagFull.length - 2] === '/') { + // self-closing + handleBr(tagFull) } else { handleOpen(tagFull) } @@ -76,6 +84,9 @@ export const processHtml = (html, processor) => { textBuffer += char } } + if (tagBuffer) { + textBuffer += tagBuffer + } flush() diff --git a/test/unit/specs/services/tiny_post_html_processor/tiny_post_html_processor.spec.js b/test/unit/specs/services/tiny_post_html_processor/tiny_post_html_processor.spec.js new file mode 100644 index 00000000..f301429d --- /dev/null +++ b/test/unit/specs/services/tiny_post_html_processor/tiny_post_html_processor.spec.js @@ -0,0 +1,96 @@ +import { processHtml } from 'src/services/tiny_post_html_processor/tiny_post_html_processor.service.js' + +describe('TinyPostHTMLProcessor', () => { + describe('with processor that keeps original line should not make any changes to HTML when', () => { + const processorKeep = (line) => line + it('fed with regular HTML with newlines', () => { + const inputOutput = '1
2

3 4

5 \n 6

7
8


\n
' + expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput) + }) + + it('fed with possibly broken HTML with invalid tags/composition', () => { + const inputOutput = ' ayylmao ' + expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput) + }) + + it('fed with very broken HTML with broken composition', () => { + const inputOutput = '

lmao what whats going on
wha

' + expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput) + }) + + it('fed with sorta valid HTML but tags aren\'t closed', () => { + const inputOutput = 'just leaving a

hanging' + expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput) + }) + + it('fed with not really HTML at this point... tags that aren\'t finished', () => { + const inputOutput = 'do you expect me to finish this
{ + const inputOutput = 'look ma

p \nwithin

p!

and a
div!

' + expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput) + }) + + it('fed with maybe valid HTML? self-closing divs and ps', () => { + const inputOutput = 'a
what now

?' + expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput) + }) + + it('fed with valid XHTML containing a CDATA', () => { + const inputOutput = 'Yes, it is me, ' + expect(processHtml(inputOutput, processorKeep)).to.eql(inputOutput) + }) + }) + describe('with processor that replaces lines with word "_" should match expected line when', () => { + const processorReplace = (line) => '_' + it('fed with regular HTML with newlines', () => { + const input = '1
2

3 4

5 \n 6

7
8


\n
' + const output = '_
_

_

_\n_

_
_


\n
' + expect(processHtml(input, processorReplace)).to.eql(output) + }) + + it('fed with possibly broken HTML with invalid tags/composition', () => { + const input = ' ayylmao ' + const output = '_' + expect(processHtml(input, processorReplace)).to.eql(output) + }) + + it('fed with very broken HTML with broken composition', () => { + const input = '

lmao what
whats going on
wha

' + const output = '

_
_
_

' + expect(processHtml(input, processorReplace)).to.eql(output) + }) + + it('fed with sorta valid HTML but tags aren\'t closed', () => { + const input = 'just leaving a

hanging' + const output = '_
_' + expect(processHtml(input, processorReplace)).to.eql(output) + }) + + it('fed with not really HTML at this point... tags that aren\'t finished', () => { + const input = 'do you expect me to finish this
{ + const input = 'look ma

p \nwithin

p!

and a
div!

' + const output = '_

_\n_

_

_
_

' + expect(processHtml(input, processorReplace)).to.eql(output) + }) + + it('fed with maybe valid HTML? self-closing divs and ps', () => { + const input = 'a
what now

?' + const output = '_

_

_' + expect(processHtml(input, processorReplace)).to.eql(output) + }) + + it('fed with valid XHTML containing a CDATA', () => { + const input = 'Yes, it is me, ' + const output = '_' + expect(processHtml(input, processorReplace)).to.eql(output) + }) + }) +}) -- cgit v1.2.3-70-g09d2 From 0995658757b89eeb38b78e997bec2d85b96296af Mon Sep 17 00:00:00 2001 From: kPherox Date: Tue, 19 Nov 2019 14:07:15 +0000 Subject: backend interactor service: implement startFetchingFollowRequest backend interactor service: remove unused fetchFollowRequests --- src/components/nav_panel/nav_panel.js | 7 +------ src/components/side_drawer/side_drawer.js | 4 ++++ src/modules/api.js | 7 +++++++ src/modules/users.js | 1 + .../backend_interactor_service/backend_interactor_service.js | 8 ++++++-- 5 files changed, 19 insertions(+), 8 deletions(-) (limited to 'src/services') diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index aa3f7605..7f783acb 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -1,12 +1,7 @@ -import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service' - const NavPanel = { created () { if (this.currentUser && this.currentUser.locked) { - const store = this.$store - const credentials = store.state.users.currentUser.credentials - - followRequestFetcher.startFetching({ store, credentials }) + this.$store.dispatch('startFetchingFollowRequest') } }, computed: { diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js index 567d2e5e..0188cf3e 100644 --- a/src/components/side_drawer/side_drawer.js +++ b/src/components/side_drawer/side_drawer.js @@ -10,6 +10,10 @@ const SideDrawer = { }), created () { this.closeGesture = GestureService.swipeGesture(GestureService.DIRECTION_LEFT, this.toggleDrawer) + + if (this.currentUser && this.currentUser.locked) { + this.$store.dispatch('startFetchingFollowRequest') + } }, components: { UserCard }, computed: { diff --git a/src/modules/api.js b/src/modules/api.js index eb6a7980..1293e3c8 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -43,6 +43,13 @@ const api = { const fetcher = store.state.backendInteractor.startFetchingNotifications({ store }) store.commit('addFetcher', { fetcherName: 'notifications', fetcher }) }, + startFetchingFollowRequest (store) { + // Don't start fetching if we already are. + if (store.state.fetchers['followRequest']) return + + const fetcher = store.state.backendInteractor.startFetchingFollowRequest({ store }) + store.commit('addFetcher', { fetcherName: 'followRequest', fetcher }) + }, stopFetching (store, fetcherName) { const fetcher = store.state.fetchers[fetcherName] window.clearInterval(fetcher) diff --git a/src/modules/users.js b/src/modules/users.js index 1c9ff5e8..14b2d8b5 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -434,6 +434,7 @@ const users = { store.dispatch('stopFetching', 'friends') store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken())) store.dispatch('stopFetching', 'notifications') + store.dispatch('stopFetching', 'followRequest') store.commit('clearNotifications') store.commit('resetStatuses') }) diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index d6617276..c16bd1f1 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -1,6 +1,7 @@ import apiService from '../api/api.service.js' import timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js' import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js' +import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service' const backendInteractorService = credentials => { const fetchStatus = ({ id }) => { @@ -63,6 +64,10 @@ const backendInteractorService = credentials => { return notificationsFetcher.startFetching({ store, credentials }) } + const startFetchingFollowRequest = ({ store }) => { + return followRequestFetcher.startFetching({ store, credentials }) + } + // eslint-disable-next-line camelcase const tagUser = ({ screen_name }, tag) => { return apiService.tagUser({ screen_name, tag, credentials }) @@ -111,7 +116,6 @@ const backendInteractorService = credentials => { const subscribeUser = (id) => apiService.subscribeUser({ credentials, id }) const unsubscribeUser = (id) => apiService.unsubscribeUser({ credentials, id }) const fetchBlocks = () => apiService.fetchBlocks({ credentials }) - const fetchFollowRequests = () => apiService.fetchFollowRequests({ credentials }) const fetchOAuthTokens = () => apiService.fetchOAuthTokens({ credentials }) const revokeOAuthToken = (id) => apiService.revokeOAuthToken({ id, credentials }) const fetchPinnedStatuses = (id) => apiService.fetchPinnedStatuses({ credentials, id }) @@ -168,6 +172,7 @@ const backendInteractorService = credentials => { verifyCredentials: apiService.verifyCredentials, startFetchingTimeline, startFetchingNotifications, + startFetchingFollowRequest, fetchMutes, muteUser, unmuteUser, @@ -203,7 +208,6 @@ const backendInteractorService = credentials => { mfaSetupOTP, mfaConfirmOTP, mfaDisableOTP, - fetchFollowRequests, approveUser, denyUser, vote, -- cgit v1.2.3-70-g09d2 From a55486f8d78161166e84b2b69709a49b3845c14e Mon Sep 17 00:00:00 2001 From: kPherox Date: Tue, 19 Nov 2019 14:15:41 +0000 Subject: Normalize profile fields --- src/services/entity_normalizer/entity_normalizer.service.js | 9 +++++++++ .../services/entity_normalizer/entity_normalizer.spec.js | 13 +++++++++++++ 2 files changed, 22 insertions(+) (limited to 'src/services') diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 5f45660d..ca79df6f 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -46,6 +46,14 @@ export const parseUser = (data) => { output.description = data.note output.description_html = addEmojis(data.note, data.emojis) + output.fields = data.fields + output.fields_html = data.fields.map(field => { + return { + name: addEmojis(field.name, data.emojis), + value: addEmojis(field.value, data.emojis) + } + }) + // Utilize avatar_static for gif avatars? output.profile_image_url = data.avatar output.profile_image_url_original = data.avatar @@ -95,6 +103,7 @@ export const parseUser = (data) => { if (data.source) { output.description = data.source.note output.default_scope = data.source.privacy + output.fields = data.source.fields if (data.source.pleroma) { output.no_rich_text = data.source.pleroma.no_rich_text output.show_role = data.source.pleroma.show_role diff --git a/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js b/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js index 49f378e2..cfb380ba 100644 --- a/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js +++ b/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js @@ -277,6 +277,19 @@ describe('API Entities normalizer', () => { expect(parsedUser).to.have.property('description_html').that.contains(' { + const user = makeMockUserMasto({ emojis: makeMockEmojiMasto(), fields: [{ name: ':thinking:', value: ':image:' }] }) + + const parsedUser = parseUser(user) + + expect(parsedUser).to.have.property('fields_html').to.be.an('array') + + const field = parsedUser.fields_html[0] + + expect(field).to.have.property('name').that.contains(' { const user = makeMockUserMasto({ pleroma: { hide_followers: true, hide_follows: false, hide_followers_count: false, hide_follows_count: true } }) -- cgit v1.2.3-70-g09d2 From a06705d9390b738ccace9fb6a0c213306f753f42 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sun, 8 Dec 2019 13:52:26 +0300 Subject: Added OAuth 'push' and 'admin' scopes. --- src/services/new_api/oauth.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/services') diff --git a/src/services/new_api/oauth.js b/src/services/new_api/oauth.js index d0d18c03..3c8e64bd 100644 --- a/src/services/new_api/oauth.js +++ b/src/services/new_api/oauth.js @@ -12,7 +12,7 @@ export const getOrCreateApp = ({ clientId, clientSecret, instance, commit }) => form.append('client_name', `PleromaFE_${window.___pleromafe_commit_hash}_${(new Date()).toISOString()}`) form.append('redirect_uris', REDIRECT_URI) - form.append('scopes', 'read write follow') + form.append('scopes', 'read write follow push admin') return window.fetch(url, { method: 'POST', @@ -28,7 +28,7 @@ const login = ({ instance, clientId }) => { response_type: 'code', client_id: clientId, redirect_uri: REDIRECT_URI, - scope: 'read write follow' + scope: 'read write follow push admin' } const dataString = reduce(data, (acc, v, k) => { -- cgit v1.2.3-70-g09d2 From d6dc2bad1f2175f9a9eb0dfd4c04fafab410632b Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Wed, 11 Dec 2019 15:59:29 +0300 Subject: fixed typo --- src/services/api/api.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/services') diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 8f5eb416..68be0d50 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -22,7 +22,7 @@ const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes' const MFA_SETUP_OTP_URL = '/api/pleroma/accounts/mfa/setup/totp' const MFA_CONFIRM_OTP_URL = '/api/pleroma/accounts/mfa/confirm/totp' -const MFA_DISABLE_OTP_URL = '/api/pleroma/account/mfa/totp' +const MFA_DISABLE_OTP_URL = '/api/pleroma/accounts/mfa/totp' const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials' const MASTODON_REGISTRATION_URL = '/api/v1/accounts' -- cgit v1.2.3-70-g09d2 From b3992358487d5afa7499759a90d6447a2b0bfe20 Mon Sep 17 00:00:00 2001 From: lain Date: Thu, 12 Dec 2019 09:38:24 +0000 Subject: Revert "Merge branch 'oauth-extra-scopes' into 'develop'" This reverts merge request !1024 --- src/services/new_api/oauth.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/services') diff --git a/src/services/new_api/oauth.js b/src/services/new_api/oauth.js index 3c8e64bd..d0d18c03 100644 --- a/src/services/new_api/oauth.js +++ b/src/services/new_api/oauth.js @@ -12,7 +12,7 @@ export const getOrCreateApp = ({ clientId, clientSecret, instance, commit }) => form.append('client_name', `PleromaFE_${window.___pleromafe_commit_hash}_${(new Date()).toISOString()}`) form.append('redirect_uris', REDIRECT_URI) - form.append('scopes', 'read write follow push admin') + form.append('scopes', 'read write follow') return window.fetch(url, { method: 'POST', @@ -28,7 +28,7 @@ const login = ({ instance, clientId }) => { response_type: 'code', client_id: clientId, redirect_uri: REDIRECT_URI, - scope: 'read write follow push admin' + scope: 'read write follow' } const dataString = reduce(data, (acc, v, k) => { -- cgit v1.2.3-70-g09d2