diff --git a/package.json b/package.json index 3efbe8d8..06dc4609 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,6 @@ "react-native-swipe-list-view": "^3.2.9", "react-native-tab-view": "^3.5.1", "rn-placeholder": "^3.0.3", - "url-parse": "^1.5.10", "zeego": "^1.3.1" }, "devDependencies": { @@ -102,7 +101,6 @@ "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "@types/react-native-share-menu": "^5.0.2", - "@types/url-parse": "^1.4.8", "babel-plugin-module-resolver": "^5.0.0", "babel-plugin-transform-remove-console": "^6.9.4", "chalk": "^4.1.2", diff --git a/src/components/Instance/index.tsx b/src/components/Instance/index.tsx index ef24f068..5ea043cd 100644 --- a/src/components/Instance/index.tsx +++ b/src/components/Instance/index.tsx @@ -21,6 +21,7 @@ import { useTheme } from '@utils/styles/ThemeManager' import * as AuthSession from 'expo-auth-session' import * as Crypto from 'expo-crypto' import { Image } from 'expo-image' +import * as Linking from 'expo-linking' import * as WebBrowser from 'expo-web-browser' import { debounce } from 'lodash' import React, { RefObject, useCallback, useState } from 'react' @@ -28,7 +29,6 @@ import { Trans, useTranslation } from 'react-i18next' import { Alert, KeyboardAvoidingView, Platform, TextInput, View } from 'react-native' import { ScrollView } from 'react-native-gesture-handler' import { fromByteArray } from 'react-native-quick-base64' -import parse from 'url-parse' import CustomText from '../Text' export interface Props { @@ -51,7 +51,7 @@ const ComponentInstance: React.FC = ({ const whitelisted: boolean = !!domain.length && !!errorCode && - !!(parse(`https://${domain}/`).hostname === domain) && + !!(Linking.parse(`https://${domain}/`).hostname === domain) && errorCode === 401 const instanceQuery = useInstanceQuery({ @@ -129,7 +129,7 @@ const ComponentInstance: React.FC = ({ (instanceQuery.data as Mastodon.Instance_V2)?.domain || instanceQuery.data?.account_domain || ((instanceQuery.data as Mastodon.Instance_V1)?.uri - ? parse((instanceQuery.data as Mastodon.Instance_V1).uri).hostname + ? Linking.parse((instanceQuery.data as Mastodon.Instance_V1).uri).hostname : undefined) || (instanceQuery.data as Mastodon.Instance_V1)?.uri, 'auth.account.avatar_static': avatar_static, diff --git a/src/components/contextMenu/instance.ts b/src/components/contextMenu/instance.ts index 7b0a2af8..418b6c80 100644 --- a/src/components/contextMenu/instance.ts +++ b/src/components/contextMenu/instance.ts @@ -2,9 +2,9 @@ import { displayMessage } from '@components/Message' import { useQueryClient } from '@tanstack/react-query' import { QueryKeyTimeline, useTimelineMutation } from '@utils/queryHooks/timeline' import { getAccountStorage } from '@utils/storage/actions' +import * as Linking from 'expo-linking' import { useTranslation } from 'react-i18next' import { Alert } from 'react-native' -import parse from 'url-parse' const menuInstance = ({ status, @@ -32,9 +32,9 @@ const menuInstance = ({ const menus: ContextMenu = [] - const instance = parse(status.uri).hostname + const instance = Linking.parse(status.uri).hostname - if (instance !== getAccountStorage.string('auth.domain')) { + if (instance && instance !== getAccountStorage.string('auth.domain')) { menus.push([ { type: 'item', diff --git a/src/utils/api/helpers/connect.ts b/src/utils/api/helpers/connect.ts index a84601ef..d520bf4d 100644 --- a/src/utils/api/helpers/connect.ts +++ b/src/utils/api/helpers/connect.ts @@ -2,7 +2,7 @@ import { mapEnvironment } from '@utils/helpers/checkEnvironment' import { GLOBAL } from '@utils/storage' import { setGlobalStorage } from '@utils/storage/actions' import axios from 'axios' -import parse from 'url-parse' +import * as Linking from 'expo-linking' import { userAgent } from '.' const list = [ @@ -86,21 +86,23 @@ export const connectMedia = (args?: { }): { uri?: string; headers?: { 'x-tooot-domain': string } } => { if (GLOBAL.connect) { if (args?.uri) { - const host = parse(args.uri).host - return { - ...args, - uri: args.uri.replace( - host, - CONNECT_DOMAIN( - args.uri - .split('') - .map(i => i.charCodeAt(0)) - .reduce((a, b) => a + b, 0) % - (list.length + 1) - ) - ), - headers: { 'x-tooot-domain': host } - } + const host = Linking.parse(args.uri).hostname + return host + ? { + ...args, + uri: args.uri.replace( + host, + CONNECT_DOMAIN( + args.uri + .split('') + .map(i => i.charCodeAt(0)) + .reduce((a, b) => a + b, 0) % + (list.length + 1) + ) + ), + headers: { 'x-tooot-domain': host } + } + : { ...args } } else { return { ...args } } diff --git a/src/utils/api/helpers/index.ts b/src/utils/api/helpers/index.ts index c65ec17c..a78c9928 100644 --- a/src/utils/api/helpers/index.ts +++ b/src/utils/api/helpers/index.ts @@ -3,8 +3,8 @@ import { GLOBAL } from '@utils/storage' import { setGlobalStorage } from '@utils/storage/actions' import chalk from 'chalk' import Constants from 'expo-constants' +import * as Linking from 'expo-linking' import { Platform } from 'react-native' -import parse from 'url-parse' const userAgent = { 'User-Agent': `tooot/${Constants.expoConfig?.version} ${Platform.OS}/${Platform.Version}` @@ -80,17 +80,22 @@ export const parseHeaderLinks = (headerLink?: string): PagedResponse['links'] => const linkParsed = [...headerLink.matchAll(/<(\S+?)>; *rel="(next|prev)"/gi)] for (const link of linkParsed) { - const queries = parse(link[1], true).query + const queries = Linking.parse(link[1]).queryParams + if (!queries) return + const isOffset = !!queries.offset?.length + const unwrapArray = (value: any | any[]) => (Array.isArray(value) ? value[0] : value) switch (link[2]) { case 'prev': const prevId = isOffset ? queries.offset : queries.min_id - if (prevId) links.prev = isOffset ? { offset: prevId } : { min_id: prevId } + if (prevId) + links.prev = isOffset ? { offset: unwrapArray(prevId) } : { min_id: unwrapArray(prevId) } break case 'next': const nextId = isOffset ? queries.offset : queries.max_id - if (nextId) links.next = isOffset ? { offset: nextId } : { max_id: nextId } + if (nextId) + links.next = isOffset ? { offset: unwrapArray(nextId) } : { max_id: unwrapArray(nextId) } break } } diff --git a/src/utils/helpers/urlMatcher.ts b/src/utils/helpers/urlMatcher.ts index 0d39cc14..72f6cc1b 100644 --- a/src/utils/helpers/urlMatcher.ts +++ b/src/utils/helpers/urlMatcher.ts @@ -1,5 +1,5 @@ import { getAccountStorage } from '@utils/storage/actions' -import parse from 'url-parse' +import * as Linking from 'expo-linking' // Would mess with the /@username format const BLACK_LIST = ['matters.news', 'medium.com'] @@ -13,8 +13,8 @@ export const urlMatcher = ( status?: Partial> } | undefined => { - const parsed = parse(url) - if (!parsed.hostname.length || !parsed.pathname.length) return undefined + const parsed = Linking.parse(url) + if (!parsed.hostname?.length || !parsed.path?.length) return undefined const domain = parsed.hostname if (BLACK_LIST.includes(domain)) { @@ -26,8 +26,8 @@ export const urlMatcher = ( let statusId: string | undefined let accountAcct: string | undefined - const segments = parsed.pathname.split('/') - const last = segments[segments.length - 1] + const segments = parsed.path.split('/') + const last = segments.at(-1) const length = segments.length // there is a starting slash const testAndAssignStatusId = (id: string) => { @@ -38,7 +38,7 @@ export const urlMatcher = ( switch (last?.startsWith('@')) { case true: - if (length === 2 || (length === 3 && segments[length - 2] === 'web')) { + if (length === 1 || (length === 2 && segments.at(-2) === 'web')) { // https://social.xmflsct.com/@tooot <- Mastodon v4.0 and above // https://social.xmflsct.com/web/@tooot <- Mastodon v3.5 and below ! cannot be searched on the same instance accountAcct = `${last}@${domain}` @@ -48,13 +48,13 @@ export const urlMatcher = ( const nextToLast = segments[length - 2] if (nextToLast) { if (nextToLast === 'statuses') { - if (length === 4 && segments[length - 3] === 'web') { + if (length === 3 && segments.at(-3) === 'web') { // https://social.xmflsct.com/web/statuses/105590085754428765 <- old testAndAssignStatusId(last) } else if ( - length === 5 && - segments[length - 2] === 'statuses' && - segments[length - 4] === 'users' + length === 4 && + segments.at(-2) === 'statuses' && + segments.at(-4) === 'users' ) { // https://social.xmflsct.com/users/tooot/statuses/105590085754428765 <- default Mastodon testAndAssignStatusId(last) @@ -62,7 +62,7 @@ export const urlMatcher = ( } } else if ( nextToLast.startsWith('@') && - (length === 3 || (length === 4 && segments[length - 3] === 'web')) + (length === 2 || (length === 3 && segments.at(-3) === 'web')) ) { // https://social.xmflsct.com/web/@tooot/105590085754428765 <- pretty Mastodon v3.5 and below // https://social.xmflsct.com/@tooot/105590085754428765 <- pretty Mastodon v4.0 and above diff --git a/yarn.lock b/yarn.lock index ee64f63d..dfb38794 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3781,13 +3781,6 @@ __metadata: languageName: node linkType: hard -"@types/url-parse@npm:^1.4.8": - version: 1.4.8 - resolution: "@types/url-parse@npm:1.4.8" - checksum: 44a5e96ed4b579c43750f3578bfa9165f97a359c3b2a85ee126e9c16db964f6ea105e152afd3d1adbd15850a8b812043215f3820112177bb4255a60b432dbd85 - languageName: node - linkType: hard - "@types/yargs-parser@npm:*": version: 21.0.0 resolution: "@types/yargs-parser@npm:21.0.0" @@ -11470,7 +11463,6 @@ __metadata: "@types/react": ^18.0.28 "@types/react-dom": ^18.0.11 "@types/react-native-share-menu": ^5.0.2 - "@types/url-parse": ^1.4.8 axios: ^1.3.4 babel-plugin-module-resolver: ^5.0.0 babel-plugin-transform-remove-console: ^6.9.4 @@ -11524,7 +11516,6 @@ __metadata: react-native-tab-view: ^3.5.1 rn-placeholder: ^3.0.3 typescript: ^4.9.5 - url-parse: ^1.5.10 zeego: ^1.3.1 languageName: unknown linkType: soft @@ -11825,7 +11816,7 @@ __metadata: languageName: node linkType: hard -"url-parse@npm:^1.5.10, url-parse@npm:^1.5.9": +"url-parse@npm:^1.5.9": version: 1.5.10 resolution: "url-parse@npm:1.5.10" dependencies: