mirror of https://github.com/tooot-app/app
commit
ec853d04a6
|
@ -7,7 +7,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: macos-11
|
runs-on: macos-12
|
||||||
steps:
|
steps:
|
||||||
- name: -- Step 0 -- Extract branch name
|
- name: -- Step 0 -- Extract branch name
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -16,22 +16,27 @@ jobs:
|
||||||
- name: -- Step 1 -- Checkout code
|
- name: -- Step 1 -- Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: -- Step 2 -- Setup node
|
- name: -- Step 2 -- Setup node
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16.x
|
node-version: 16
|
||||||
- name: -- Step 3 -- Use Expo action
|
- name: -- Step 3 -- Setup Java
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: '11'
|
||||||
|
- name: -- Step 4 -- Use Expo action
|
||||||
uses: expo/expo-github-action@v6
|
uses: expo/expo-github-action@v6
|
||||||
with:
|
with:
|
||||||
expo-version: 5.x
|
expo-version: 5.x
|
||||||
username: ${{ secrets.EXPO_USERNAME }}
|
username: ${{ secrets.EXPO_USERNAME }}
|
||||||
token: ${{ secrets.EXPO_TOKEN }}
|
token: ${{ secrets.EXPO_TOKEN }}
|
||||||
- name: -- Step 4 -- Install node dependencies
|
- name: -- Step 5 -- Install node dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
- name: -- Step 5 -- Install ruby dependencies
|
- name: -- Step 6 -- Install ruby dependencies
|
||||||
run: bundle install
|
run: bundle install
|
||||||
- name: -- Step 6 -- Run fastlane
|
- name: -- Step 7 -- Run fastlane
|
||||||
env:
|
env:
|
||||||
DEVELOPER_DIR: /Applications/Xcode_13.2.1.app/Contents/Developer
|
DEVELOPER_DIR: /Applications/Xcode_13.3.1.app/Contents/Developer
|
||||||
ENVIRONMENT: ${{ steps.branch.outputs.branch }}
|
ENVIRONMENT: ${{ steps.branch.outputs.branch }}
|
||||||
LC_ALL: en_US.UTF-8
|
LC_ALL: en_US.UTF-8
|
||||||
LANG: en_US.UTF-8
|
LANG: en_US.UTF-8
|
||||||
|
|
66
Gemfile.lock
66
Gemfile.lock
|
@ -3,7 +3,7 @@ GEM
|
||||||
specs:
|
specs:
|
||||||
CFPropertyList (3.0.5)
|
CFPropertyList (3.0.5)
|
||||||
rexml
|
rexml
|
||||||
activesupport (6.1.4.1)
|
activesupport (6.1.5)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
|
@ -17,27 +17,27 @@ GEM
|
||||||
artifactory (3.0.15)
|
artifactory (3.0.15)
|
||||||
atomos (0.1.3)
|
atomos (0.1.3)
|
||||||
aws-eventstream (1.2.0)
|
aws-eventstream (1.2.0)
|
||||||
aws-partitions (1.554.0)
|
aws-partitions (1.582.0)
|
||||||
aws-sdk-core (3.126.0)
|
aws-sdk-core (3.130.2)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
aws-partitions (~> 1, >= 1.525.0)
|
aws-partitions (~> 1, >= 1.525.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
jmespath (~> 1.0)
|
jmespath (~> 1.0)
|
||||||
aws-sdk-kms (1.54.0)
|
aws-sdk-kms (1.56.0)
|
||||||
aws-sdk-core (~> 3, >= 3.126.0)
|
aws-sdk-core (~> 3, >= 3.127.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sdk-s3 (1.112.0)
|
aws-sdk-s3 (1.113.2)
|
||||||
aws-sdk-core (~> 3, >= 3.126.0)
|
aws-sdk-core (~> 3, >= 3.127.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.4)
|
aws-sigv4 (~> 1.4)
|
||||||
aws-sigv4 (1.4.0)
|
aws-sigv4 (1.5.0)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
babosa (1.0.4)
|
babosa (1.0.4)
|
||||||
claide (1.1.0)
|
claide (1.1.0)
|
||||||
cocoapods (1.11.2)
|
cocoapods (1.11.3)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
cocoapods-core (= 1.11.2)
|
cocoapods-core (= 1.11.3)
|
||||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||||
cocoapods-downloader (>= 1.4.0, < 2.0)
|
cocoapods-downloader (>= 1.4.0, < 2.0)
|
||||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||||
|
@ -52,7 +52,7 @@ GEM
|
||||||
nap (~> 1.0)
|
nap (~> 1.0)
|
||||||
ruby-macho (>= 1.0, < 3.0)
|
ruby-macho (>= 1.0, < 3.0)
|
||||||
xcodeproj (>= 1.21.0, < 2.0)
|
xcodeproj (>= 1.21.0, < 2.0)
|
||||||
cocoapods-core (1.11.2)
|
cocoapods-core (1.11.3)
|
||||||
activesupport (>= 5.0, < 7)
|
activesupport (>= 5.0, < 7)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
algoliasearch (~> 1.0)
|
algoliasearch (~> 1.0)
|
||||||
|
@ -63,7 +63,7 @@ GEM
|
||||||
public_suffix (~> 4.0)
|
public_suffix (~> 4.0)
|
||||||
typhoeus (~> 1.0)
|
typhoeus (~> 1.0)
|
||||||
cocoapods-deintegrate (1.0.5)
|
cocoapods-deintegrate (1.0.5)
|
||||||
cocoapods-downloader (1.5.1)
|
cocoapods-downloader (1.6.3)
|
||||||
cocoapods-plugins (1.0.0)
|
cocoapods-plugins (1.0.0)
|
||||||
nap
|
nap
|
||||||
cocoapods-search (1.0.1)
|
cocoapods-search (1.0.1)
|
||||||
|
@ -75,7 +75,7 @@ GEM
|
||||||
colored2 (3.1.2)
|
colored2 (3.1.2)
|
||||||
commander (4.6.0)
|
commander (4.6.0)
|
||||||
highline (~> 2.0.0)
|
highline (~> 2.0.0)
|
||||||
concurrent-ruby (1.1.9)
|
concurrent-ruby (1.1.10)
|
||||||
declarative (0.0.20)
|
declarative (0.0.20)
|
||||||
digest-crc (0.6.4)
|
digest-crc (0.6.4)
|
||||||
rake (>= 12.0.0, < 14.0.0)
|
rake (>= 12.0.0, < 14.0.0)
|
||||||
|
@ -86,8 +86,8 @@ GEM
|
||||||
escape (0.0.4)
|
escape (0.0.4)
|
||||||
ethon (0.15.0)
|
ethon (0.15.0)
|
||||||
ffi (>= 1.15.0)
|
ffi (>= 1.15.0)
|
||||||
excon (0.91.0)
|
excon (0.92.3)
|
||||||
faraday (1.9.3)
|
faraday (1.10.0)
|
||||||
faraday-em_http (~> 1.0)
|
faraday-em_http (~> 1.0)
|
||||||
faraday-em_synchrony (~> 1.0)
|
faraday-em_synchrony (~> 1.0)
|
||||||
faraday-excon (~> 1.1)
|
faraday-excon (~> 1.1)
|
||||||
|
@ -116,7 +116,7 @@ GEM
|
||||||
faraday_middleware (1.2.0)
|
faraday_middleware (1.2.0)
|
||||||
faraday (~> 1.0)
|
faraday (~> 1.0)
|
||||||
fastimage (2.2.6)
|
fastimage (2.2.6)
|
||||||
fastlane (2.204.2)
|
fastlane (2.205.2)
|
||||||
CFPropertyList (>= 2.3, < 4.0.0)
|
CFPropertyList (>= 2.3, < 4.0.0)
|
||||||
addressable (>= 2.8, < 3.0.0)
|
addressable (>= 2.8, < 3.0.0)
|
||||||
artifactory (~> 3.0)
|
artifactory (~> 3.0)
|
||||||
|
@ -156,14 +156,14 @@ GEM
|
||||||
xcpretty (~> 0.3.0)
|
xcpretty (~> 0.3.0)
|
||||||
xcpretty-travis-formatter (>= 0.0.3)
|
xcpretty-travis-formatter (>= 0.0.3)
|
||||||
fastlane-plugin-json (1.1.0)
|
fastlane-plugin-json (1.1.0)
|
||||||
fastlane-plugin-sentry (1.11.1)
|
fastlane-plugin-sentry (1.12.0)
|
||||||
fastlane-plugin-versioning_android (0.1.0)
|
fastlane-plugin-versioning_android (0.1.0)
|
||||||
fastlane-plugin-yarn (1.2)
|
fastlane-plugin-yarn (1.2)
|
||||||
ffi (1.15.4)
|
ffi (1.15.5)
|
||||||
fourflusher (2.3.1)
|
fourflusher (2.3.1)
|
||||||
fuzzy_match (2.0.4)
|
fuzzy_match (2.0.4)
|
||||||
gh_inspector (1.1.3)
|
gh_inspector (1.1.3)
|
||||||
google-apis-androidpublisher_v3 (0.16.0)
|
google-apis-androidpublisher_v3 (0.19.0)
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
google-apis-core (>= 0.4, < 2.a)
|
||||||
google-apis-core (0.4.2)
|
google-apis-core (0.4.2)
|
||||||
addressable (~> 2.5, >= 2.5.1)
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
|
@ -178,15 +178,15 @@ GEM
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
google-apis-core (>= 0.4, < 2.a)
|
||||||
google-apis-playcustomapp_v1 (0.7.0)
|
google-apis-playcustomapp_v1 (0.7.0)
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
google-apis-core (>= 0.4, < 2.a)
|
||||||
google-apis-storage_v1 (0.11.0)
|
google-apis-storage_v1 (0.13.0)
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
google-apis-core (>= 0.4, < 2.a)
|
||||||
google-cloud-core (1.6.0)
|
google-cloud-core (1.6.0)
|
||||||
google-cloud-env (~> 1.0)
|
google-cloud-env (~> 1.0)
|
||||||
google-cloud-errors (~> 1.0)
|
google-cloud-errors (~> 1.0)
|
||||||
google-cloud-env (1.5.0)
|
google-cloud-env (1.6.0)
|
||||||
faraday (>= 0.17.3, < 2.0)
|
faraday (>= 0.17.3, < 3.0)
|
||||||
google-cloud-errors (1.2.0)
|
google-cloud-errors (1.2.0)
|
||||||
google-cloud-storage (1.36.0)
|
google-cloud-storage (1.36.2)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
digest-crc (~> 0.4)
|
digest-crc (~> 0.4)
|
||||||
google-apis-iamcredentials_v1 (~> 0.1)
|
google-apis-iamcredentials_v1 (~> 0.1)
|
||||||
|
@ -194,8 +194,8 @@ GEM
|
||||||
google-cloud-core (~> 1.6)
|
google-cloud-core (~> 1.6)
|
||||||
googleauth (>= 0.16.2, < 2.a)
|
googleauth (>= 0.16.2, < 2.a)
|
||||||
mini_mime (~> 1.0)
|
mini_mime (~> 1.0)
|
||||||
googleauth (1.1.0)
|
googleauth (1.1.3)
|
||||||
faraday (>= 0.17.3, < 2.0)
|
faraday (>= 0.17.3, < 3.a)
|
||||||
jwt (>= 1.4, < 3.0)
|
jwt (>= 1.4, < 3.0)
|
||||||
memoist (~> 0.16)
|
memoist (~> 0.16)
|
||||||
multi_json (~> 1.11)
|
multi_json (~> 1.11)
|
||||||
|
@ -205,15 +205,15 @@ GEM
|
||||||
http-cookie (1.0.4)
|
http-cookie (1.0.4)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (1.8.10)
|
i18n (1.10.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
jmespath (1.5.0)
|
jmespath (1.6.1)
|
||||||
json (2.6.1)
|
json (2.6.1)
|
||||||
jwt (2.3.0)
|
jwt (2.3.0)
|
||||||
memoist (0.16.2)
|
memoist (0.16.2)
|
||||||
mini_magick (4.11.0)
|
mini_magick (4.11.0)
|
||||||
mini_mime (1.1.2)
|
mini_mime (1.1.2)
|
||||||
minitest (5.14.4)
|
minitest (5.15.0)
|
||||||
molinillo (0.8.0)
|
molinillo (0.8.0)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multipart-post (2.0.0)
|
multipart-post (2.0.0)
|
||||||
|
@ -224,7 +224,7 @@ GEM
|
||||||
optparse (0.1.1)
|
optparse (0.1.1)
|
||||||
os (1.1.4)
|
os (1.1.4)
|
||||||
plist (3.6.0)
|
plist (3.6.0)
|
||||||
public_suffix (4.0.6)
|
public_suffix (4.0.7)
|
||||||
rake (13.0.6)
|
rake (13.0.6)
|
||||||
representable (3.1.1)
|
representable (3.1.1)
|
||||||
declarative (< 0.1.0)
|
declarative (< 0.1.0)
|
||||||
|
@ -237,9 +237,9 @@ GEM
|
||||||
ruby2_keywords (0.0.5)
|
ruby2_keywords (0.0.5)
|
||||||
rubyzip (2.3.2)
|
rubyzip (2.3.2)
|
||||||
security (0.1.3)
|
security (0.1.3)
|
||||||
signet (0.16.0)
|
signet (0.16.1)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
faraday (>= 0.17.3, < 2.0)
|
faraday (>= 0.17.5, < 3.0)
|
||||||
jwt (>= 1.5, < 3.0)
|
jwt (>= 1.5, < 3.0)
|
||||||
multi_json (~> 1.10)
|
multi_json (~> 1.10)
|
||||||
simctl (1.6.8)
|
simctl (1.6.8)
|
||||||
|
@ -260,7 +260,7 @@ GEM
|
||||||
uber (0.1.0)
|
uber (0.1.0)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.8)
|
unf_ext (0.0.8.1)
|
||||||
unicode-display_width (1.8.0)
|
unicode-display_width (1.8.0)
|
||||||
webrick (1.7.0)
|
webrick (1.7.0)
|
||||||
word_wrap (1.0.0)
|
word_wrap (1.0.0)
|
||||||
|
@ -275,7 +275,7 @@ GEM
|
||||||
rouge (~> 2.0.7)
|
rouge (~> 2.0.7)
|
||||||
xcpretty-travis-formatter (1.0.1)
|
xcpretty-travis-formatter (1.0.1)
|
||||||
xcpretty (~> 0.2, >= 0.0.7)
|
xcpretty (~> 0.2, >= 0.0.7)
|
||||||
zeitwerk (2.5.1)
|
zeitwerk (2.5.4)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
universal-darwin-20
|
universal-darwin-20
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import {
|
|
||||||
toBeDisabled,
|
|
||||||
toHaveStyle,
|
|
||||||
toHaveTextContent
|
|
||||||
} from '@testing-library/jest-native'
|
|
||||||
import { cleanup, fireEvent, render } from '@testing-library/react-native/pure'
|
|
||||||
|
|
||||||
import Button from '@components/Button'
|
|
||||||
|
|
||||||
expect.extend({ toBeDisabled, toHaveStyle, toHaveTextContent })
|
|
||||||
|
|
||||||
describe('Testing component button', () => {
|
|
||||||
afterEach(cleanup)
|
|
||||||
|
|
||||||
describe('static button', () => {
|
|
||||||
it('with text only', () => {
|
|
||||||
const onPress = jest.fn()
|
|
||||||
const { getByTestId, toJSON } = render(
|
|
||||||
<Button type='text' content='Test Button' onPress={onPress} />
|
|
||||||
)
|
|
||||||
fireEvent.press(getByTestId('base'))
|
|
||||||
|
|
||||||
expect(onPress).toHaveBeenCalled()
|
|
||||||
expect(onPress).toHaveBeenCalledTimes(1)
|
|
||||||
expect(getByTestId('text')).toHaveTextContent('Test Button')
|
|
||||||
expect(toJSON()).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('with icon only', () => {
|
|
||||||
const onPress = jest.fn()
|
|
||||||
const { getByTestId, toJSON } = render(
|
|
||||||
<Button type='icon' content='X' onPress={onPress} />
|
|
||||||
)
|
|
||||||
fireEvent.press(getByTestId('base'))
|
|
||||||
|
|
||||||
expect(onPress).toHaveBeenCalled()
|
|
||||||
expect(onPress).toHaveBeenCalledTimes(1)
|
|
||||||
expect(toJSON()).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('loading state', () => {
|
|
||||||
const { getByTestId, toJSON } = render(
|
|
||||||
<Button type='text' content='test' onPress={jest.fn()} loading />
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(getByTestId('base')).toBeDisabled()
|
|
||||||
expect(toJSON()).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('disabled state', () => {
|
|
||||||
const { getByTestId, toJSON } = render(
|
|
||||||
<Button type='text' content='test' onPress={jest.fn()} disabled />
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(getByTestId('base')).toBeDisabled()
|
|
||||||
expect(toJSON()).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('apply custom styling', () => {
|
|
||||||
const { getByTestId, toJSON } = render(
|
|
||||||
<Button
|
|
||||||
type='text'
|
|
||||||
content='test'
|
|
||||||
onPress={jest.fn()}
|
|
||||||
style={{ backgroundColor: 'black' }}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(getByTestId('base')).toHaveStyle({ backgroundColor: 'black' })
|
|
||||||
expect(toJSON()).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('dynamic button', () => {
|
|
||||||
it('from default to loading', () => {
|
|
||||||
const onPress = jest.fn()
|
|
||||||
const { getByTestId, rerender } = render(
|
|
||||||
<Button type='text' content='test' onPress={onPress} />
|
|
||||||
)
|
|
||||||
rerender(<Button type='text' content='test' onPress={onPress} loading />)
|
|
||||||
|
|
||||||
expect(getByTestId('base')).toBeDisabled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('from default to disabled', () => {
|
|
||||||
const onPress = jest.fn()
|
|
||||||
const { getByTestId, rerender } = render(
|
|
||||||
<Button type='text' content='test' onPress={onPress} />
|
|
||||||
)
|
|
||||||
rerender(<Button type='text' content='test' onPress={onPress} disabled />)
|
|
||||||
|
|
||||||
expect(getByTestId('base')).toBeDisabled()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,15 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import { cleanup, render } from '@testing-library/react-native/pure'
|
|
||||||
|
|
||||||
import MenuHeader from '@components/Menu/Header'
|
|
||||||
|
|
||||||
describe('Testing component menu header', () => {
|
|
||||||
afterEach(cleanup)
|
|
||||||
|
|
||||||
it('with text only', () => {
|
|
||||||
const { getByText, toJSON } = render(<MenuHeader heading='test' />)
|
|
||||||
|
|
||||||
getByText('test')
|
|
||||||
expect(toJSON()).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,50 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import { toBeDisabled } from '@testing-library/jest-native'
|
|
||||||
import { cleanup, fireEvent, render } from '@testing-library/react-native'
|
|
||||||
|
|
||||||
import MenuRow from '@components/Menu/Row'
|
|
||||||
|
|
||||||
expect.extend({ toBeDisabled })
|
|
||||||
|
|
||||||
describe('Testing component menu row', () => {
|
|
||||||
afterEach(cleanup)
|
|
||||||
|
|
||||||
it('with title only', () => {
|
|
||||||
const { getByText, toJSON } = render(<MenuRow title='test title' />)
|
|
||||||
getByText('test title')
|
|
||||||
expect(toJSON()).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('with title and content', () => {
|
|
||||||
const { getByText, toJSON } = render(
|
|
||||||
<MenuRow title='test title' content='test content' />
|
|
||||||
)
|
|
||||||
getByText('test title')
|
|
||||||
getByText('test content')
|
|
||||||
expect(toJSON()).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('on press event', () => {
|
|
||||||
const onPress = jest.fn()
|
|
||||||
const { getByTestId, toJSON } = render(
|
|
||||||
<MenuRow title='test' onPress={onPress} />
|
|
||||||
)
|
|
||||||
fireEvent.press(getByTestId('base'))
|
|
||||||
|
|
||||||
expect(onPress).toHaveBeenCalled()
|
|
||||||
expect(onPress).toHaveBeenCalledTimes(1)
|
|
||||||
expect(toJSON()).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('loading state', () => {
|
|
||||||
const onPress = jest.fn()
|
|
||||||
const { getByTestId, toJSON } = render(
|
|
||||||
<MenuRow title='test' loading onPress={onPress} />
|
|
||||||
)
|
|
||||||
fireEvent.press(getByTestId('base'))
|
|
||||||
|
|
||||||
expect(onPress).toHaveBeenCalledTimes(0)
|
|
||||||
expect(getByTestId('base')).toBeDisabled()
|
|
||||||
expect(toJSON()).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,30 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Testing component menu header with text only 1`] = `
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"paddingBottom": 8,
|
|
||||||
"paddingLeft": 16,
|
|
||||||
"paddingRight": 16,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"fontSize": 14,
|
|
||||||
"fontWeight": "600",
|
|
||||||
"lineHeight": 20,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"color": "rgb(135, 135, 135)",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
test
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
`;
|
|
|
@ -1,302 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Testing component menu row loading state 1`] = `
|
|
||||||
<View
|
|
||||||
accessible={true}
|
|
||||||
focusable={true}
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"minHeight": 50,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
testID="base"
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"flex": 1,
|
|
||||||
"flexDirection": "row",
|
|
||||||
"paddingLeft": 16,
|
|
||||||
"paddingRight": 16,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"flex": 2,
|
|
||||||
"flexDirection": "row",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"flex": 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
numberOfLines={1}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"fontSize": 16,
|
|
||||||
"lineHeight": 22,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"color": "rgb(18, 18, 18)",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
test
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Testing component menu row on press event 1`] = `
|
|
||||||
<View
|
|
||||||
accessible={true}
|
|
||||||
focusable={true}
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"minHeight": 50,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
testID="base"
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"flex": 1,
|
|
||||||
"flexDirection": "row",
|
|
||||||
"paddingLeft": 16,
|
|
||||||
"paddingRight": 16,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"flex": 2,
|
|
||||||
"flexDirection": "row",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"flex": 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
numberOfLines={1}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"fontSize": 16,
|
|
||||||
"lineHeight": 22,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"color": "rgb(18, 18, 18)",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
test
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Testing component menu row with title and content 1`] = `
|
|
||||||
<View
|
|
||||||
accessible={true}
|
|
||||||
focusable={true}
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"minHeight": 50,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
testID="base"
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"flex": 1,
|
|
||||||
"flexDirection": "row",
|
|
||||||
"paddingLeft": 16,
|
|
||||||
"paddingRight": 16,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"flex": 2,
|
|
||||||
"flexDirection": "row",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"flex": 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
numberOfLines={1}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"fontSize": 16,
|
|
||||||
"lineHeight": 22,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"color": "rgb(18, 18, 18)",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
test title
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"flex": 1,
|
|
||||||
"flexDirection": "row",
|
|
||||||
"justifyContent": "flex-end",
|
|
||||||
"marginLeft": 16,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
numberOfLines={1}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"fontSize": 16,
|
|
||||||
"lineHeight": 22,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"color": "rgb(135, 135, 135)",
|
|
||||||
"opacity": 1,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
test content
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Testing component menu row with title only 1`] = `
|
|
||||||
<View
|
|
||||||
accessible={true}
|
|
||||||
focusable={true}
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"minHeight": 50,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
testID="base"
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"flex": 1,
|
|
||||||
"flexDirection": "row",
|
|
||||||
"paddingLeft": 16,
|
|
||||||
"paddingRight": 16,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"flex": 2,
|
|
||||||
"flexDirection": "row",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"flex": 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
numberOfLines={1}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"fontSize": 16,
|
|
||||||
"lineHeight": 22,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"color": "rgb(18, 18, 18)",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
test title
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
`;
|
|
|
@ -1,59 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import {
|
|
||||||
toBeDisabled,
|
|
||||||
toContainElement,
|
|
||||||
toHaveStyle,
|
|
||||||
toHaveTextContent
|
|
||||||
} from '@testing-library/jest-native'
|
|
||||||
import { cleanup, render } from '@testing-library/react-native/pure'
|
|
||||||
|
|
||||||
import Card from '@components/Timelines/Timeline/Shared/Card'
|
|
||||||
|
|
||||||
expect.extend({
|
|
||||||
toBeDisabled,
|
|
||||||
toContainElement,
|
|
||||||
toHaveStyle,
|
|
||||||
toHaveTextContent
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Testing component timeline card', () => {
|
|
||||||
afterEach(cleanup)
|
|
||||||
|
|
||||||
it('with text only', () => {
|
|
||||||
const { getByTestId, queryByTestId, toJSON } = render(
|
|
||||||
<Card
|
|
||||||
card={{
|
|
||||||
url: 'http://example.com',
|
|
||||||
title: 'Title'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(queryByTestId('image')).toBeNull()
|
|
||||||
expect(getByTestId('base')).toContainElement(getByTestId('title'))
|
|
||||||
expect(queryByTestId('description')).toBeNull()
|
|
||||||
|
|
||||||
expect(getByTestId('title')).toHaveTextContent('Title')
|
|
||||||
expect(toJSON()).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('with text and description', () => {
|
|
||||||
const { getByTestId, queryByTestId, toJSON } = render(
|
|
||||||
<Card
|
|
||||||
card={{
|
|
||||||
url: 'http://example.com',
|
|
||||||
title: 'Title',
|
|
||||||
description: 'Description'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(queryByTestId('image')).toBeNull()
|
|
||||||
expect(getByTestId('base')).toContainElement(getByTestId('title'))
|
|
||||||
expect(getByTestId('base')).toContainElement(getByTestId('description'))
|
|
||||||
|
|
||||||
expect(getByTestId('title')).toHaveTextContent('Title')
|
|
||||||
expect(getByTestId('description')).toHaveTextContent('Description')
|
|
||||||
expect(toJSON()).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,155 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Testing component timeline card with text and description 1`] = `
|
|
||||||
<View
|
|
||||||
accessible={true}
|
|
||||||
focusable={true}
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"borderRadius": 6,
|
|
||||||
"borderWidth": 0.5,
|
|
||||||
"flex": 1,
|
|
||||||
"flexDirection": "row",
|
|
||||||
"height": 104,
|
|
||||||
"marginTop": 16,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"borderColor": "rgba(18, 18, 18, 0.3)",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="base"
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"flex": 1,
|
|
||||||
"padding": 8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
numberOfLines={2}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"fontWeight": "600",
|
|
||||||
"marginBottom": 4,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"color": "rgb(18, 18, 18)",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="title"
|
|
||||||
>
|
|
||||||
Title
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
numberOfLines={1}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"marginBottom": 4,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"color": "rgb(18, 18, 18)",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="description"
|
|
||||||
>
|
|
||||||
Description
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
numberOfLines={1}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"color": "rgb(135, 135, 135)",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
http://example.com
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Testing component timeline card with text only 1`] = `
|
|
||||||
<View
|
|
||||||
accessible={true}
|
|
||||||
focusable={true}
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"borderRadius": 6,
|
|
||||||
"borderWidth": 0.5,
|
|
||||||
"flex": 1,
|
|
||||||
"flexDirection": "row",
|
|
||||||
"height": 104,
|
|
||||||
"marginTop": 16,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"borderColor": "rgba(18, 18, 18, 0.3)",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="base"
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"flex": 1,
|
|
||||||
"padding": 8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
numberOfLines={2}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"fontWeight": "600",
|
|
||||||
"marginBottom": 4,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"color": "rgb(18, 18, 18)",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="title"
|
|
||||||
>
|
|
||||||
Title
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
numberOfLines={1}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"color": "rgb(135, 135, 135)",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
http://example.com
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
`;
|
|
|
@ -1,474 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Testing component button static button apply custom styling 1`] = `
|
|
||||||
<View>
|
|
||||||
<View
|
|
||||||
accessible={true}
|
|
||||||
focusable={true}
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"borderRadius": 100,
|
|
||||||
"flexDirection": "row",
|
|
||||||
"justifyContent": "center",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "rgb(250, 250, 250)",
|
|
||||||
"borderColor": "rgb(18, 18, 18)",
|
|
||||||
"borderWidth": 1,
|
|
||||||
"paddingHorizontal": 16,
|
|
||||||
"paddingVertical": 8,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "black",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="base"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"color": "rgb(18, 18, 18)",
|
|
||||||
"fontSize": 16,
|
|
||||||
"fontWeight": undefined,
|
|
||||||
"opacity": 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
testID="text"
|
|
||||||
>
|
|
||||||
test
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Testing component button static button disabled state 1`] = `
|
|
||||||
<View>
|
|
||||||
<View
|
|
||||||
accessible={true}
|
|
||||||
focusable={true}
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"borderRadius": 100,
|
|
||||||
"flexDirection": "row",
|
|
||||||
"justifyContent": "center",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "rgb(250, 250, 250)",
|
|
||||||
"borderColor": "rgb(135, 135, 135)",
|
|
||||||
"borderWidth": 1,
|
|
||||||
"paddingHorizontal": 16,
|
|
||||||
"paddingVertical": 8,
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="base"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"color": "rgb(135, 135, 135)",
|
|
||||||
"fontSize": 16,
|
|
||||||
"fontWeight": undefined,
|
|
||||||
"opacity": 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
testID="text"
|
|
||||||
>
|
|
||||||
test
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Testing component button static button loading state 1`] = `
|
|
||||||
<View>
|
|
||||||
<View
|
|
||||||
accessible={true}
|
|
||||||
focusable={true}
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"borderRadius": 100,
|
|
||||||
"flexDirection": "row",
|
|
||||||
"justifyContent": "center",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "rgb(250, 250, 250)",
|
|
||||||
"borderColor": "rgb(135, 135, 135)",
|
|
||||||
"borderWidth": 1,
|
|
||||||
"paddingHorizontal": 16,
|
|
||||||
"paddingVertical": 8,
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="base"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"color": "rgb(18, 18, 18)",
|
|
||||||
"fontSize": 16,
|
|
||||||
"fontWeight": undefined,
|
|
||||||
"opacity": 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
testID="text"
|
|
||||||
>
|
|
||||||
test
|
|
||||||
</Text>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"position": "absolute",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"height": 16,
|
|
||||||
"justifyContent": "center",
|
|
||||||
"opacity": 1,
|
|
||||||
"transform": Array [
|
|
||||||
Object {
|
|
||||||
"rotate": "0deg",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"width": 16,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "rgb(135, 135, 135)",
|
|
||||||
"borderRadius": 2,
|
|
||||||
"height": 4,
|
|
||||||
"position": "absolute",
|
|
||||||
"transform": Array [
|
|
||||||
Object {
|
|
||||||
"rotate": "73.27536734311887deg",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"translateY": -6,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"scale": 0.7,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"width": 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "rgb(135, 135, 135)",
|
|
||||||
"borderRadius": 2,
|
|
||||||
"height": 4,
|
|
||||||
"position": "absolute",
|
|
||||||
"transform": Array [
|
|
||||||
Object {
|
|
||||||
"rotate": "46.49829517703514deg",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"translateY": -6,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"scale": 0.8008696779414123,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"width": 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "rgb(135, 135, 135)",
|
|
||||||
"borderRadius": 2,
|
|
||||||
"height": 4,
|
|
||||||
"position": "absolute",
|
|
||||||
"transform": Array [
|
|
||||||
Object {
|
|
||||||
"rotate": "25.743213498935145deg",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"translateY": -6,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"scale": 0.8875624559768125,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"width": 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "rgb(135, 135, 135)",
|
|
||||||
"borderRadius": 2,
|
|
||||||
"height": 4,
|
|
||||||
"position": "absolute",
|
|
||||||
"transform": Array [
|
|
||||||
Object {
|
|
||||||
"rotate": "11.201058030774364deg",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"translateY": -6,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"scale": 0.9510040862404615,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"width": 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "rgb(135, 135, 135)",
|
|
||||||
"borderRadius": 2,
|
|
||||||
"height": 4,
|
|
||||||
"position": "absolute",
|
|
||||||
"transform": Array [
|
|
||||||
Object {
|
|
||||||
"rotate": "2.731234791722257deg",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"translateY": -6,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"scale": 0.9881665278710133,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"width": 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "rgb(135, 135, 135)",
|
|
||||||
"borderRadius": 2,
|
|
||||||
"height": 4,
|
|
||||||
"position": "absolute",
|
|
||||||
"transform": Array [
|
|
||||||
Object {
|
|
||||||
"rotate": "0deg",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"translateY": -6,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"scale": 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"width": 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Testing component button static button with icon only 1`] = `
|
|
||||||
<View>
|
|
||||||
<View
|
|
||||||
accessible={true}
|
|
||||||
focusable={true}
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"borderRadius": 100,
|
|
||||||
"flexDirection": "row",
|
|
||||||
"justifyContent": "center",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "rgb(250, 250, 250)",
|
|
||||||
"borderColor": "rgb(18, 18, 18)",
|
|
||||||
"borderWidth": 1,
|
|
||||||
"paddingHorizontal": 16,
|
|
||||||
"paddingVertical": 8,
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="base"
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"opacity": 1,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"height": 16,
|
|
||||||
"justifyContent": "center",
|
|
||||||
"width": 16,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<RNSVGSvgView
|
|
||||||
align="xMidYMid"
|
|
||||||
bbHeight={16}
|
|
||||||
bbWidth={16}
|
|
||||||
className=""
|
|
||||||
color={4279374354}
|
|
||||||
focusable={false}
|
|
||||||
height={16}
|
|
||||||
meetOrSlice={0}
|
|
||||||
minX={0}
|
|
||||||
minY={0}
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "transparent",
|
|
||||||
"borderWidth": 0,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"flex": 0,
|
|
||||||
"height": 16,
|
|
||||||
"width": 16,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
tintColor={4279374354}
|
|
||||||
vbHeight={24}
|
|
||||||
vbWidth={24}
|
|
||||||
width={16}
|
|
||||||
>
|
|
||||||
<RNSVGGroup
|
|
||||||
propList={
|
|
||||||
Array [
|
|
||||||
"stroke",
|
|
||||||
"strokeWidth",
|
|
||||||
"strokeLinecap",
|
|
||||||
"strokeLinejoin",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
stroke={
|
|
||||||
Array [
|
|
||||||
2,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
strokeLinecap={1}
|
|
||||||
strokeLinejoin={1}
|
|
||||||
strokeWidth={2}
|
|
||||||
>
|
|
||||||
<RNSVGPath
|
|
||||||
d="M18 6L6 18M6 6l12 12"
|
|
||||||
/>
|
|
||||||
</RNSVGGroup>
|
|
||||||
</RNSVGSvgView>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Testing component button static button with text only 1`] = `
|
|
||||||
<View>
|
|
||||||
<View
|
|
||||||
accessible={true}
|
|
||||||
focusable={true}
|
|
||||||
onBlur={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onFocus={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"borderRadius": 100,
|
|
||||||
"flexDirection": "row",
|
|
||||||
"justifyContent": "center",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "rgb(250, 250, 250)",
|
|
||||||
"borderColor": "rgb(18, 18, 18)",
|
|
||||||
"borderWidth": 1,
|
|
||||||
"paddingHorizontal": 16,
|
|
||||||
"paddingVertical": 8,
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="base"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"color": "rgb(18, 18, 18)",
|
|
||||||
"fontSize": 16,
|
|
||||||
"fontWeight": undefined,
|
|
||||||
"opacity": 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
testID="text"
|
|
||||||
>
|
|
||||||
Test Button
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
`;
|
|
|
@ -1,6 +1,7 @@
|
||||||
apply plugin: "com.android.application"
|
apply plugin: "com.android.application"
|
||||||
|
|
||||||
import com.android.build.OutputFile
|
import com.android.build.OutputFile
|
||||||
|
import org.apache.tools.ant.taskdefs.condition.Os
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
||||||
|
@ -79,7 +80,6 @@ import com.android.build.OutputFile
|
||||||
|
|
||||||
project.ext.react = [
|
project.ext.react = [
|
||||||
enableHermes: (findProperty('expo.jsEngine') ?: "jsc") == "hermes",
|
enableHermes: (findProperty('expo.jsEngine') ?: "jsc") == "hermes",
|
||||||
cliPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute().text.trim(), "../cli.js")
|
|
||||||
]
|
]
|
||||||
|
|
||||||
apply from: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute().text.trim(), "../react.gradle")
|
apply from: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute().text.trim(), "../react.gradle")
|
||||||
|
@ -121,6 +121,14 @@ def jscFlavor = 'org.webkit:android-jsc:+'
|
||||||
*/
|
*/
|
||||||
def enableHermes = project.ext.react.get("enableHermes", true);
|
def enableHermes = project.ext.react.get("enableHermes", true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Architectures to build native code for.
|
||||||
|
*/
|
||||||
|
def reactNativeArchitectures() {
|
||||||
|
def value = project.getProperties().get("reactNativeArchitectures")
|
||||||
|
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
ndkVersion rootProject.ext.ndkVersion
|
ndkVersion rootProject.ext.ndkVersion
|
||||||
|
|
||||||
|
@ -137,17 +145,86 @@ android {
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 50
|
versionCode 50
|
||||||
versionName "0.2"
|
versionName "0.2"
|
||||||
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
||||||
|
if (isNewArchitectureEnabled()) {
|
||||||
|
// We configure the NDK build only if you decide to opt-in for the New Architecture.
|
||||||
|
externalNativeBuild {
|
||||||
|
ndkBuild {
|
||||||
|
arguments "APP_PLATFORM=android-21",
|
||||||
|
"APP_STL=c++_shared",
|
||||||
|
"NDK_TOOLCHAIN_VERSION=clang",
|
||||||
|
"GENERATED_SRC_DIR=$buildDir/generated/source",
|
||||||
|
"PROJECT_BUILD_DIR=$buildDir",
|
||||||
|
"REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
|
||||||
|
"REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build"
|
||||||
|
cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1"
|
||||||
|
cppFlags "-std=c++17"
|
||||||
|
// Make sure this target name is the same you specify inside the
|
||||||
|
// src/main/jni/Android.mk file for the `LOCAL_MODULE` variable.
|
||||||
|
targets "tooot_appmodules"
|
||||||
|
// Fix for windows limit on number of character in file paths and in command lines
|
||||||
|
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||||
|
arguments "NDK_OUT=${rootProject.projectDir.getParent()}\\.cxx",
|
||||||
|
"NDK_APP_SHORT_COMMANDS=true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!enableSeparateBuildPerCPUArchitecture) {
|
||||||
|
ndk {
|
||||||
|
abiFilters (*reactNativeArchitectures())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
manifestPlaceholders = [
|
manifestPlaceholders = [
|
||||||
expoSDK: project.hasProperty('expoSDK') ? project.property('expoSDK') : "",
|
expoSDK: project.hasProperty('expoSDK') ? project.property('expoSDK') : "",
|
||||||
releaseChannel: project.hasProperty('releaseChannel') ? project.property('releaseChannel') : "default"
|
releaseChannel: project.hasProperty('releaseChannel') ? project.property('releaseChannel') : "default"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
if (isNewArchitectureEnabled()) {
|
||||||
|
// We configure the NDK build only if you decide to opt-in for the New Architecture.
|
||||||
|
externalNativeBuild {
|
||||||
|
ndkBuild {
|
||||||
|
path "$projectDir/src/main/jni/Android.mk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def reactAndroidProjectDir = project(':ReactAndroid').projectDir
|
||||||
|
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
|
||||||
|
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
|
||||||
|
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
|
||||||
|
into("$buildDir/react-ndk/exported")
|
||||||
|
}
|
||||||
|
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
|
||||||
|
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
|
||||||
|
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
|
||||||
|
into("$buildDir/react-ndk/exported")
|
||||||
|
}
|
||||||
|
afterEvaluate {
|
||||||
|
// If you wish to add a custom TurboModule or component locally,
|
||||||
|
// you should uncomment this line.
|
||||||
|
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
|
||||||
|
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
|
||||||
|
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
|
||||||
|
// Due to a bug inside AGP, we have to explicitly set a dependency
|
||||||
|
// between configureNdkBuild* tasks and the preBuild tasks.
|
||||||
|
// This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
|
||||||
|
configureNdkBuildRelease.dependsOn(preReleaseBuild)
|
||||||
|
configureNdkBuildDebug.dependsOn(preDebugBuild)
|
||||||
|
reactNativeArchitectures().each { architecture ->
|
||||||
|
tasks.findByName("configureNdkBuildDebug[${architecture}]")?.configure {
|
||||||
|
dependsOn("preDebugBuild")
|
||||||
|
}
|
||||||
|
tasks.findByName("configureNdkBuildRelease[${architecture}]")?.configure {
|
||||||
|
dependsOn("preReleaseBuild")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
splits {
|
splits {
|
||||||
abi {
|
abi {
|
||||||
reset()
|
reset()
|
||||||
enable enableSeparateBuildPerCPUArchitecture
|
enable enableSeparateBuildPerCPUArchitecture
|
||||||
universalApk false // If true, also generate a universal APK
|
universalApk false // If true, also generate a universal APK
|
||||||
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
include (*reactNativeArchitectures())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
|
@ -241,14 +318,34 @@ dependencies {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isNewArchitectureEnabled()) {
|
||||||
|
// If new architecture is enabled, we let you build RN from source
|
||||||
|
// Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
|
||||||
|
// This will be applied to all the imported transtitive dependency.
|
||||||
|
configurations.all {
|
||||||
|
resolutionStrategy.dependencySubstitution {
|
||||||
|
substitute(module("com.facebook.react:react-native"))
|
||||||
|
.using(project(":ReactAndroid")).because("On New Architecture we're building React Native from source")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Run this once to be able to run the application with BUCK
|
// Run this once to be able to run the application with BUCK
|
||||||
// puts all compile dependencies into folder libs for BUCK to use
|
// puts all compile dependencies into folder libs for BUCK to use
|
||||||
task copyDownloadableDepsToLibs(type: Copy) {
|
task copyDownloadableDepsToLibs(type: Copy) {
|
||||||
from configurations.compile
|
from configurations.implementation
|
||||||
into 'libs'
|
into 'libs'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute().text.trim(), "../native_modules.gradle");
|
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute().text.trim(), "../native_modules.gradle");
|
||||||
applyNativeModulesAppBuildGradle(project)
|
applyNativeModulesAppBuildGradle(project)
|
||||||
|
|
||||||
|
def isNewArchitectureEnabled() {
|
||||||
|
// To opt-in for the New Architecture, you can either:
|
||||||
|
// - Set `newArchEnabled` to true inside the `gradle.properties` file
|
||||||
|
// - Invoke gradle with `-newArchEnabled=true`
|
||||||
|
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
|
||||||
|
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
|
||||||
|
}
|
||||||
|
|
||||||
apply plugin: 'com.google.gms.google-services'
|
apply plugin: 'com.google.gms.google-services'
|
||||||
|
|
|
@ -7,6 +7,6 @@
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
tools:targetApi="28"
|
tools:targetApi="28"
|
||||||
tools:ignore="GoogleAppIndexingWarning">
|
tools:ignore="GoogleAppIndexingWarning">
|
||||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -19,6 +19,7 @@ import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
|
||||||
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
|
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
|
||||||
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
|
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
|
||||||
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
|
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
|
||||||
|
import com.facebook.react.ReactInstanceEventListener;
|
||||||
import com.facebook.react.ReactInstanceManager;
|
import com.facebook.react.ReactInstanceManager;
|
||||||
import com.facebook.react.bridge.ReactContext;
|
import com.facebook.react.bridge.ReactContext;
|
||||||
import com.facebook.react.modules.network.NetworkingModule;
|
import com.facebook.react.modules.network.NetworkingModule;
|
||||||
|
@ -48,7 +49,7 @@ public class ReactNativeFlipper {
|
||||||
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||||
if (reactContext == null) {
|
if (reactContext == null) {
|
||||||
reactInstanceManager.addReactInstanceEventListener(
|
reactInstanceManager.addReactInstanceEventListener(
|
||||||
new ReactInstanceManager.ReactInstanceEventListener() {
|
new ReactInstanceEventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onReactContextInitialized(ReactContext reactContext) {
|
public void onReactContextInitialized(ReactContext reactContext) {
|
||||||
reactInstanceManager.removeReactInstanceEventListener(this);
|
reactInstanceManager.removeReactInstanceEventListener(this);
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
|
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
|
||||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
||||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
||||||
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:screenOrientation="portrait">
|
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:exported="true" android:theme="@style/Theme.App.SplashScreen" android:screenOrientation="portrait" android:documentLaunchMode="never">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
@ -31,6 +31,31 @@
|
||||||
<data android:scheme="tooot"/>
|
<data android:scheme="tooot"/>
|
||||||
<data android:scheme="com.xmflsct.app.tooot"/>
|
<data android:scheme="com.xmflsct.app.tooot"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="image/*" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="text/plain" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="text/plain" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="image/*" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="video/*"/>
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -4,7 +4,6 @@ import android.os.Bundle;
|
||||||
import com.facebook.react.ReactActivity;
|
import com.facebook.react.ReactActivity;
|
||||||
import com.facebook.react.ReactActivityDelegate;
|
import com.facebook.react.ReactActivityDelegate;
|
||||||
import com.facebook.react.ReactRootView;
|
import com.facebook.react.ReactRootView;
|
||||||
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
|
|
||||||
|
|
||||||
import expo.modules.ReactActivityDelegateWrapper;
|
import expo.modules.ReactActivityDelegateWrapper;
|
||||||
|
|
||||||
|
@ -14,25 +13,33 @@ public class MainActivity extends ReactActivity {
|
||||||
super.onCreate(null);
|
super.onCreate(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of the main component registered from JavaScript.
|
* Returns the name of the main component registered from JavaScript.
|
||||||
* This is used to schedule rendering of the component.
|
* This is used to schedule rendering of the component.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected String getMainComponentName() {
|
protected String getMainComponentName() {
|
||||||
return "main";
|
return "main";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
* Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
|
||||||
return new ReactActivityDelegateWrapper(
|
* you can specify the rendered you wish to use (Fabric or the older renderer).
|
||||||
this,
|
*/
|
||||||
new ReactActivityDelegate(this, getMainComponentName()) {
|
@Override
|
||||||
@Override
|
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||||
protected ReactRootView createRootView() {
|
return new ReactActivityDelegateWrapper(this, new MainActivityDelegate(this, getMainComponentName()));
|
||||||
return new RNGestureHandlerEnabledRootView(MainActivity.this);
|
}
|
||||||
}
|
public static class MainActivityDelegate extends ReactActivityDelegate {
|
||||||
}
|
public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
|
||||||
);
|
super(activity, mainComponentName);
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
protected ReactRootView createRootView() {
|
||||||
|
ReactRootView reactRootView = new ReactRootView(getContext());
|
||||||
|
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
||||||
|
reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
|
||||||
|
return reactRootView;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.facebook.react.ReactApplication;
|
||||||
import com.facebook.react.ReactInstanceManager;
|
import com.facebook.react.ReactInstanceManager;
|
||||||
import com.facebook.react.ReactNativeHost;
|
import com.facebook.react.ReactNativeHost;
|
||||||
import com.facebook.react.ReactPackage;
|
import com.facebook.react.ReactPackage;
|
||||||
|
import com.facebook.react.config.ReactFeatureFlags;
|
||||||
import com.facebook.react.shell.MainReactPackage;
|
import com.facebook.react.shell.MainReactPackage;
|
||||||
import com.facebook.soloader.SoLoader;
|
import com.facebook.soloader.SoLoader;
|
||||||
|
|
||||||
|
@ -54,12 +55,19 @@ public class MainApplication extends Application implements ReactApplication {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReactNativeHost getReactNativeHost() {
|
public ReactNativeHost getReactNativeHost() {
|
||||||
|
// if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||||
|
// return mNewArchitectureNativeHost;
|
||||||
|
// } else {
|
||||||
|
// return mReactNativeHost;
|
||||||
|
// }
|
||||||
return mReactNativeHost;
|
return mReactNativeHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
// If you opted-in for the New Architecture, we enable the TurboModule system
|
||||||
|
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||||
SoLoader.init(this, /* native exopackage */ false);
|
SoLoader.init(this, /* native exopackage */ false);
|
||||||
|
|
||||||
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||||
|
|
|
@ -1,13 +1,24 @@
|
||||||
|
import org.apache.tools.ant.taskdefs.condition.Os
|
||||||
|
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext {
|
ext {
|
||||||
buildToolsVersion = "30.0.2"
|
buildToolsVersion = "31.0.0"
|
||||||
minSdkVersion = 21
|
minSdkVersion = 21
|
||||||
compileSdkVersion = 30
|
compileSdkVersion = 31
|
||||||
targetSdkVersion = 30
|
targetSdkVersion = 31
|
||||||
ndkVersion = "21.4.7075529"
|
if (System.properties['os.arch'] == "aarch64") {
|
||||||
kotlinVersion = '1.5.32'
|
// For M1 Users we need to use the NDK 24 which added support for aarch64
|
||||||
|
ndkVersion = "24.0.8215888"
|
||||||
|
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||||
|
// For Android Users, we need to use NDK 23, otherwise the build will
|
||||||
|
// fail due to paths longer than the OS limit
|
||||||
|
ndkVersion = "23.1.7779620"
|
||||||
|
} else {
|
||||||
|
// Otherwise we default to the side-by-side NDK version from AGP.
|
||||||
|
ndkVersion = "21.4.7075529"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
@ -16,7 +27,9 @@ buildscript {
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.google.gms:google-services:4.3.3'
|
classpath 'com.google.gms:google-services:4.3.3'
|
||||||
classpath("com.android.tools.build:gradle:4.2.0")
|
classpath("com.android.tools.build:gradle:7.0.4")
|
||||||
|
classpath("com.facebook.react:react-native-gradle-plugin")
|
||||||
|
classpath("de.undercouch:gradle-download-task:4.1.2")
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
|
|
@ -26,7 +26,18 @@ android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
|
|
||||||
# Version of flipper SDK to use with React Native
|
# Version of flipper SDK to use with React Native
|
||||||
FLIPPER_VERSION=0.75.1
|
FLIPPER_VERSION=0.125.0
|
||||||
|
|
||||||
|
# Use this property to specify which architecture you want to build.
|
||||||
|
# You can also override it from the CLI using
|
||||||
|
# ./gradlew <task> -PreactNativeArchitectures=x86_64
|
||||||
|
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
||||||
|
# Use this property to enable support to the new architecture.
|
||||||
|
# This will allow you to use TurboModules and the Fabric render in
|
||||||
|
# your application. You should enable this flag either if you want
|
||||||
|
# to write custom TurboModules/Fabric components OR use libraries that
|
||||||
|
# are providing them.
|
||||||
|
newArchEnabled=false
|
||||||
|
|
||||||
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=4096m -XX:+HeapDumpOnOutOfMemoryError
|
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=4096m -XX:+HeapDumpOnOutOfMemoryError
|
||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
|
|
Binary file not shown.
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright 2015 the original author or authors.
|
# Copyright 2015 the original author or authors.
|
||||||
|
@ -24,60 +24,51 @@
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
app_path=$0
|
||||||
# Need this for relative symlinks.
|
# Need this for daisy-chained symlinks.
|
||||||
while [ -h "$PRG" ] ; do
|
while
|
||||||
ls=`ls -ld "$PRG"`
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
[ -h "$app_path" ]
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
do
|
||||||
PRG="$link"
|
ls=$( ls -ld "$app_path" )
|
||||||
else
|
link=${ls#*' -> '}
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
case $link in #(
|
||||||
fi
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
APP_NAME="Gradle"
|
||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=${0##*/}
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD=maximum
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "`uname`" in
|
case "$( uname )" in #(
|
||||||
CYGWIN* )
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
cygwin=true
|
Darwin* ) darwin=true ;; #(
|
||||||
;;
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
Darwin* )
|
NONSTOP* ) nonstop=true ;;
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
@ -86,9 +77,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
@ -97,7 +88,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
@ -105,79 +96,91 @@ location of your Java installation."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
case $MAX_FD in #(
|
||||||
if [ $? -eq 0 ] ; then
|
max*)
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
warn "Could not query maximum file descriptor limit"
|
||||||
fi
|
esac
|
||||||
ulimit -n $MAX_FD
|
case $MAX_FD in #(
|
||||||
if [ $? -ne 0 ] ; then
|
'' | soft) :;; #(
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
*)
|
||||||
fi
|
ulimit -n "$MAX_FD" ||
|
||||||
else
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=`expr $i + 1`
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
0) set -- ;;
|
|
||||||
1) set -- "$args0" ;;
|
|
||||||
2) set -- "$args0" "$args1" ;;
|
|
||||||
3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
save () {
|
# * args from the command line
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
# * the main class name
|
||||||
echo " "
|
# * -classpath
|
||||||
}
|
# * -D...appname settings
|
||||||
APP_ARGS=`save "$@"`
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|
|
@ -7,3 +7,9 @@ apply from: new File(["node", "--print", "require.resolve('@react-native-communi
|
||||||
applyNativeModulesSettingsGradle(settings)
|
applyNativeModulesSettingsGradle(settings)
|
||||||
|
|
||||||
include ':app'
|
include ':app'
|
||||||
|
|
||||||
|
includeBuild('../node_modules/react-native-gradle-plugin')
|
||||||
|
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
|
||||||
|
include(":ReactAndroid")
|
||||||
|
project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid')
|
||||||
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ export default (): ExpoConfig => ({
|
||||||
android: {
|
android: {
|
||||||
package: 'com.xmflsct.app.tooot',
|
package: 'com.xmflsct.app.tooot',
|
||||||
googleServicesFile: './configs/google-services.json',
|
googleServicesFile: './configs/google-services.json',
|
||||||
permissions: ['CAMERA', 'VIBRATE']
|
permissions: ['CAMERA', 'VIBRATE'],
|
||||||
|
blockedPermissions: ['USE_BIOMETRIC', 'USE_FINGERPRINT']
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -224,7 +224,7 @@ lane :build do
|
||||||
puts("Release #{GITHUB_RELEASE} exists. Continue with building React Native only.")
|
puts("Release #{GITHUB_RELEASE} exists. Continue with building React Native only.")
|
||||||
else
|
else
|
||||||
puts("Release #{GITHUB_RELEASE} does not exist. Create new release as well as new native build.")
|
puts("Release #{GITHUB_RELEASE} does not exist. Create new release as well as new native build.")
|
||||||
cocoapods(clean_install: true, podfile: "./ios/Podfile", deployment: true)
|
cocoapods(clean_install: true, podfile: "./ios/Podfile")
|
||||||
build_ios
|
build_ios
|
||||||
build_android
|
build_android
|
||||||
case ENVIRONMENT
|
case ENVIRONMENT
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
storage_mode("git")
|
storage_mode "git"
|
||||||
|
app_identifier ["com.xmflsct.app.tooot", "com.xmflsct.app.tooot.ShareExtension"]
|
||||||
|
|
31
ios/Podfile
31
ios/Podfile
|
@ -3,17 +3,32 @@ require File.join(File.dirname(`node --print "require.resolve('react-native/pack
|
||||||
require File.join(File.dirname(`node --print "require.resolve('@react-native-community/cli-platform-ios/package.json')"`), "native_modules")
|
require File.join(File.dirname(`node --print "require.resolve('@react-native-community/cli-platform-ios/package.json')"`), "native_modules")
|
||||||
|
|
||||||
platform :ios, '12.0'
|
platform :ios, '12.0'
|
||||||
|
install! 'cocoapods', :deterministic_uuids => false
|
||||||
|
|
||||||
require 'json'
|
require 'json'
|
||||||
podfile_properties = JSON.parse(File.read('./Podfile.properties.json')) rescue {}
|
podfile_properties = JSON.parse(File.read('./Podfile.properties.json')) rescue {}
|
||||||
|
|
||||||
target 'tooot' do
|
target 'tooot' do
|
||||||
use_expo_modules!
|
use_expo_modules!
|
||||||
|
post_integrate do |installer|
|
||||||
|
begin
|
||||||
|
expo_patch_react_imports!(installer)
|
||||||
|
rescue => e
|
||||||
|
Pod::UI.warn e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
config = use_native_modules!
|
config = use_native_modules!
|
||||||
|
|
||||||
|
# Flags change depending on the env values.
|
||||||
|
flags = get_default_flags()
|
||||||
|
|
||||||
use_react_native!(
|
use_react_native!(
|
||||||
:path => config[:reactNativePath],
|
:path => config[:reactNativePath],
|
||||||
:hermes_enabled => podfile_properties['expo.jsEngine'] == 'hermes'
|
:hermes_enabled => podfile_properties['expo.jsEngine'] == 'hermes',
|
||||||
|
:fabric_enabled => flags[:fabric_enabled],
|
||||||
|
# An absolute path to your application root.
|
||||||
|
:app_path => "#{Pod::Config.instance.installation_root}/.."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Enables Flipper.
|
# Enables Flipper.
|
||||||
|
@ -24,5 +39,19 @@ target 'tooot' do
|
||||||
|
|
||||||
post_install do |installer|
|
post_install do |installer|
|
||||||
react_native_post_install(installer)
|
react_native_post_install(installer)
|
||||||
|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
target.build_configurations.each do |config|
|
||||||
|
config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'NO'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
target 'ShareExtension' do
|
||||||
|
use_react_native!(
|
||||||
|
:hermes_enabled => podfile_properties['expo.jsEngine'] == 'hermes'
|
||||||
|
)
|
||||||
|
|
||||||
|
pod 'RNShareMenu', :path => '../node_modules/react-native-share-menu'
|
||||||
|
end
|
||||||
|
|
862
ios/Podfile.lock
862
ios/Podfile.lock
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,70 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionActivationRule</key>
|
||||||
|
<string>TRUEPREDICATE</string>
|
||||||
|
</dict>
|
||||||
|
<key>NSExtensionMainStoryboard</key>
|
||||||
|
<string>MainInterface</string>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.share-services</string>
|
||||||
|
</dict>
|
||||||
|
<key>HostAppBundleIdentifier</key>
|
||||||
|
<string>com.xmflsct.app.tooot</string>
|
||||||
|
<key>HostAppURLScheme</key>
|
||||||
|
<string>tooot-share://</string>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionActivationRule</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
|
||||||
|
<integer>4</integer>
|
||||||
|
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>NSExtensionActivationSupportsText</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>NSExtensionPrincipalClass</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).ShareViewController</string>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.share-services</string>
|
||||||
|
</dict>
|
||||||
|
|
||||||
|
<key>ReactShareViewBackgroundColor</key>
|
||||||
|
<dict>
|
||||||
|
<key>Red</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>Green</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>Blue</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>Alpha</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>Transparent</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSExceptionDomains</key>
|
||||||
|
<dict>
|
||||||
|
<key>localhost</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,8 @@
|
||||||
|
//
|
||||||
|
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <React/RCTBridge.h>
|
||||||
|
#import <React/RCTBundleURLProvider.h>
|
||||||
|
#import <React/RCTBridgeDelegate.h>
|
||||||
|
#import <React/RCTRootView.h>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.com.xmflsct.app.tooot</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.security.personal-information.photos-library</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -7,10 +7,10 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
|
|
||||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
|
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
|
||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||||
|
34A37A6C820725DC6DDAA0EE /* libPods-ShareExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B3640FCDF7C4396A68A74D1 /* libPods-ShareExtension.a */; };
|
||||||
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
|
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
|
||||||
5E36538325C9B8BD009F93EE /* RootViewColor.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E36538225C9B8BD009F93EE /* RootViewColor.xcassets */; };
|
5E36538325C9B8BD009F93EE /* RootViewColor.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E36538225C9B8BD009F93EE /* RootViewColor.xcassets */; };
|
||||||
5EE088C926297820007E5FEC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5EE088CB26297820007E5FEC /* InfoPlist.strings */; };
|
5EE088C926297820007E5FEC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5EE088CB26297820007E5FEC /* InfoPlist.strings */; };
|
||||||
|
@ -19,17 +19,46 @@
|
||||||
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
|
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
|
||||||
DA8B5B7F0DED488CAC0FF169 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B96B72E5384D44A7B240B27E /* GoogleService-Info.plist */; };
|
DA8B5B7F0DED488CAC0FF169 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B96B72E5384D44A7B240B27E /* GoogleService-Info.plist */; };
|
||||||
E3BC22F5F8ABE515E14CF199 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D878F932AF7A9974E06E461 /* ExpoModulesProvider.swift */; };
|
E3BC22F5F8ABE515E14CF199 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D878F932AF7A9974E06E461 /* ExpoModulesProvider.swift */; };
|
||||||
|
E613A80B28282A01003C97D6 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = E613A80A28282A01003C97D6 /* AppDelegate.mm */; };
|
||||||
|
E633A42B281EAEAB000E540F /* ShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E633A420281EAEAB000E540F /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
E633A430281EAF38000E540F /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E633A42F281EAF38000E540F /* ShareViewController.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
E633A428281EAEAB000E540F /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = E633A41F281EAEAB000E540F;
|
||||||
|
remoteInfo = ShareExtension;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
E633A42A281EAEAB000E540F /* Embed App Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
E633A42B281EAEAB000E540F /* ShareExtension.appex in Embed App Extensions */,
|
||||||
|
);
|
||||||
|
name = "Embed App Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
|
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
|
||||||
13B07F961A680F5B00A75B9A /* tooot.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tooot.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
13B07F961A680F5B00A75B9A /* tooot.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tooot.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = tooot/AppDelegate.h; sourceTree = "<group>"; };
|
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = tooot/AppDelegate.h; sourceTree = "<group>"; };
|
||||||
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = tooot/AppDelegate.m; sourceTree = "<group>"; };
|
|
||||||
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
|
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
|
||||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = tooot/Images.xcassets; sourceTree = "<group>"; };
|
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = tooot/Images.xcassets; sourceTree = "<group>"; };
|
||||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = tooot/Info.plist; sourceTree = "<group>"; };
|
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = tooot/Info.plist; sourceTree = "<group>"; };
|
||||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = tooot/main.m; sourceTree = "<group>"; };
|
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = tooot/main.m; sourceTree = "<group>"; };
|
||||||
|
1B3640FCDF7C4396A68A74D1 /* libPods-ShareExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ShareExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
312CB8F38010C3E0D27A8663 /* Pods-ShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.release.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
49AC0972A79258360BEDD73B /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-tooot.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-tooot.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-tooot.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-tooot.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
5E36538225C9B8BD009F93EE /* RootViewColor.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = RootViewColor.xcassets; path = tooot/RootViewColor.xcassets; sourceTree = "<group>"; };
|
5E36538225C9B8BD009F93EE /* RootViewColor.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = RootViewColor.xcassets; path = tooot/RootViewColor.xcassets; sourceTree = "<group>"; };
|
||||||
5EE088CA26297820007E5FEC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
5EE088CA26297820007E5FEC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
|
@ -42,6 +71,11 @@
|
||||||
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = tooot/SplashScreen.storyboard; sourceTree = "<group>"; };
|
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = tooot/SplashScreen.storyboard; sourceTree = "<group>"; };
|
||||||
B96B72E5384D44A7B240B27E /* GoogleService-Info.plist */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "tooot/GoogleService-Info.plist"; sourceTree = "<group>"; };
|
B96B72E5384D44A7B240B27E /* GoogleService-Info.plist */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "tooot/GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||||
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
|
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
|
||||||
|
E613A80A28282A01003C97D6 /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = tooot/AppDelegate.mm; sourceTree = "<group>"; };
|
||||||
|
E633A420281EAEAB000E540F /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
E633A427281EAEAB000E540F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
E633A42F281EAF38000E540F /* ShareViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShareViewController.swift; path = "../../node_modules/react-native-share-menu/ios/ShareViewController.swift"; sourceTree = "<group>"; };
|
||||||
|
E633A431281EB55C000E540F /* ShareExtension-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ShareExtension-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||||
ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
|
ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
@ -55,16 +89,24 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
E633A41D281EAEAB000E540F /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
34A37A6C820725DC6DDAA0EE /* libPods-ShareExtension.a in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
13B07FAE1A68108700A75B9A /* tooot */ = {
|
13B07FAE1A68108700A75B9A /* tooot */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E613A80A28282A01003C97D6 /* AppDelegate.mm */,
|
||||||
BB2F792B24A3F905000567C9 /* Supporting */,
|
BB2F792B24A3F905000567C9 /* Supporting */,
|
||||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
|
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
|
||||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||||
13B07FB01A68108700A75B9A /* AppDelegate.m */,
|
|
||||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||||
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
|
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
|
||||||
|
@ -91,6 +133,7 @@
|
||||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||||
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
|
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
|
||||||
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-tooot.a */,
|
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-tooot.a */,
|
||||||
|
1B3640FCDF7C4396A68A74D1 /* libPods-ShareExtension.a */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -116,6 +159,7 @@
|
||||||
5EE44DD52600124E00A9BCED /* File.swift */,
|
5EE44DD52600124E00A9BCED /* File.swift */,
|
||||||
13B07FAE1A68108700A75B9A /* tooot */,
|
13B07FAE1A68108700A75B9A /* tooot */,
|
||||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||||
|
E633A421281EAEAB000E540F /* ShareExtension */,
|
||||||
83CBBA001A601CBA00E9B192 /* Products */,
|
83CBBA001A601CBA00E9B192 /* Products */,
|
||||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||||
D65327D7A22EEC0BE12398D9 /* Pods */,
|
D65327D7A22EEC0BE12398D9 /* Pods */,
|
||||||
|
@ -131,6 +175,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
13B07F961A680F5B00A75B9A /* tooot.app */,
|
13B07F961A680F5B00A75B9A /* tooot.app */,
|
||||||
|
E633A420281EAEAB000E540F /* ShareExtension.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -149,10 +194,22 @@
|
||||||
children = (
|
children = (
|
||||||
6C2E3173556A471DD304B334 /* Pods-tooot.debug.xcconfig */,
|
6C2E3173556A471DD304B334 /* Pods-tooot.debug.xcconfig */,
|
||||||
7A4D352CD337FB3A3BF06240 /* Pods-tooot.release.xcconfig */,
|
7A4D352CD337FB3A3BF06240 /* Pods-tooot.release.xcconfig */,
|
||||||
|
49AC0972A79258360BEDD73B /* Pods-ShareExtension.debug.xcconfig */,
|
||||||
|
312CB8F38010C3E0D27A8663 /* Pods-ShareExtension.release.xcconfig */,
|
||||||
);
|
);
|
||||||
path = Pods;
|
path = Pods;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
E633A421281EAEAB000E540F /* ShareExtension */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
E633A42F281EAF38000E540F /* ShareViewController.swift */,
|
||||||
|
E633A427281EAEAB000E540F /* Info.plist */,
|
||||||
|
E633A431281EB55C000E540F /* ShareExtension-Bridging-Header.h */,
|
||||||
|
);
|
||||||
|
path = ShareExtension;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
@ -168,22 +225,44 @@
|
||||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||||
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
|
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
|
||||||
49D30A53634620EF2A5C6692 /* [CP] Embed Pods Frameworks */,
|
49D30A53634620EF2A5C6692 /* [CP] Embed Pods Frameworks */,
|
||||||
|
E633A42A281EAEAB000E540F /* Embed App Extensions */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
|
E633A429281EAEAB000E540F /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = tooot;
|
name = tooot;
|
||||||
productName = tooot;
|
productName = tooot;
|
||||||
productReference = 13B07F961A680F5B00A75B9A /* tooot.app */;
|
productReference = 13B07F961A680F5B00A75B9A /* tooot.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
|
E633A41F281EAEAB000E540F /* ShareExtension */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = E633A42E281EAEAB000E540F /* Build configuration list for PBXNativeTarget "ShareExtension" */;
|
||||||
|
buildPhases = (
|
||||||
|
8E32C2F04B8226F2A839525E /* [CP] Check Pods Manifest.lock */,
|
||||||
|
E633A41C281EAEAB000E540F /* Sources */,
|
||||||
|
E633A41D281EAEAB000E540F /* Frameworks */,
|
||||||
|
E633A41E281EAEAB000E540F /* Resources */,
|
||||||
|
9620878489526FB1EDDF9FB7 /* [CP] Copy Pods Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = ShareExtension;
|
||||||
|
productName = ShareExtension;
|
||||||
|
productReference = E633A420281EAEAB000E540F /* ShareExtension.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
|
LastSwiftUpdateCheck = 1330;
|
||||||
LastUpgradeCheck = 1320;
|
LastUpgradeCheck = 1320;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
13B07F861A680F5B00A75B9A = {
|
13B07F861A680F5B00A75B9A = {
|
||||||
|
@ -191,6 +270,12 @@
|
||||||
LastSwiftMigration = 1240;
|
LastSwiftMigration = 1240;
|
||||||
ProvisioningStyle = Manual;
|
ProvisioningStyle = Manual;
|
||||||
};
|
};
|
||||||
|
E633A41F281EAEAB000E540F = {
|
||||||
|
CreatedOnToolsVersion = 13.3.1;
|
||||||
|
DevelopmentTeam = 8EGBLQ2MA6;
|
||||||
|
LastSwiftMigration = 1330;
|
||||||
|
ProvisioningStyle = Manual;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "tooot" */;
|
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "tooot" */;
|
||||||
|
@ -208,6 +293,7 @@
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
13B07F861A680F5B00A75B9A /* tooot */,
|
13B07F861A680F5B00A75B9A /* tooot */,
|
||||||
|
E633A41F281EAEAB000E540F /* ShareExtension */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
@ -227,6 +313,13 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
E633A41E281EAEAB000E540F /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
@ -242,7 +335,7 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n../node_modules/expo-constants/scripts/get-app-config-ios.sh\n../node_modules/expo-updates/scripts/create-manifest-ios.sh\n";
|
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n../node_modules/expo-constants/scripts/get-app-config-ios.sh\n";
|
||||||
};
|
};
|
||||||
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = {
|
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
@ -306,6 +399,46 @@
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-tooot/Pods-tooot-resources.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-tooot/Pods-tooot-resources.sh\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
|
8E32C2F04B8226F2A839525E /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-ShareExtension-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
9620878489526FB1EDDF9FB7 /* [CP] Copy Pods Resources */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-ShareExtension/Pods-ShareExtension-resources.sh",
|
||||||
|
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
|
||||||
|
);
|
||||||
|
name = "[CP] Copy Pods Resources";
|
||||||
|
outputPaths = (
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ShareExtension/Pods-ShareExtension-resources.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
FD10A7F022414F080027D42C /* Start Packager */ = {
|
FD10A7F022414F080027D42C /* Start Packager */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -332,15 +465,31 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
|
|
||||||
5EE44DD62600124E00A9BCED /* File.swift in Sources */,
|
5EE44DD62600124E00A9BCED /* File.swift in Sources */,
|
||||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||||
|
E613A80B28282A01003C97D6 /* AppDelegate.mm in Sources */,
|
||||||
E3BC22F5F8ABE515E14CF199 /* ExpoModulesProvider.swift in Sources */,
|
E3BC22F5F8ABE515E14CF199 /* ExpoModulesProvider.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
E633A41C281EAEAB000E540F /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
E633A430281EAF38000E540F /* ShareViewController.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
E633A429281EAEAB000E540F /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = E633A41F281EAEAB000E540F /* ShareExtension */;
|
||||||
|
targetProxy = E633A428281EAEAB000E540F /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
/* Begin PBXVariantGroup section */
|
||||||
13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
|
13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
|
@ -367,6 +516,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-tooot.debug.xcconfig */;
|
baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-tooot.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
|
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
|
||||||
|
@ -388,6 +538,7 @@
|
||||||
"-ObjC",
|
"-ObjC",
|
||||||
"-lc++",
|
"-lc++",
|
||||||
);
|
);
|
||||||
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.xmflsct.app.tooot;
|
PRODUCT_BUNDLE_IDENTIFIER = com.xmflsct.app.tooot;
|
||||||
PRODUCT_NAME = tooot;
|
PRODUCT_NAME = tooot;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.xmflsct.app.tooot";
|
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.xmflsct.app.tooot";
|
||||||
|
@ -396,7 +547,7 @@
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "tooot-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "tooot-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
|
@ -405,6 +556,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-tooot.release.xcconfig */;
|
baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-tooot.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
|
CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
|
||||||
|
@ -421,6 +573,7 @@
|
||||||
"-ObjC",
|
"-ObjC",
|
||||||
"-lc++",
|
"-lc++",
|
||||||
);
|
);
|
||||||
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.xmflsct.app.tooot;
|
PRODUCT_BUNDLE_IDENTIFIER = com.xmflsct.app.tooot;
|
||||||
PRODUCT_NAME = tooot;
|
PRODUCT_NAME = tooot;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.xmflsct.app.tooot";
|
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.xmflsct.app.tooot";
|
||||||
|
@ -428,7 +581,7 @@
|
||||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "tooot-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "tooot-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
@ -467,7 +620,7 @@
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 i386";
|
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
@ -486,8 +639,8 @@
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(SDKROOT)/usr/lib/swift",
|
||||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"",
|
|
||||||
"\"$(inherited)\"",
|
"\"$(inherited)\"",
|
||||||
);
|
);
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
|
@ -530,7 +683,7 @@
|
||||||
COPY_PHASE_STRIP = YES;
|
COPY_PHASE_STRIP = YES;
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 i386";
|
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
@ -542,8 +695,8 @@
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(SDKROOT)/usr/lib/swift",
|
||||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||||
"\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"",
|
|
||||||
"\"$(inherited)\"",
|
"\"$(inherited)\"",
|
||||||
);
|
);
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
@ -553,6 +706,93 @@
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
E633A42C281EAEAB000E540F /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 49AC0972A79258360BEDD73B /* Pods-ShareExtension.debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||||
|
CODE_SIGN_STYLE = Manual;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
DEVELOPMENT_TEAM = 8EGBLQ2MA6;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||||
|
"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.xmflsct.app.tooot.ShareExtension;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.xmflsct.app.tooot.ShareExtension";
|
||||||
|
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match AdHoc com.xmflsct.app.tooot.ShareExtension";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SUPPORTS_MACCATALYST = YES;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "ShareExtension/ShareExtension-Bridging-Header.h";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
E633A42D281EAEAB000E540F /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 312CB8F38010C3E0D27A8663 /* Pods-ShareExtension.release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||||
|
CODE_SIGN_STYLE = Manual;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
DEVELOPMENT_TEAM = 8EGBLQ2MA6;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||||
|
"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.xmflsct.app.tooot.ShareExtension;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.xmflsct.app.tooot.ShareExtension";
|
||||||
|
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match AppStore com.xmflsct.app.tooot.ShareExtension";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SUPPORTS_MACCATALYST = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "ShareExtension/ShareExtension-Bridging-Header.h";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
|
@ -574,6 +814,15 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
E633A42E281EAEAB000E540F /* Build configuration list for PBXNativeTarget "ShareExtension" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
E633A42C281EAEAB000E540F /* Debug */,
|
||||||
|
E633A42D281EAEAB000E540F /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||||
|
|
|
@ -4,6 +4,6 @@
|
||||||
|
|
||||||
#import <Expo/Expo.h>
|
#import <Expo/Expo.h>
|
||||||
|
|
||||||
@interface AppDelegate : EXAppDelegateWrapper <RCTBridgeDelegate>
|
@interface AppDelegate : EXAppDelegateWrapper <UIApplicationDelegate, RCTBridgeDelegate>
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
#import "AppDelegate.h"
|
|
||||||
|
|
||||||
#import <React/RCTBridge.h>
|
|
||||||
#import <React/RCTBundleURLProvider.h>
|
|
||||||
#import <React/RCTRootView.h>
|
|
||||||
#import <React/RCTLinkingManager.h>
|
|
||||||
#import <React/RCTConvert.h>
|
|
||||||
|
|
||||||
#if defined(FB_SONARKIT_ENABLED) && __has_include(<FlipperKit/FlipperClient.h>)
|
|
||||||
#import <FlipperKit/FlipperClient.h>
|
|
||||||
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
|
|
||||||
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
|
|
||||||
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
|
|
||||||
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
|
|
||||||
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
|
|
||||||
|
|
||||||
// iOS 9.x or newer
|
|
||||||
- (BOOL)application:(UIApplication *)application
|
|
||||||
openURL:(NSURL *)url
|
|
||||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
|
|
||||||
{
|
|
||||||
return [RCTLinkingManager application:application openURL:url options:options];
|
|
||||||
}
|
|
||||||
|
|
||||||
// iOS 8.x or older
|
|
||||||
- (BOOL)application:(UIApplication *)application
|
|
||||||
openURL:(NSURL *)url
|
|
||||||
sourceApplication:(NSString *)sourceApplication
|
|
||||||
annotation:(id)annotation
|
|
||||||
{
|
|
||||||
return [RCTLinkingManager application:application openURL:url
|
|
||||||
sourceApplication:sourceApplication annotation:annotation];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void InitializeFlipper(UIApplication *application) {
|
|
||||||
FlipperClient *client = [FlipperClient sharedClient];
|
|
||||||
SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
|
|
||||||
[client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
|
|
||||||
[client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
|
|
||||||
[client addPlugin:[FlipperKitReactPlugin new]];
|
|
||||||
[client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
|
|
||||||
[client start];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@implementation AppDelegate
|
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
|
||||||
{
|
|
||||||
#if defined(FB_SONARKIT_ENABLED) && __has_include(<FlipperKit/FlipperClient.h>)
|
|
||||||
InitializeFlipper(application);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
|
|
||||||
RCTRootView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:nil];
|
|
||||||
rootView.backgroundColor = [UIColor colorNamed:@"SplashScreenBackgroundColor"];
|
|
||||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
|
||||||
UIViewController *rootViewController = [self.reactDelegate createRootViewController];
|
|
||||||
rootViewController.view = rootView;
|
|
||||||
self.window.rootViewController = rootViewController;
|
|
||||||
[self.window makeKeyAndVisible];
|
|
||||||
|
|
||||||
[super application:application didFinishLaunchingWithOptions:launchOptions];
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
|
|
||||||
{
|
|
||||||
// If you'd like to export some custom RCTBridgeModules that are not Expo modules, add them here!
|
|
||||||
return @[];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
|
|
||||||
#ifdef DEBUG
|
|
||||||
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
|
|
||||||
#else
|
|
||||||
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Linking API
|
|
||||||
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
|
||||||
return [RCTLinkingManager application:application openURL:url options:options];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Universal Links
|
|
||||||
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
|
|
||||||
return [RCTLinkingManager application:application
|
|
||||||
continueUserActivity:userActivity
|
|
||||||
restorationHandler:restorationHandler];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
#import <React/RCTBridge.h>
|
||||||
|
#import <React/RCTBundleURLProvider.h>
|
||||||
|
#import <React/RCTRootView.h>
|
||||||
|
|
||||||
|
#import <React/RCTAppSetupUtils.h>
|
||||||
|
|
||||||
|
#import <React/RCTLinkingManager.h>
|
||||||
|
#import <RNShareMenu/ShareMenuManager.h>
|
||||||
|
|
||||||
|
#if RCT_NEW_ARCH_ENABLED
|
||||||
|
#import <React/CoreModulesPlugins.h>
|
||||||
|
#import <React/RCTCxxBridgeDelegate.h>
|
||||||
|
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
|
||||||
|
#import <React/RCTSurfacePresenter.h>
|
||||||
|
#import <React/RCTSurfacePresenterBridgeAdapter.h>
|
||||||
|
#import <ReactCommon/RCTTurboModuleManager.h>
|
||||||
|
|
||||||
|
#import <react/config/ReactNativeConfig.h>
|
||||||
|
|
||||||
|
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
|
||||||
|
RCTTurboModuleManager *_turboModuleManager;
|
||||||
|
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
|
||||||
|
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
|
||||||
|
facebook::react::ContextContainer::Shared _contextContainer;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@implementation AppDelegate
|
||||||
|
|
||||||
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||||
|
{
|
||||||
|
RCTAppSetupPrepareApp(application);
|
||||||
|
|
||||||
|
RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
|
||||||
|
|
||||||
|
#if RCT_NEW_ARCH_ENABLED
|
||||||
|
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
|
||||||
|
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
|
||||||
|
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
|
||||||
|
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
|
||||||
|
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
UIView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:nil];
|
||||||
|
|
||||||
|
if (@available(iOS 13.0, *)) {
|
||||||
|
rootView.backgroundColor = [UIColor colorNamed:@"SplashScreenBackgroundColor"];
|
||||||
|
} else {
|
||||||
|
rootView.backgroundColor = [UIColor whiteColor];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||||
|
UIViewController *rootViewController = [self.reactDelegate createRootViewController];
|
||||||
|
rootViewController.view = rootView;
|
||||||
|
self.window.rootViewController = rootViewController;
|
||||||
|
[self.window makeKeyAndVisible];
|
||||||
|
[super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
|
||||||
|
#else
|
||||||
|
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if RCT_NEW_ARCH_ENABLED
|
||||||
|
|
||||||
|
#pragma mark - RCTCxxBridgeDelegate
|
||||||
|
|
||||||
|
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge delegate:self jsInvoker:bridge.jsCallInvoker];
|
||||||
|
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark RCTTurboModuleManagerDelegate
|
||||||
|
|
||||||
|
- (Class)getModuleClassFromName:(const char *)name
|
||||||
|
{
|
||||||
|
return RCTCoreModulesClassProvider(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name initParams: (const facebook::react::ObjCTurboModule::InitParams &)params
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
|
||||||
|
{
|
||||||
|
return RCTAppSetupDefaultModuleFromClass(moduleClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Linking API
|
||||||
|
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||||
|
NSString *urlString = url.absoluteString;
|
||||||
|
|
||||||
|
if ([urlString hasPrefix:@"tooot-share://"]) {
|
||||||
|
NSLog(@"Entered with the following string: %@s", urlString);
|
||||||
|
return [ShareMenuManager application:application openURL:url options:options];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [RCTLinkingManager application:application openURL:url options:options];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Universal Links
|
||||||
|
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
|
||||||
|
return [RCTLinkingManager application:application
|
||||||
|
continueUserActivity:userActivity
|
||||||
|
restorationHandler:restorationHandler];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -25,10 +25,13 @@
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Editor</string>
|
||||||
<key>CFBundleURLName</key>
|
<key>CFBundleURLName</key>
|
||||||
<string>gizmos</string>
|
<string>com.xmflsct.app.tooot</string>
|
||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
|
<string>tooot-share</string>
|
||||||
<string>tooot</string>
|
<string>tooot</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
int main(int argc, char * argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,10 @@
|
||||||
<string>development</string>
|
<string>development</string>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.com.xmflsct.app.tooot</string>
|
||||||
|
</array>
|
||||||
<key>com.apple.security.device.audio-input</key>
|
<key>com.apple.security.device.audio-input</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.device.camera</key>
|
<key>com.apple.security.device.camera</key>
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
preset: 'jest-expo',
|
|
||||||
collectCoverage: true,
|
|
||||||
collectCoverageFrom: [
|
|
||||||
'src /**/*.{ts,tsx}',
|
|
||||||
'!**/coverage /**',
|
|
||||||
'!**/node_modules /**',
|
|
||||||
'!**/app.config.ts',
|
|
||||||
'!**/babel.config.js',
|
|
||||||
'!**/jest.setup.ts'
|
|
||||||
],
|
|
||||||
setupFiles: [
|
|
||||||
'<rootDir>/jest/async-storage.js',
|
|
||||||
'<rootDir>/jest/react-native.js',
|
|
||||||
'<rootDir>/jest/react-navigation.js'
|
|
||||||
],
|
|
||||||
transformIgnorePatterns: [
|
|
||||||
'node_modules/(?!(jest-)?react-native' +
|
|
||||||
'|react-clone-referenced-element' +
|
|
||||||
'|@react-native-community' +
|
|
||||||
'|expo(nent)?' +
|
|
||||||
'|@expo(nent)?/.*' +
|
|
||||||
'|react-navigation' +
|
|
||||||
'|@react-navigation/.*|@unimodules/.*|unimodules' +
|
|
||||||
'|sentry-expo' +
|
|
||||||
'|native-base' +
|
|
||||||
'|@sentry/.*' +
|
|
||||||
'|redux-persist-expo-securestore' +
|
|
||||||
')'
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
import mockAsyncStorage from '@react-native-async-storage/async-storage/jest/async-storage-mock'
|
|
||||||
|
|
||||||
jest.mock('@react-native-async-storage/async-storage', () => mockAsyncStorage)
|
|
|
@ -1,6 +0,0 @@
|
||||||
jest.mock('react-native/Libraries/LayoutAnimation/LayoutAnimation', () => ({
|
|
||||||
...require.requireActual(
|
|
||||||
'react-native/Libraries/LayoutAnimation/LayoutAnimation'
|
|
||||||
),
|
|
||||||
configureNext: jest.fn()
|
|
||||||
}))
|
|
|
@ -1,14 +0,0 @@
|
||||||
import 'react-native-gesture-handler/jestSetup'
|
|
||||||
|
|
||||||
jest.mock('react-native-reanimated', () => {
|
|
||||||
const Reanimated = require('react-native-reanimated/mock')
|
|
||||||
|
|
||||||
// The mock for `call` immediately calls the callback which is incorrect
|
|
||||||
// So we override it with a no-op
|
|
||||||
Reanimated.default.call = () => {}
|
|
||||||
|
|
||||||
return Reanimated
|
|
||||||
})
|
|
||||||
|
|
||||||
// Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing
|
|
||||||
jest.mock('react-native/Libraries/Animated/src/NativeAnimatedHelper')
|
|
141
package.json
141
package.json
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "tooot",
|
"name": "tooot",
|
||||||
"versions": {
|
"versions": {
|
||||||
"native": "220214",
|
"native": "220508",
|
||||||
"major": 3,
|
"major": 4,
|
||||||
"minor": 5,
|
"minor": 0,
|
||||||
"patch": 4,
|
"patch": 0,
|
||||||
"expo": "44.0.0"
|
"expo": "45.0.0"
|
||||||
},
|
},
|
||||||
"description": "tooot app for Mastodon",
|
"description": "tooot app for Mastodon",
|
||||||
"author": "xmflsct <me@xmflsct.com>",
|
"author": "xmflsct <me@xmflsct.com>",
|
||||||
|
@ -26,95 +26,107 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/react-native-action-sheet": "3.13.0",
|
"@expo/react-native-action-sheet": "3.13.0",
|
||||||
|
"@formatjs/intl-datetimeformat": "^5.0.2",
|
||||||
|
"@formatjs/intl-getcanonicallocales": "^1.9.2",
|
||||||
|
"@formatjs/intl-locale": "^2.4.47",
|
||||||
|
"@formatjs/intl-numberformat": "^7.4.3",
|
||||||
|
"@formatjs/intl-pluralrules": "^4.3.3",
|
||||||
|
"@formatjs/intl-relativetimeformat": "^10.0.1",
|
||||||
"@neverdull-agency/expo-unlimited-secure-store": "1.0.10",
|
"@neverdull-agency/expo-unlimited-secure-store": "1.0.10",
|
||||||
"@react-native-async-storage/async-storage": "1.16.1",
|
"@react-native-async-storage/async-storage": "1.17.4",
|
||||||
"@react-native-community/blur": "3.6.0",
|
"@react-native-community/blur": "3.6.0",
|
||||||
"@react-native-community/cameraroll": "4.1.2",
|
"@react-native-community/cameraroll": "4.1.2",
|
||||||
"@react-native-community/netinfo": "8.0.0",
|
"@react-native-community/netinfo": "8.3.0",
|
||||||
"@react-native-community/segmented-control": "2.2.2",
|
"@react-native-community/segmented-control": "2.2.2",
|
||||||
"@react-navigation/bottom-tabs": "6.2.0",
|
"@react-navigation/bottom-tabs": "6.3.1",
|
||||||
"@react-navigation/native": "6.0.8",
|
"@react-navigation/native": "6.0.10",
|
||||||
"@react-navigation/native-stack": "6.5.0",
|
"@react-navigation/native-stack": "6.6.2",
|
||||||
"@react-navigation/stack": "6.1.1",
|
"@react-navigation/stack": "6.2.1",
|
||||||
"@reduxjs/toolkit": "1.7.2",
|
"@reduxjs/toolkit": "1.8.1",
|
||||||
"@sentry/react-native": "3.2.13",
|
"@sentry/react-native": "3.4.2",
|
||||||
"@sharcoux/slider": "5.6.4",
|
"@sharcoux/slider": "6.0.3",
|
||||||
"axios": "0.24.0",
|
"axios": "0.27.2",
|
||||||
"expo": "44.0.6",
|
"expo": "45.0.3",
|
||||||
"expo-auth-session": "3.5.0",
|
"expo-auth-session": "3.6.0",
|
||||||
"expo-av": "10.2.1",
|
"expo-av": "11.2.3",
|
||||||
"expo-constants": "^13.0.2",
|
"expo-constants": "^13.1.1",
|
||||||
"expo-crypto": "10.1.2",
|
"expo-crypto": "10.2.0",
|
||||||
"expo-device": "4.1.1",
|
"expo-device": "4.2.0",
|
||||||
"expo-file-system": "13.2.0",
|
"expo-file-system": "14.0.0",
|
||||||
"expo-firebase-analytics": "6.0.1",
|
"expo-firebase-analytics": "7.0.0",
|
||||||
"expo-haptics": "11.1.1",
|
"expo-haptics": "11.2.0",
|
||||||
"expo-image-manipulator": "10.2.1",
|
"expo-image-manipulator": "10.3.1",
|
||||||
"expo-image-picker": "12.0.2",
|
"expo-image-picker": "13.1.1",
|
||||||
"expo-linking": "3.0.0",
|
"expo-linking": "3.1.0",
|
||||||
"expo-localization": "12.0.1",
|
"expo-localization": "13.0.0",
|
||||||
"expo-notifications": "0.14.1",
|
"expo-notifications": "0.15.2",
|
||||||
"expo-random": "12.1.2",
|
"expo-random": "12.2.0",
|
||||||
"expo-screen-capture": "4.1.1",
|
"expo-screen-capture": "4.2.0",
|
||||||
"expo-secure-store": "11.1.1",
|
"expo-secure-store": "11.2.0",
|
||||||
"expo-splash-screen": "0.14.2",
|
"expo-splash-screen": "0.15.1",
|
||||||
"expo-store-review": "5.1.1",
|
"expo-store-review": "5.2.0",
|
||||||
"expo-updates": "0.11.3",
|
"expo-updates": "0.13.1",
|
||||||
"expo-video-thumbnails": "6.2.0",
|
"expo-video-thumbnails": "6.3.0",
|
||||||
"expo-web-browser": "10.1.1",
|
"expo-web-browser": "10.2.0",
|
||||||
"i18next": "20.6.1",
|
"i18next": "21.8.1",
|
||||||
"li": "1.3.0",
|
"li": "1.3.0",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"react-i18next": "11.15.4",
|
"react-i18next": "11.16.9",
|
||||||
"react-native": "0.66.4",
|
"react-intl": "^5.25.1",
|
||||||
|
"react-native": "0.68.2",
|
||||||
"react-native-animated-spinkit": "1.5.2",
|
"react-native-animated-spinkit": "1.5.2",
|
||||||
"react-native-base64": "^0.2.1",
|
"react-native-base64": "^0.2.1",
|
||||||
"react-native-blurhash": "1.1.8",
|
"react-native-blurhash": "1.1.10",
|
||||||
"react-native-fast-image": "8.5.11",
|
"react-native-fast-image": "8.5.11",
|
||||||
"react-native-feather": "1.1.2",
|
"react-native-feather": "1.1.2",
|
||||||
"react-native-flash-message": "0.2.1",
|
"react-native-flash-message": "0.2.1",
|
||||||
"react-native-gesture-handler": "2.2.0",
|
"react-native-gesture-handler": "2.4.2",
|
||||||
"react-native-htmlview": "0.16.0",
|
"react-native-htmlview": "0.16.0",
|
||||||
|
"react-native-image-keyboard": "^2.2.0",
|
||||||
"react-native-pager-view": "5.4.11",
|
"react-native-pager-view": "5.4.11",
|
||||||
"react-native-reanimated": "2.4.1",
|
"react-native-reanimated": "2.8.0",
|
||||||
"react-native-safe-area-context": "3.3.2",
|
"react-native-safe-area-context": "4.2.5",
|
||||||
"react-native-screens": "3.11.1",
|
"react-native-screens": "3.13.1",
|
||||||
"react-native-svg": "12.1.1",
|
"react-native-share-menu": "^5.0.5",
|
||||||
|
"react-native-svg": "12.3.0",
|
||||||
"react-native-swipe-list-view": "3.2.9",
|
"react-native-swipe-list-view": "3.2.9",
|
||||||
"react-native-tab-view": "3.1.1",
|
"react-native-tab-view": "3.1.1",
|
||||||
"react-query": "3.34.15",
|
"react-query": "3.39.0",
|
||||||
"react-redux": "7.2.6",
|
"react-redux": "8.0.1",
|
||||||
"react-timeago": "6.2.1",
|
|
||||||
"redux-persist": "6.0.0",
|
"redux-persist": "6.0.0",
|
||||||
"rn-placeholder": "3.0.3",
|
"rn-placeholder": "3.0.3",
|
||||||
"sentry-expo": "4.0.3",
|
"sentry-expo": "4.1.1",
|
||||||
"tslib": "2.3.1",
|
"tslib": "2.4.0",
|
||||||
"valid-url": "1.0.9"
|
"valid-url": "1.0.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.17.2",
|
"@babel/core": "7.17.10",
|
||||||
"@babel/plugin-proposal-optional-chaining": "7.16.7",
|
"@babel/plugin-proposal-optional-chaining": "7.16.7",
|
||||||
"@babel/preset-typescript": "7.16.7",
|
"@babel/preset-typescript": "7.16.7",
|
||||||
"@expo/config": "6.0.18",
|
"@expo/config": "6.0.23",
|
||||||
"@types/lodash": "4.14.178",
|
"@types/lodash": "4.14.182",
|
||||||
"@types/react": "17.0.39",
|
"@types/react": "17.0.43",
|
||||||
"@types/react-dom": "17.0.11",
|
"@types/react-dom": "17.0.14",
|
||||||
"@types/react-native": "0.66.15",
|
"@types/react-native": "0.67.6",
|
||||||
"@types/react-native-base64": "^0.2.0",
|
"@types/react-native-base64": "^0.2.0",
|
||||||
"@types/react-redux": "7.1.22",
|
"@types/react-native-share-menu": "^5.0.2",
|
||||||
"@types/react-timeago": "4.1.3",
|
"@types/react-timeago": "4.1.3",
|
||||||
"@types/valid-url": "1.0.3",
|
"@types/valid-url": "1.0.3",
|
||||||
"@welldone-software/why-did-you-render": "6.2.3",
|
"@welldone-software/why-did-you-render": "7.0.1",
|
||||||
"babel-plugin-module-resolver": "4.1.0",
|
"babel-plugin-module-resolver": "4.1.0",
|
||||||
"babel-plugin-transform-remove-console": "6.9.4",
|
"babel-plugin-transform-remove-console": "6.9.4",
|
||||||
"chalk": "4.1.2",
|
"chalk": "4.1.2",
|
||||||
"dotenv": "16.0.0",
|
"dotenv": "16.0.1",
|
||||||
"patch-package": "6.4.7",
|
"patch-package": "6.4.7",
|
||||||
"postinstall-postinstall": "2.1.0",
|
"postinstall-postinstall": "2.1.0",
|
||||||
"react-native-clean-project": "4.0.0",
|
"react-native-clean-project": "4.0.1",
|
||||||
"typescript": "4.5.5"
|
"typescript": "4.6.4"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"@types/react": "17.0.43",
|
||||||
|
"@types/react-dom": "17.0.14"
|
||||||
},
|
},
|
||||||
"expo": {
|
"expo": {
|
||||||
"autolinking": {
|
"autolinking": {
|
||||||
|
@ -132,9 +144,10 @@
|
||||||
"expo-firebase-core",
|
"expo-firebase-core",
|
||||||
"expo-font",
|
"expo-font",
|
||||||
"expo-keep-awake",
|
"expo-keep-awake",
|
||||||
"expo-store-review"
|
"expo-store-review",
|
||||||
|
"react-native-reanimated"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
diff --git a/node_modules/@types/react-native-share-menu/index.d.ts b/node_modules/@types/react-native-share-menu/index.d.ts
|
||||||
|
index f52822c..ee98565 100755
|
||||||
|
--- a/node_modules/@types/react-native-share-menu/index.d.ts
|
||||||
|
+++ b/node_modules/@types/react-native-share-menu/index.d.ts
|
||||||
|
@@ -5,11 +5,9 @@
|
||||||
|
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||||
|
// Minimum TypeScript Version: 3.7
|
||||||
|
|
||||||
|
-export interface ShareData {
|
||||||
|
- mimeType: string;
|
||||||
|
- data: string | string[];
|
||||||
|
- extraData?: object | undefined;
|
||||||
|
-}
|
||||||
|
+export type ShareData = {
|
||||||
|
+ data: {mimeType: string; data: string}[];
|
||||||
|
+} | {mimeType: string; data: string | string[]}
|
||||||
|
|
||||||
|
export type ShareCallback = (share?: ShareData) => void;
|
||||||
|
|
||||||
|
@@ -28,7 +26,7 @@ interface ShareMenuReactView {
|
||||||
|
dismissExtension(error?: string): void;
|
||||||
|
openApp(): void;
|
||||||
|
continueInApp(extraData?: object): void;
|
||||||
|
- data(): Promise<{mimeType: string, data: string}>;
|
||||||
|
+ data(): Promise<{data: {mimeType: string; data: string}[]}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ShareMenuReactView: ShareMenuReactView;
|
|
@ -1,81 +0,0 @@
|
||||||
diff --git a/node_modules/expo-av/ios/EXAV/EXAV.m b/node_modules/expo-av/ios/EXAV/EXAV.m
|
|
||||||
index d255852..edf934f 100644
|
|
||||||
--- a/node_modules/expo-av/ios/EXAV/EXAV.m
|
|
||||||
+++ b/node_modules/expo-av/ios/EXAV/EXAV.m
|
|
||||||
@@ -63,7 +63,7 @@ NSString *const EXDidUpdateMetadataEventName = @"didUpdateMetadata";
|
|
||||||
@property (nonatomic, assign) BOOL audioRecorderShouldBeginRecording;
|
|
||||||
@property (nonatomic, assign) int audioRecorderDurationMillis;
|
|
||||||
|
|
||||||
-@property (nonatomic, weak) EXModuleRegistry *moduleRegistry;
|
|
||||||
+@property (nonatomic, weak) EXModuleRegistry *expoModuleRegistry;
|
|
||||||
@property (nonatomic, weak) id<EXPermissionsInterface> permissionsManager;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -106,7 +106,7 @@ EX_EXPORT_MODULE(ExponentAV);
|
|
||||||
|
|
||||||
- (void)installJsiBindings
|
|
||||||
{
|
|
||||||
- id<EXJavaScriptContextProvider> jsContextProvider = [_moduleRegistry getModuleImplementingProtocol:@protocol(EXJavaScriptContextProvider)];
|
|
||||||
+ id<EXJavaScriptContextProvider> jsContextProvider = [_expoModuleRegistry getModuleImplementingProtocol:@protocol(EXJavaScriptContextProvider)];
|
|
||||||
void *jsRuntimePtr = [jsContextProvider javaScriptRuntimePointer];
|
|
||||||
if (jsRuntimePtr) {
|
|
||||||
[self installJSIBindingsForRuntime:jsRuntimePtr withSoundDictionary:_soundDictionary];
|
|
||||||
@@ -131,16 +131,16 @@ EX_EXPORT_MODULE(ExponentAV);
|
|
||||||
|
|
||||||
#pragma mark - Expo experience lifecycle
|
|
||||||
|
|
||||||
-- (void)setModuleRegistry:(EXModuleRegistry *)moduleRegistry
|
|
||||||
+- (void)setModuleRegistry:(EXModuleRegistry *)expoModuleRegistry
|
|
||||||
{
|
|
||||||
- [[_moduleRegistry getModuleImplementingProtocol:@protocol(EXAppLifecycleService)] unregisterAppLifecycleListener:self];
|
|
||||||
- _moduleRegistry = moduleRegistry;
|
|
||||||
- _kernelAudioSessionManagerDelegate = [_moduleRegistry getSingletonModuleForName:@"AudioSessionManager"];
|
|
||||||
+ [[_expoModuleRegistry getModuleImplementingProtocol:@protocol(EXAppLifecycleService)] unregisterAppLifecycleListener:self];
|
|
||||||
+ _expoModuleRegistry = expoModuleRegistry;
|
|
||||||
+ _kernelAudioSessionManagerDelegate = [_expoModuleRegistry getSingletonModuleForName:@"AudioSessionManager"];
|
|
||||||
if (!_isBackgrounded) {
|
|
||||||
[_kernelAudioSessionManagerDelegate moduleDidForeground:self];
|
|
||||||
}
|
|
||||||
- [[_moduleRegistry getModuleImplementingProtocol:@protocol(EXAppLifecycleService)] registerAppLifecycleListener:self];
|
|
||||||
- _permissionsManager = [_moduleRegistry getModuleImplementingProtocol:@protocol(EXPermissionsInterface)];
|
|
||||||
+ [[_expoModuleRegistry getModuleImplementingProtocol:@protocol(EXAppLifecycleService)] registerAppLifecycleListener:self];
|
|
||||||
+ _permissionsManager = [_expoModuleRegistry getModuleImplementingProtocol:@protocol(EXPermissionsInterface)];
|
|
||||||
[EXPermissionsMethodsDelegate registerRequesters:@[[EXAudioRecordingPermissionRequester new]] withPermissionsManager:_permissionsManager];
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -478,7 +478,7 @@ withEXVideoViewForTag:(nonnull NSNumber *)reactTag
|
|
||||||
{
|
|
||||||
// TODO check that the bridge is still valid after the dispatch
|
|
||||||
// TODO check if the queues are ok
|
|
||||||
- [[_moduleRegistry getModuleImplementingProtocol:@protocol(EXUIManager)] executeUIBlock:^(id view) {
|
|
||||||
+ [[_expoModuleRegistry getModuleImplementingProtocol:@protocol(EXUIManager)] executeUIBlock:^(id view) {
|
|
||||||
if ([view isKindOfClass:[EXVideoView class]]) {
|
|
||||||
block(view);
|
|
||||||
} else {
|
|
||||||
@@ -566,7 +566,7 @@ withEXVideoViewForTag:(nonnull NSNumber *)reactTag
|
|
||||||
return EXErrorWithMessage(@"Recorder is already prepared.");
|
|
||||||
}
|
|
||||||
|
|
||||||
- id<EXFileSystemInterface> fileSystem = [_moduleRegistry getModuleImplementingProtocol:@protocol(EXFileSystemInterface)];
|
|
||||||
+ id<EXFileSystemInterface> fileSystem = [_expoModuleRegistry getModuleImplementingProtocol:@protocol(EXFileSystemInterface)];
|
|
||||||
|
|
||||||
if (!fileSystem) {
|
|
||||||
return EXErrorWithMessage(@"No FileSystem module.");
|
|
||||||
@@ -726,7 +726,7 @@ EX_EXPORT_METHOD_AS(loadForSound,
|
|
||||||
|
|
||||||
- (void)sendEventWithName:(NSString *)eventName body:(NSDictionary *)body
|
|
||||||
{
|
|
||||||
- [[_moduleRegistry getModuleImplementingProtocol:@protocol(EXEventEmitterService)] sendEventWithName:eventName body:body];
|
|
||||||
+ [[_expoModuleRegistry getModuleImplementingProtocol:@protocol(EXEventEmitterService)] sendEventWithName:eventName body:body];
|
|
||||||
}
|
|
||||||
|
|
||||||
EX_EXPORT_METHOD_AS(unloadForSound,
|
|
||||||
@@ -984,7 +984,7 @@ EX_EXPORT_METHOD_AS(unloadAudioRecorder,
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
[_kernelAudioSessionManagerDelegate moduleWillDeallocate:self];
|
|
||||||
- [[_moduleRegistry getModuleImplementingProtocol:@protocol(EXAppLifecycleService)] unregisterAppLifecycleListener:self];
|
|
||||||
+ [[_expoModuleRegistry getModuleImplementingProtocol:@protocol(EXAppLifecycleService)] unregisterAppLifecycleListener:self];
|
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
||||||
|
|
||||||
// This will clear all @properties and deactivate the audio session:
|
|
|
@ -1,166 +0,0 @@
|
||||||
diff --git a/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt b/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt
|
|
||||||
index 53bf40f..0ba5d89 100644
|
|
||||||
--- a/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt
|
|
||||||
+++ b/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt
|
|
||||||
@@ -56,6 +56,7 @@ import okhttp3.Callback
|
|
||||||
import okhttp3.Headers
|
|
||||||
import okhttp3.JavaNetCookieJar
|
|
||||||
import okhttp3.MediaType
|
|
||||||
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
|
||||||
import okhttp3.MultipartBody
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
@@ -63,11 +64,7 @@ import okhttp3.RequestBody
|
|
||||||
import okhttp3.Response
|
|
||||||
import okhttp3.ResponseBody
|
|
||||||
|
|
||||||
-import okio.Buffer
|
|
||||||
-import okio.BufferedSource
|
|
||||||
-import okio.ForwardingSource
|
|
||||||
-import okio.Okio
|
|
||||||
-import okio.Source
|
|
||||||
+import okio.*
|
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Hex
|
|
||||||
import org.apache.commons.codec.digest.DigestUtils
|
|
||||||
@@ -766,7 +763,7 @@ open class FileSystemModule(
|
|
||||||
}
|
|
||||||
|
|
||||||
val body = createRequestBody(options, decorator, fileUri.toFile())
|
|
||||||
- return requestBuilder.method(method, body).build()
|
|
||||||
+ return method?.let { requestBuilder.method(it, body).build() }
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.message?.let { Log.e(TAG, it) }
|
|
||||||
promise.reject(e)
|
|
||||||
@@ -791,7 +788,7 @@ open class FileSystemModule(
|
|
||||||
} ?: URLConnection.guessContentTypeFromName(file.name)
|
|
||||||
|
|
||||||
val fieldName = options["fieldName"]?.let { it as String } ?: file.name
|
|
||||||
- bodyBuilder.addFormDataPart(fieldName, file.name, decorator.decorate(RequestBody.create(MediaType.parse(mimeType), file)))
|
|
||||||
+ bodyBuilder.addFormDataPart(fieldName, file.name, decorator.decorate(RequestBody.create(mimeType.toMediaTypeOrNull(), file)))
|
|
||||||
bodyBuilder.build()
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
@@ -816,9 +813,9 @@ open class FileSystemModule(
|
|
||||||
|
|
||||||
override fun onResponse(call: Call, response: Response) {
|
|
||||||
val result = Bundle().apply {
|
|
||||||
- putString("body", response.body()?.string())
|
|
||||||
- putInt("status", response.code())
|
|
||||||
- putBundle("headers", translateHeaders(response.headers()))
|
|
||||||
+ putString("body", response.body?.string())
|
|
||||||
+ putInt("status", response.code)
|
|
||||||
+ putBundle("headers", translateHeaders(response.headers))
|
|
||||||
}
|
|
||||||
response.close()
|
|
||||||
promise.resolve(result)
|
|
||||||
@@ -866,7 +863,7 @@ open class FileSystemModule(
|
|
||||||
taskHandlers[uuid] = TaskHandler(call)
|
|
||||||
call.enqueue(object : Callback {
|
|
||||||
override fun onFailure(call: Call, e: IOException) {
|
|
||||||
- if (call.isCanceled) {
|
|
||||||
+ if (call.isCanceled()) {
|
|
||||||
promise.resolve(null)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -876,11 +873,11 @@ open class FileSystemModule(
|
|
||||||
|
|
||||||
override fun onResponse(call: Call, response: Response) {
|
|
||||||
val result = Bundle()
|
|
||||||
- val body = response.body()
|
|
||||||
+ val body = response.body
|
|
||||||
result.apply {
|
|
||||||
putString("body", body?.string())
|
|
||||||
- putInt("status", response.code())
|
|
||||||
- putBundle("headers", translateHeaders(response.headers()))
|
|
||||||
+ putInt("status", response.code)
|
|
||||||
+ putBundle("headers", translateHeaders(response.headers))
|
|
||||||
}
|
|
||||||
response.close()
|
|
||||||
promise.resolve(result)
|
|
||||||
@@ -900,10 +897,10 @@ open class FileSystemModule(
|
|
||||||
val resources = context.resources
|
|
||||||
val packageName = context.packageName
|
|
||||||
val resourceId = resources.getIdentifier(url, "raw", packageName)
|
|
||||||
- val bufferedSource = Okio.buffer(Okio.source(context.resources.openRawResource(resourceId)))
|
|
||||||
+ val bufferedSource = context.resources.openRawResource(resourceId).source().buffer()
|
|
||||||
val file = uri.toFile()
|
|
||||||
file.delete()
|
|
||||||
- val sink = Okio.buffer(Okio.sink(file))
|
|
||||||
+ val sink = file.sink().buffer()
|
|
||||||
sink.writeAll(bufferedSource)
|
|
||||||
sink.close()
|
|
||||||
val result = Bundle()
|
|
||||||
@@ -934,13 +931,13 @@ open class FileSystemModule(
|
|
||||||
override fun onResponse(call: Call, response: Response) {
|
|
||||||
val file = uri.toFile()
|
|
||||||
file.delete()
|
|
||||||
- val sink = Okio.buffer(Okio.sink(file))
|
|
||||||
- sink.writeAll(response.body()!!.source())
|
|
||||||
+ val sink = file.sink().buffer()
|
|
||||||
+ sink.writeAll(response.body!!.source())
|
|
||||||
sink.close()
|
|
||||||
val result = Bundle().apply {
|
|
||||||
putString("uri", Uri.fromFile(file).toString())
|
|
||||||
- putInt("status", response.code())
|
|
||||||
- putBundle("headers", translateHeaders(response.headers()))
|
|
||||||
+ putInt("status", response.code)
|
|
||||||
+ putBundle("headers", translateHeaders(response.headers))
|
|
||||||
if (options?.get("md5") == true) {
|
|
||||||
putString("md5", md5(file))
|
|
||||||
}
|
|
||||||
@@ -1003,7 +1000,7 @@ open class FileSystemModule(
|
|
||||||
?.addNetworkInterceptor { chain ->
|
|
||||||
val originalResponse = chain.proceed(chain.request())
|
|
||||||
originalResponse.newBuilder()
|
|
||||||
- .body(ProgressResponseBody(originalResponse.body(), progressListener))
|
|
||||||
+ .body(ProgressResponseBody(originalResponse.body, progressListener))
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
?.build()
|
|
||||||
@@ -1098,7 +1095,7 @@ open class FileSystemModule(
|
|
||||||
val options = params[0]?.options
|
|
||||||
return try {
|
|
||||||
val response = call!!.execute()
|
|
||||||
- val responseBody = response.body()
|
|
||||||
+ val responseBody = response.body
|
|
||||||
val input = BufferedInputStream(responseBody!!.byteStream())
|
|
||||||
val output = FileOutputStream(file, isResume == true)
|
|
||||||
val data = ByteArray(1024)
|
|
||||||
@@ -1108,15 +1105,15 @@ open class FileSystemModule(
|
|
||||||
}
|
|
||||||
val result = Bundle().apply {
|
|
||||||
putString("uri", Uri.fromFile(file).toString())
|
|
||||||
- putInt("status", response.code())
|
|
||||||
- putBundle("headers", translateHeaders(response.headers()))
|
|
||||||
+ putInt("status", response.code)
|
|
||||||
+ putBundle("headers", translateHeaders(response.headers))
|
|
||||||
options?.get("md5").takeIf { it == true }?.let { putString("md5", file?.let { md5(it) }) }
|
|
||||||
}
|
|
||||||
response.close()
|
|
||||||
promise?.resolve(result)
|
|
||||||
null
|
|
||||||
} catch (e: Exception) {
|
|
||||||
- if (call?.isCanceled == true) {
|
|
||||||
+ if (call?.isCanceled() == true) {
|
|
||||||
promise?.resolve(null)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
@@ -1139,7 +1136,7 @@ open class FileSystemModule(
|
|
||||||
override fun contentLength(): Long = responseBody?.contentLength() ?: -1
|
|
||||||
|
|
||||||
override fun source(): BufferedSource =
|
|
||||||
- bufferedSource ?: Okio.buffer(source(responseBody!!.source()))
|
|
||||||
+ bufferedSource ?: source(responseBody!!.source()).buffer()
|
|
||||||
|
|
||||||
private fun source(source: Source): Source {
|
|
||||||
return object : ForwardingSource(source) {
|
|
||||||
@@ -1304,7 +1301,7 @@ open class FileSystemModule(
|
|
||||||
// Copied out of React Native's `NetworkingModule.java`
|
|
||||||
private fun translateHeaders(headers: Headers): Bundle {
|
|
||||||
val responseHeaders = Bundle()
|
|
||||||
- for (i in 0 until headers.size()) {
|
|
||||||
+ for (i in 0 until headers.size) {
|
|
||||||
val headerName = headers.name(i)
|
|
||||||
// multiple values for the same header
|
|
||||||
if (responseHeaders[headerName] != null) {
|
|
|
@ -1,106 +0,0 @@
|
||||||
diff --git a/node_modules/expo-updates/android/src/main/java/expo/modules/updates/loader/Crypto.kt b/node_modules/expo-updates/android/src/main/java/expo/modules/updates/loader/Crypto.kt
|
|
||||||
index 69d5957..ec9858c 100644
|
|
||||||
--- a/node_modules/expo-updates/android/src/main/java/expo/modules/updates/loader/Crypto.kt
|
|
||||||
+++ b/node_modules/expo-updates/android/src/main/java/expo/modules/updates/loader/Crypto.kt
|
|
||||||
@@ -47,7 +47,7 @@ object Crypto {
|
|
||||||
override fun onResponse(call: Call, response: Response) {
|
|
||||||
val exception: Exception = try {
|
|
||||||
val isValid = verifyPublicRSASignature(
|
|
||||||
- response.body()!!.string(), plainText, cipherText
|
|
||||||
+ response.body!!.string(), plainText, cipherText
|
|
||||||
)
|
|
||||||
listener.onCompleted(isValid)
|
|
||||||
return
|
|
||||||
diff --git a/node_modules/expo-updates/android/src/main/java/expo/modules/updates/loader/FileDownloader.kt b/node_modules/expo-updates/android/src/main/java/expo/modules/updates/loader/FileDownloader.kt
|
|
||||||
index 530a259..3c70674 100644
|
|
||||||
--- a/node_modules/expo-updates/android/src/main/java/expo/modules/updates/loader/FileDownloader.kt
|
|
||||||
+++ b/node_modules/expo-updates/android/src/main/java/expo/modules/updates/loader/FileDownloader.kt
|
|
||||||
@@ -14,6 +14,7 @@ import expo.modules.updates.manifest.ManifestHeaderData
|
|
||||||
import expo.modules.updates.manifest.UpdateManifest
|
|
||||||
import expo.modules.updates.selectionpolicy.SelectionPolicies
|
|
||||||
import okhttp3.*
|
|
||||||
+import okhttp3.Headers.Companion.toHeaders
|
|
||||||
import org.json.JSONArray
|
|
||||||
import org.json.JSONException
|
|
||||||
import org.json.JSONObject
|
|
||||||
@@ -65,14 +66,14 @@ open class FileDownloader(context: Context) {
|
|
||||||
if (!response.isSuccessful) {
|
|
||||||
callback.onFailure(
|
|
||||||
Exception(
|
|
||||||
- "Network request failed: " + response.body()!!
|
|
||||||
+ "Network request failed: " + response.body!!
|
|
||||||
.string()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
- response.body()!!.byteStream().use { inputStream ->
|
|
||||||
+ response.body!!.byteStream().use { inputStream ->
|
|
||||||
val hash = UpdatesUtils.sha256AndWriteToFile(inputStream, destination)
|
|
||||||
callback.onSuccess(destination, hash)
|
|
||||||
}
|
|
||||||
@@ -100,7 +101,7 @@ open class FileDownloader(context: Context) {
|
|
||||||
|
|
||||||
parseMultipartManifestResponse(response, boundaryParameter, configuration, callback)
|
|
||||||
} else {
|
|
||||||
- val responseHeaders = response.headers()
|
|
||||||
+ val responseHeaders = response.headers
|
|
||||||
val manifestHeaderData = ManifestHeaderData(
|
|
||||||
protocolVersion = responseHeaders["expo-protocol-version"],
|
|
||||||
manifestFilters = responseHeaders["expo-manifest-filters"],
|
|
||||||
@@ -108,7 +109,7 @@ open class FileDownloader(context: Context) {
|
|
||||||
manifestSignature = responseHeaders["expo-manifest-signature"],
|
|
||||||
)
|
|
||||||
|
|
||||||
- parseManifest(response.body()!!.string(), manifestHeaderData, null, configuration, callback)
|
|
||||||
+ parseManifest(response.body!!.string(), manifestHeaderData, null, configuration, callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -124,14 +125,14 @@ open class FileDownloader(context: Context) {
|
|
||||||
val value = line.substring(indexOfSeparator + 1).trim()
|
|
||||||
headers[key] = value
|
|
||||||
}
|
|
||||||
- return Headers.of(headers)
|
|
||||||
+ return headers.toHeaders()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseMultipartManifestResponse(response: Response, boundary: String, configuration: UpdatesConfiguration, callback: ManifestDownloadCallback) {
|
|
||||||
var manifestPartBodyAndHeaders: Pair<String, Headers>? = null
|
|
||||||
var extensionsBody: String? = null
|
|
||||||
|
|
||||||
- val multipartStream = MultipartStream(response.body()!!.byteStream(), boundary.toByteArray())
|
|
||||||
+ val multipartStream = MultipartStream(response.body!!.byteStream(), boundary.toByteArray())
|
|
||||||
|
|
||||||
try {
|
|
||||||
var nextPart = multipartStream.skipPreamble()
|
|
||||||
@@ -178,7 +179,7 @@ open class FileDownloader(context: Context) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
- val responseHeaders = response.headers()
|
|
||||||
+ val responseHeaders = response.headers
|
|
||||||
val manifestHeaderData = ManifestHeaderData(
|
|
||||||
protocolVersion = responseHeaders["expo-protocol-version"],
|
|
||||||
manifestFilters = responseHeaders["expo-manifest-filters"],
|
|
||||||
@@ -275,7 +276,7 @@ open class FileDownloader(context: Context) {
|
|
||||||
callback.onFailure(
|
|
||||||
"Failed to download manifest from URL: " + configuration.updateUrl,
|
|
||||||
Exception(
|
|
||||||
- response.body()!!.string()
|
|
||||||
+ response.body!!.string()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
@@ -464,7 +465,9 @@ open class FileDownloader(context: Context) {
|
|
||||||
if (runtimeVersion != null && runtimeVersion.isNotEmpty()) {
|
|
||||||
header("Expo-Runtime-Version", runtimeVersion)
|
|
||||||
} else {
|
|
||||||
- header("Expo-SDK-Version", sdkVersion)
|
|
||||||
+ if (sdkVersion != null) {
|
|
||||||
+ header("Expo-SDK-Version", sdkVersion)
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.header("Expo-Release-Channel", configuration.releaseChannel)
|
|
|
@ -1,5 +1,5 @@
|
||||||
diff --git a/node_modules/react-native-fast-image/RNFastImage.podspec b/node_modules/react-native-fast-image/RNFastImage.podspec
|
diff --git a/node_modules/react-native-fast-image/RNFastImage.podspec b/node_modules/react-native-fast-image/RNFastImage.podspec
|
||||||
index db0fada..9a2457c 100644
|
index db0fada..54d8d5b 100644
|
||||||
--- a/node_modules/react-native-fast-image/RNFastImage.podspec
|
--- a/node_modules/react-native-fast-image/RNFastImage.podspec
|
||||||
+++ b/node_modules/react-native-fast-image/RNFastImage.podspec
|
+++ b/node_modules/react-native-fast-image/RNFastImage.podspec
|
||||||
@@ -16,6 +16,6 @@ Pod::Spec.new do |s|
|
@@ -16,6 +16,6 @@ Pod::Spec.new do |s|
|
||||||
|
@ -7,7 +7,7 @@ index db0fada..9a2457c 100644
|
||||||
|
|
||||||
s.dependency 'React-Core'
|
s.dependency 'React-Core'
|
||||||
- s.dependency 'SDWebImage', '~> 5.11.1'
|
- s.dependency 'SDWebImage', '~> 5.11.1'
|
||||||
+ s.dependency 'SDWebImage', '~> 5.12.3'
|
+ s.dependency 'SDWebImage', '~> 5.12.5'
|
||||||
s.dependency 'SDWebImageWebPCoder', '~> 0.8.4'
|
s.dependency 'SDWebImageWebPCoder', '~> 0.8.4'
|
||||||
end
|
end
|
||||||
diff --git a/node_modules/react-native-fast-image/android/build.gradle b/node_modules/react-native-fast-image/android/build.gradle
|
diff --git a/node_modules/react-native-fast-image/android/build.gradle b/node_modules/react-native-fast-image/android/build.gradle
|
||||||
|
|
|
@ -0,0 +1,501 @@
|
||||||
|
diff --git a/node_modules/react-native-share-menu/ios/Constants.swift b/node_modules/react-native-share-menu/ios/Constants.swift
|
||||||
|
index 2811008..08385b7 100644
|
||||||
|
--- a/node_modules/react-native-share-menu/ios/Constants.swift
|
||||||
|
+++ b/node_modules/react-native-share-menu/ios/Constants.swift
|
||||||
|
@@ -23,7 +23,7 @@ public let COULD_NOT_PARSE_IMG_ERROR = "Couldn't parse image"
|
||||||
|
public let COULD_NOT_SAVE_FILE_ERROR = "Couldn't save file on disk"
|
||||||
|
public let NO_EXTENSION_CONTEXT_ERROR = "No extension context attached"
|
||||||
|
public let NO_DELEGATE_ERROR = "No ReactShareViewDelegate attached"
|
||||||
|
-public let COULD_NOT_FIND_ITEM_ERROR = "Couldn't find item attached to this share"
|
||||||
|
+public let COULD_NOT_FIND_ITEMS_ERROR = "Couldn't find items attached to this share"
|
||||||
|
|
||||||
|
// MARK: Keys
|
||||||
|
|
||||||
|
diff --git a/node_modules/react-native-share-menu/ios/Modules/ShareMenu.swift b/node_modules/react-native-share-menu/ios/Modules/ShareMenu.swift
|
||||||
|
index 6c4922a..74badda 100644
|
||||||
|
--- a/node_modules/react-native-share-menu/ios/Modules/ShareMenu.swift
|
||||||
|
+++ b/node_modules/react-native-share-menu/ios/Modules/ShareMenu.swift
|
||||||
|
@@ -9,7 +9,7 @@ class ShareMenu: RCTEventEmitter {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- var sharedData: [String:String]?
|
||||||
|
+ var sharedData: [[String:String]?]?
|
||||||
|
|
||||||
|
static var initialShare: (UIApplication, URL, [UIApplication.OpenURLOptionsKey : Any])?
|
||||||
|
|
||||||
|
@@ -91,7 +91,7 @@ class ShareMenu: RCTEventEmitter {
|
||||||
|
|
||||||
|
let extraData = userDefaults.object(forKey: USER_DEFAULTS_EXTRA_DATA_KEY) as? [String:Any]
|
||||||
|
|
||||||
|
- if let data = userDefaults.object(forKey: USER_DEFAULTS_KEY) as? [String:String] {
|
||||||
|
+ if let data = userDefaults.object(forKey: USER_DEFAULTS_KEY) as? [[String:String]] {
|
||||||
|
sharedData = data
|
||||||
|
dispatchEvent(with: data, and: extraData)
|
||||||
|
userDefaults.removeObject(forKey: USER_DEFAULTS_KEY)
|
||||||
|
@@ -100,25 +100,22 @@ class ShareMenu: RCTEventEmitter {
|
||||||
|
|
||||||
|
@objc(getSharedText:)
|
||||||
|
func getSharedText(callback: RCTResponseSenderBlock) {
|
||||||
|
- guard var data: [String:Any] = sharedData else {
|
||||||
|
- callback([])
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
+ var data = [DATA_KEY: sharedData] as [String: Any]
|
||||||
|
|
||||||
|
if let bundleId = Bundle.main.bundleIdentifier, let userDefaults = UserDefaults(suiteName: "group.\(bundleId)") {
|
||||||
|
- data[EXTRA_DATA_KEY] = userDefaults.object(forKey: USER_DEFAULTS_EXTRA_DATA_KEY) as? [String:Any]
|
||||||
|
+ data[EXTRA_DATA_KEY] = userDefaults.object(forKey: USER_DEFAULTS_EXTRA_DATA_KEY) as? [String: Any]
|
||||||
|
} else {
|
||||||
|
print("Error: \(NO_APP_GROUP_ERROR)")
|
||||||
|
}
|
||||||
|
|
||||||
|
callback([data as Any])
|
||||||
|
- sharedData = nil
|
||||||
|
+ sharedData = []
|
||||||
|
}
|
||||||
|
|
||||||
|
- func dispatchEvent(with data: [String:String], and extraData: [String:Any]?) {
|
||||||
|
+ func dispatchEvent(with data: [[String:String]], and extraData: [String:Any]?) {
|
||||||
|
guard hasListeners else { return }
|
||||||
|
|
||||||
|
- var finalData = data as [String:Any]
|
||||||
|
+ var finalData = [DATA_KEY: data] as [String: Any]
|
||||||
|
if (extraData != nil) {
|
||||||
|
finalData[EXTRA_DATA_KEY] = extraData
|
||||||
|
}
|
||||||
|
diff --git a/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift b/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift
|
||||||
|
index 5d21773..0c7eaa7 100644
|
||||||
|
--- a/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift
|
||||||
|
+++ b/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift
|
||||||
|
@@ -3,8 +3,9 @@
|
||||||
|
// RNShareMenu
|
||||||
|
//
|
||||||
|
// Created by Gustavo Parreira on 28/07/2020.
|
||||||
|
-//
|
||||||
|
+// Modified by Veselin Stoyanov on 17/04/2021.
|
||||||
|
|
||||||
|
+import Foundation
|
||||||
|
import MobileCoreServices
|
||||||
|
|
||||||
|
@objc(ShareMenuReactView)
|
||||||
|
@@ -17,8 +18,6 @@ public class ShareMenuReactView: NSObject {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func attachViewDelegate(_ delegate: ReactShareViewDelegate!) {
|
||||||
|
- guard (ShareMenuReactView.viewDelegate == nil) else { return }
|
||||||
|
-
|
||||||
|
ShareMenuReactView.viewDelegate = delegate
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -65,12 +64,12 @@ public class ShareMenuReactView: NSObject {
|
||||||
|
|
||||||
|
let extensionContext = viewDelegate.loadExtensionContext()
|
||||||
|
|
||||||
|
- guard let item = extensionContext.inputItems.first as? NSExtensionItem else {
|
||||||
|
- print("Error: \(COULD_NOT_FIND_ITEM_ERROR)")
|
||||||
|
+ guard let items = extensionContext.inputItems as? [NSExtensionItem] else {
|
||||||
|
+ print("Error: \(COULD_NOT_FIND_ITEMS_ERROR)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
- viewDelegate.continueInApp(with: item, and: extraData)
|
||||||
|
+ viewDelegate.continueInApp(with: items, and: extraData)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc(data:reject:)
|
||||||
|
@@ -82,91 +81,96 @@ public class ShareMenuReactView: NSObject {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
- extractDataFromContext(context: extensionContext) { (data, mimeType, error) in
|
||||||
|
+ extractDataFromContext(context: extensionContext) { (data, error) in
|
||||||
|
guard (error == nil) else {
|
||||||
|
reject("error", error?.description, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
- resolve([MIME_TYPE_KEY: mimeType, DATA_KEY: data])
|
||||||
|
+ resolve([DATA_KEY: data])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- func extractDataFromContext(context: NSExtensionContext, withCallback callback: @escaping (String?, String?, NSException?) -> Void) {
|
||||||
|
- let item:NSExtensionItem! = context.inputItems.first as? NSExtensionItem
|
||||||
|
- let attachments:[AnyObject]! = item.attachments
|
||||||
|
-
|
||||||
|
- var urlProvider:NSItemProvider! = nil
|
||||||
|
- var imageProvider:NSItemProvider! = nil
|
||||||
|
- var textProvider:NSItemProvider! = nil
|
||||||
|
- var dataProvider:NSItemProvider! = nil
|
||||||
|
-
|
||||||
|
- for provider in attachments {
|
||||||
|
- if provider.hasItemConformingToTypeIdentifier(kUTTypeURL as String) {
|
||||||
|
- urlProvider = provider as? NSItemProvider
|
||||||
|
- break
|
||||||
|
- } else if provider.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
|
||||||
|
- textProvider = provider as? NSItemProvider
|
||||||
|
- break
|
||||||
|
- } else if provider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
|
||||||
|
- imageProvider = provider as? NSItemProvider
|
||||||
|
- break
|
||||||
|
- } else if provider.hasItemConformingToTypeIdentifier(kUTTypeData as String) {
|
||||||
|
- dataProvider = provider as? NSItemProvider
|
||||||
|
- break
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
+ func extractDataFromContext(context: NSExtensionContext, withCallback callback: @escaping ([Any]?, NSException?) -> Void) {
|
||||||
|
+ DispatchQueue.global().async {
|
||||||
|
+ let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
+ let items:[NSExtensionItem]! = context.inputItems as? [NSExtensionItem]
|
||||||
|
+ var results: [[String: String]] = []
|
||||||
|
|
||||||
|
- if (urlProvider != nil) {
|
||||||
|
- urlProvider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) { (item, error) in
|
||||||
|
- let url: URL! = item as? URL
|
||||||
|
+ for item in items {
|
||||||
|
+ guard let attachments = item.attachments else {
|
||||||
|
+ callback(nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"couldn't find attachments", userInfo:nil))
|
||||||
|
+ return
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- callback(url.absoluteString, "text/plain", nil)
|
||||||
|
- }
|
||||||
|
- } else if (imageProvider != nil) {
|
||||||
|
- imageProvider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { (item, error) in
|
||||||
|
- let imageUrl: URL! = item as? URL
|
||||||
|
+ for provider in attachments {
|
||||||
|
+ if provider.hasItemConformingToTypeIdentifier(kUTTypeURL as String) {
|
||||||
|
+ provider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) { (item, error) in
|
||||||
|
+ let url: URL! = item as? URL
|
||||||
|
|
||||||
|
- if (imageUrl != nil) {
|
||||||
|
- if let imageData = try? Data(contentsOf: imageUrl) {
|
||||||
|
- callback(imageUrl.absoluteString, self.extractMimeType(from: imageUrl), nil)
|
||||||
|
- }
|
||||||
|
- } else {
|
||||||
|
- let image: UIImage! = item as? UIImage
|
||||||
|
+ results.append([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: "text/plain"])
|
||||||
|
|
||||||
|
- if (image != nil) {
|
||||||
|
- let imageData: Data! = image.pngData();
|
||||||
|
+ semaphore.signal()
|
||||||
|
+ }
|
||||||
|
+ semaphore.wait()
|
||||||
|
+ } else if provider.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
|
||||||
|
+ provider.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil) { (item, error) in
|
||||||
|
+ let text:String! = item as? String
|
||||||
|
+
|
||||||
|
+ results.append([DATA_KEY: text, MIME_TYPE_KEY: "text/plain"])
|
||||||
|
|
||||||
|
- // Creating temporary URL for image data (UIImage)
|
||||||
|
- guard let imageURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("TemporaryScreenshot.png") else {
|
||||||
|
- return
|
||||||
|
+ semaphore.signal()
|
||||||
|
+ }
|
||||||
|
+ semaphore.wait()
|
||||||
|
+ } else if provider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
|
||||||
|
+ provider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { (item, error) in
|
||||||
|
+ let imageUrl: URL! = item as? URL
|
||||||
|
+
|
||||||
|
+ if (imageUrl != nil) {
|
||||||
|
+ if let imageData = try? Data(contentsOf: imageUrl) {
|
||||||
|
+ results.append([DATA_KEY: imageUrl.absoluteString, MIME_TYPE_KEY: self.extractMimeType(from: imageUrl)])
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ let image: UIImage! = item as? UIImage
|
||||||
|
+
|
||||||
|
+ if (image != nil) {
|
||||||
|
+ let imageData: Data! = image.pngData();
|
||||||
|
+
|
||||||
|
+ // Creating temporary URL for image data (UIImage)
|
||||||
|
+ guard let imageURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("TemporaryScreenshot.png") else {
|
||||||
|
+ return
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ do {
|
||||||
|
+ // Writing the image to the URL
|
||||||
|
+ try imageData.write(to: imageURL)
|
||||||
|
+
|
||||||
|
+ results.append([DATA_KEY: imageUrl.absoluteString, MIME_TYPE_KEY: imageURL.extractMimeType()])
|
||||||
|
+ } catch {
|
||||||
|
+ callback(nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"Can't load image", userInfo:nil))
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ semaphore.signal()
|
||||||
|
}
|
||||||
|
+ semaphore.wait()
|
||||||
|
+ } else if provider.hasItemConformingToTypeIdentifier(kUTTypeData as String) {
|
||||||
|
+ provider.loadItem(forTypeIdentifier: kUTTypeData as String, options: nil) { (item, error) in
|
||||||
|
+ let url: URL! = item as? URL
|
||||||
|
|
||||||
|
- do {
|
||||||
|
- // Writing the image to the URL
|
||||||
|
- try imageData.write(to: imageURL)
|
||||||
|
+ results.append([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: self.extractMimeType(from: url)])
|
||||||
|
|
||||||
|
- callback(imageURL.absoluteString, imageURL.extractMimeType(), nil)
|
||||||
|
- } catch {
|
||||||
|
- callback(nil, nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"Can't load image", userInfo:nil))
|
||||||
|
+ semaphore.signal()
|
||||||
|
}
|
||||||
|
+ semaphore.wait()
|
||||||
|
+ } else {
|
||||||
|
+ callback(nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"couldn't find provider", userInfo:nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- } else if (textProvider != nil) {
|
||||||
|
- textProvider.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil) { (item, error) in
|
||||||
|
- let text:String! = item as? String
|
||||||
|
|
||||||
|
- callback(text, "text/plain", nil)
|
||||||
|
- }
|
||||||
|
- } else if (dataProvider != nil) {
|
||||||
|
- dataProvider.loadItem(forTypeIdentifier: kUTTypeData as String, options: nil) { (item, error) in
|
||||||
|
- let url: URL! = item as? URL
|
||||||
|
-
|
||||||
|
- callback(url.absoluteString, self.extractMimeType(from: url), nil)
|
||||||
|
- }
|
||||||
|
- } else {
|
||||||
|
- callback(nil, nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"couldn't find provider", userInfo:nil))
|
||||||
|
+ callback(results, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/node_modules/react-native-share-menu/ios/ReactShareViewController.swift b/node_modules/react-native-share-menu/ios/ReactShareViewController.swift
|
||||||
|
index 0189ef6..f42bce6 100644
|
||||||
|
--- a/node_modules/react-native-share-menu/ios/ReactShareViewController.swift
|
||||||
|
+++ b/node_modules/react-native-share-menu/ios/ReactShareViewController.swift
|
||||||
|
@@ -62,7 +62,7 @@ class ReactShareViewController: ShareViewController, RCTBridgeDelegate, ReactSha
|
||||||
|
self.openHostApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
- func continueInApp(with item: NSExtensionItem, and extraData: [String:Any]?) {
|
||||||
|
- handlePost(item, extraData: extraData)
|
||||||
|
+ func continueInApp(with items: [NSExtensionItem], and extraData: [String:Any]?) {
|
||||||
|
+ handlePost(items, extraData: extraData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diff --git a/node_modules/react-native-share-menu/ios/ReactShareViewDelegate.swift b/node_modules/react-native-share-menu/ios/ReactShareViewDelegate.swift
|
||||||
|
index 0aa4c58..ad0812c 100644
|
||||||
|
--- a/node_modules/react-native-share-menu/ios/ReactShareViewDelegate.swift
|
||||||
|
+++ b/node_modules/react-native-share-menu/ios/ReactShareViewDelegate.swift
|
||||||
|
@@ -10,5 +10,5 @@ public protocol ReactShareViewDelegate {
|
||||||
|
|
||||||
|
func openApp()
|
||||||
|
|
||||||
|
- func continueInApp(with item: NSExtensionItem, and extraData: [String:Any]?)
|
||||||
|
+ func continueInApp(with items: [NSExtensionItem], and extraData: [String:Any]?)
|
||||||
|
}
|
||||||
|
diff --git a/node_modules/react-native-share-menu/ios/ShareViewController.swift b/node_modules/react-native-share-menu/ios/ShareViewController.swift
|
||||||
|
index 7faf6e4..f02bde5 100644
|
||||||
|
--- a/node_modules/react-native-share-menu/ios/ShareViewController.swift
|
||||||
|
+++ b/node_modules/react-native-share-menu/ios/ShareViewController.swift
|
||||||
|
@@ -6,15 +6,18 @@
|
||||||
|
//
|
||||||
|
// Created by Gustavo Parreira on 26/07/2020.
|
||||||
|
//
|
||||||
|
+// Modified by Veselin Stoyanov on 17/04/2021.
|
||||||
|
|
||||||
|
+import Foundation
|
||||||
|
import MobileCoreServices
|
||||||
|
import UIKit
|
||||||
|
import Social
|
||||||
|
import RNShareMenu
|
||||||
|
|
||||||
|
-class ShareViewController: SLComposeServiceViewController {
|
||||||
|
+class ShareViewController: UIViewController {
|
||||||
|
var hostAppId: String?
|
||||||
|
var hostAppUrlScheme: String?
|
||||||
|
+ var sharedItems: [Any] = []
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
@@ -30,46 +33,64 @@ class ShareViewController: SLComposeServiceViewController {
|
||||||
|
} else {
|
||||||
|
print("Error: \(NO_INFO_PLIST_URL_SCHEME_ERROR)")
|
||||||
|
}
|
||||||
|
- }
|
||||||
|
|
||||||
|
- override func isContentValid() -> Bool {
|
||||||
|
- // Do validation of contentText and/or NSExtensionContext attachments here
|
||||||
|
- return true
|
||||||
|
+ guard let items = extensionContext?.inputItems as? [NSExtensionItem] else {
|
||||||
|
+ cancelRequest()
|
||||||
|
+ return
|
||||||
|
}
|
||||||
|
|
||||||
|
- override func didSelectPost() {
|
||||||
|
- // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
|
||||||
|
- guard let item = extensionContext?.inputItems.first as? NSExtensionItem else {
|
||||||
|
- cancelRequest()
|
||||||
|
+ handlePost(items)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ override func viewDidAppear(_ animated: Bool) {
|
||||||
|
+ super.viewDidAppear(animated)
|
||||||
|
+
|
||||||
|
+ completeRequest()
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ func handlePost(_ items: [NSExtensionItem], extraData: [String:Any]? = nil) {
|
||||||
|
+ DispatchQueue.global().async {
|
||||||
|
+ guard let hostAppId = self.hostAppId else {
|
||||||
|
+ self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR)
|
||||||
|
+ return
|
||||||
|
+ }
|
||||||
|
+ guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else {
|
||||||
|
+ self.exit(withError: NO_APP_GROUP_ERROR)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
- handlePost(item)
|
||||||
|
- }
|
||||||
|
+ if let data = extraData {
|
||||||
|
+ self.storeExtraData(data)
|
||||||
|
+ } else {
|
||||||
|
+ self.removeExtraData()
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- override func configurationItems() -> [Any]! {
|
||||||
|
- // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
|
||||||
|
- return []
|
||||||
|
- }
|
||||||
|
+ let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
|
||||||
|
- func handlePost(_ item: NSExtensionItem, extraData: [String:Any]? = nil) {
|
||||||
|
- guard let provider = item.attachments?.first else {
|
||||||
|
- cancelRequest()
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
+ for item in items {
|
||||||
|
+ guard let attachments = item.attachments else {
|
||||||
|
+ self.cancelRequest()
|
||||||
|
+ return
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- if let data = extraData {
|
||||||
|
- storeExtraData(data)
|
||||||
|
- } else {
|
||||||
|
- removeExtraData()
|
||||||
|
- }
|
||||||
|
+ for provider in attachments {
|
||||||
|
+ if provider.isText {
|
||||||
|
+ self.storeText(withProvider: provider, semaphore)
|
||||||
|
+ } else if provider.isURL {
|
||||||
|
+ self.storeUrl(withProvider: provider, semaphore)
|
||||||
|
+ } else {
|
||||||
|
+ self.storeFile(withProvider: provider, semaphore)
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- if provider.isText {
|
||||||
|
- storeText(withProvider: provider)
|
||||||
|
- } else if provider.isURL {
|
||||||
|
- storeUrl(withProvider: provider)
|
||||||
|
- } else {
|
||||||
|
- storeFile(withProvider: provider)
|
||||||
|
+ semaphore.wait()
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ userDefaults.set(self.sharedItems,
|
||||||
|
+ forKey: USER_DEFAULTS_KEY)
|
||||||
|
+ userDefaults.synchronize()
|
||||||
|
+
|
||||||
|
+ self.openHostApp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -99,7 +120,7 @@ class ShareViewController: SLComposeServiceViewController {
|
||||||
|
userDefaults.synchronize()
|
||||||
|
}
|
||||||
|
|
||||||
|
- func storeText(withProvider provider: NSItemProvider) {
|
||||||
|
+ func storeText(withProvider provider: NSItemProvider, _ semaphore: DispatchSemaphore) {
|
||||||
|
provider.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil) { (data, error) in
|
||||||
|
guard (error == nil) else {
|
||||||
|
self.exit(withError: error.debugDescription)
|
||||||
|
@@ -109,24 +130,13 @@ class ShareViewController: SLComposeServiceViewController {
|
||||||
|
self.exit(withError: COULD_NOT_FIND_STRING_ERROR)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
- guard let hostAppId = self.hostAppId else {
|
||||||
|
- self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR)
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
- guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else {
|
||||||
|
- self.exit(withError: NO_APP_GROUP_ERROR)
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- userDefaults.set([DATA_KEY: text, MIME_TYPE_KEY: "text/plain"],
|
||||||
|
- forKey: USER_DEFAULTS_KEY)
|
||||||
|
- userDefaults.synchronize()
|
||||||
|
|
||||||
|
- self.openHostApp()
|
||||||
|
+ self.sharedItems.append([DATA_KEY: text, MIME_TYPE_KEY: "text/plain"])
|
||||||
|
+ semaphore.signal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- func storeUrl(withProvider provider: NSItemProvider) {
|
||||||
|
+ func storeUrl(withProvider provider: NSItemProvider, _ semaphore: DispatchSemaphore) {
|
||||||
|
provider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) { (data, error) in
|
||||||
|
guard (error == nil) else {
|
||||||
|
self.exit(withError: error.debugDescription)
|
||||||
|
@@ -136,24 +146,13 @@ class ShareViewController: SLComposeServiceViewController {
|
||||||
|
self.exit(withError: COULD_NOT_FIND_URL_ERROR)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
- guard let hostAppId = self.hostAppId else {
|
||||||
|
- self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR)
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
- guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else {
|
||||||
|
- self.exit(withError: NO_APP_GROUP_ERROR)
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- userDefaults.set([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: "text/plain"],
|
||||||
|
- forKey: USER_DEFAULTS_KEY)
|
||||||
|
- userDefaults.synchronize()
|
||||||
|
|
||||||
|
- self.openHostApp()
|
||||||
|
+ self.sharedItems.append([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: "text/plain"])
|
||||||
|
+ semaphore.signal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- func storeFile(withProvider provider: NSItemProvider) {
|
||||||
|
+ func storeFile(withProvider provider: NSItemProvider, _ semaphore: DispatchSemaphore) {
|
||||||
|
provider.loadItem(forTypeIdentifier: kUTTypeData as String, options: nil) { (data, error) in
|
||||||
|
guard (error == nil) else {
|
||||||
|
self.exit(withError: error.debugDescription)
|
||||||
|
@@ -167,10 +166,6 @@ class ShareViewController: SLComposeServiceViewController {
|
||||||
|
self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
- guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else {
|
||||||
|
- self.exit(withError: NO_APP_GROUP_ERROR)
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
guard let groupFileManagerContainer = FileManager.default
|
||||||
|
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppId)")
|
||||||
|
else {
|
||||||
|
@@ -189,11 +184,8 @@ class ShareViewController: SLComposeServiceViewController {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
- userDefaults.set([DATA_KEY: filePath.absoluteString, MIME_TYPE_KEY: mimeType],
|
||||||
|
- forKey: USER_DEFAULTS_KEY)
|
||||||
|
- userDefaults.synchronize()
|
||||||
|
-
|
||||||
|
- self.openHostApp()
|
||||||
|
+ self.sharedItems.append([DATA_KEY: filePath.absoluteString, MIME_TYPE_KEY: mimeType])
|
||||||
|
+ semaphore.signal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -320,8 +320,6 @@ declare namespace Mastodon {
|
||||||
max_expiration: number
|
max_expiration: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Custom - to be deprecated in v4
|
|
||||||
max_toot_chars?: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mention = {
|
type Mention = {
|
||||||
|
@ -428,6 +426,7 @@ declare namespace Mastodon {
|
||||||
reblogs_count: number
|
reblogs_count: number
|
||||||
favourites_count: number
|
favourites_count: number
|
||||||
replies_count: number
|
replies_count: number
|
||||||
|
edited_at?: string // FEATURE edit_post
|
||||||
favourited: boolean
|
favourited: boolean
|
||||||
reblogged: boolean
|
reblogged: boolean
|
||||||
muted: boolean
|
muted: boolean
|
||||||
|
@ -445,6 +444,17 @@ declare namespace Mastodon {
|
||||||
text?: string
|
text?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StatusHistory = {
|
||||||
|
content: Status['content']
|
||||||
|
spoiler_text: Status['spoiler_text']
|
||||||
|
sensitive: Status['sensitive']
|
||||||
|
created_at: Status['created_at']
|
||||||
|
poll: Status['poll']
|
||||||
|
account: Status['account']
|
||||||
|
media_attachments: Status['media_attachments']
|
||||||
|
emojis: Status['emojis']
|
||||||
|
}
|
||||||
|
|
||||||
type Source = {
|
type Source = {
|
||||||
// Base
|
// Base
|
||||||
note: string
|
note: string
|
||||||
|
|
|
@ -4,3 +4,8 @@ declare module 'li'
|
||||||
declare module 'react-native-feather'
|
declare module 'react-native-feather'
|
||||||
declare module 'react-native-htmlview'
|
declare module 'react-native-htmlview'
|
||||||
declare module 'react-native-toast-message'
|
declare module 'react-native-toast-message'
|
||||||
|
|
||||||
|
declare module '@helpers/features' {
|
||||||
|
const features: { feature: string; version: number; reference?: string }[]
|
||||||
|
export default features
|
||||||
|
}
|
||||||
|
|
43
src/App.tsx
43
src/App.tsx
|
@ -1,4 +1,31 @@
|
||||||
import { ActionSheetProvider } from '@expo/react-native-action-sheet'
|
import { ActionSheetProvider } from '@expo/react-native-action-sheet'
|
||||||
|
import '@formatjs/intl-getcanonicallocales/polyfill'
|
||||||
|
import '@formatjs/intl-locale/polyfill'
|
||||||
|
import '@formatjs/intl-pluralrules/polyfill'
|
||||||
|
import '@formatjs/intl-pluralrules/locale-data/de'
|
||||||
|
import '@formatjs/intl-pluralrules/locale-data/en'
|
||||||
|
import '@formatjs/intl-pluralrules/locale-data/ko'
|
||||||
|
import '@formatjs/intl-pluralrules/locale-data/vi'
|
||||||
|
import '@formatjs/intl-pluralrules/locale-data/zh'
|
||||||
|
import '@formatjs/intl-numberformat/polyfill'
|
||||||
|
import '@formatjs/intl-numberformat/locale-data/de'
|
||||||
|
import '@formatjs/intl-numberformat/locale-data/en'
|
||||||
|
import '@formatjs/intl-numberformat/locale-data/ko'
|
||||||
|
import '@formatjs/intl-numberformat/locale-data/vi'
|
||||||
|
import '@formatjs/intl-numberformat/locale-data/zh'
|
||||||
|
import '@formatjs/intl-datetimeformat/polyfill'
|
||||||
|
import '@formatjs/intl-datetimeformat/locale-data/de'
|
||||||
|
import '@formatjs/intl-datetimeformat/locale-data/en'
|
||||||
|
import '@formatjs/intl-datetimeformat/locale-data/ko'
|
||||||
|
import '@formatjs/intl-datetimeformat/locale-data/vi'
|
||||||
|
import '@formatjs/intl-datetimeformat/locale-data/zh'
|
||||||
|
import '@formatjs/intl-datetimeformat/add-all-tz'
|
||||||
|
import '@formatjs/intl-relativetimeformat/polyfill'
|
||||||
|
import '@formatjs/intl-relativetimeformat/locale-data/de'
|
||||||
|
import '@formatjs/intl-relativetimeformat/locale-data/en'
|
||||||
|
import '@formatjs/intl-relativetimeformat/locale-data/ko'
|
||||||
|
import '@formatjs/intl-relativetimeformat/locale-data/vi'
|
||||||
|
import '@formatjs/intl-relativetimeformat/locale-data/zh'
|
||||||
import queryClient from '@helpers/queryClient'
|
import queryClient from '@helpers/queryClient'
|
||||||
import i18n from '@root/i18n/i18n'
|
import i18n from '@root/i18n/i18n'
|
||||||
import Screens from '@root/Screens'
|
import Screens from '@root/Screens'
|
||||||
|
@ -6,7 +33,9 @@ import audio from '@root/startup/audio'
|
||||||
import dev from '@root/startup/dev'
|
import dev from '@root/startup/dev'
|
||||||
import log from '@root/startup/log'
|
import log from '@root/startup/log'
|
||||||
import netInfo from '@root/startup/netInfo'
|
import netInfo from '@root/startup/netInfo'
|
||||||
|
import push from '@root/startup/push'
|
||||||
import sentry from '@root/startup/sentry'
|
import sentry from '@root/startup/sentry'
|
||||||
|
import timezone from '@root/startup/timezone'
|
||||||
import { persistor, store } from '@root/store'
|
import { persistor, store } from '@root/store'
|
||||||
import AccessibilityManager from '@utils/accessibility/AccessibilityManager'
|
import AccessibilityManager from '@utils/accessibility/AccessibilityManager'
|
||||||
import {
|
import {
|
||||||
|
@ -14,16 +43,18 @@ import {
|
||||||
getSettingsLanguage
|
getSettingsLanguage
|
||||||
} from '@utils/slices/settingsSlice'
|
} from '@utils/slices/settingsSlice'
|
||||||
import ThemeManager from '@utils/styles/ThemeManager'
|
import ThemeManager from '@utils/styles/ThemeManager'
|
||||||
|
import 'expo-asset'
|
||||||
import * as Notifications from 'expo-notifications'
|
import * as Notifications from 'expo-notifications'
|
||||||
import * as SplashScreen from 'expo-splash-screen'
|
import * as SplashScreen from 'expo-splash-screen'
|
||||||
import React, { useCallback, useEffect, useState } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
import { AppState, LogBox, Platform } from 'react-native'
|
import { AppState, LogBox, Platform } from 'react-native'
|
||||||
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
||||||
|
import 'react-native-image-keyboard'
|
||||||
import { enableFreeze } from 'react-native-screens'
|
import { enableFreeze } from 'react-native-screens'
|
||||||
import { QueryClientProvider } from 'react-query'
|
import { QueryClientProvider } from 'react-query'
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
import { PersistGate } from 'redux-persist/integration/react'
|
import { PersistGate } from 'redux-persist/integration/react'
|
||||||
import push from './startup/push'
|
import { IntlProvider } from 'react-intl'
|
||||||
|
|
||||||
Platform.select({
|
Platform.select({
|
||||||
android: LogBox.ignoreLogs(['Setting a timer for a long period of time'])
|
android: LogBox.ignoreLogs(['Setting a timer for a long period of time'])
|
||||||
|
@ -33,6 +64,7 @@ dev()
|
||||||
sentry()
|
sentry()
|
||||||
audio()
|
audio()
|
||||||
push()
|
push()
|
||||||
|
timezone()
|
||||||
enableFreeze(true)
|
enableFreeze(true)
|
||||||
|
|
||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
|
@ -91,13 +123,18 @@ const App: React.FC = () => {
|
||||||
const language = getSettingsLanguage(store.getState())
|
const language = getSettingsLanguage(store.getState())
|
||||||
if (!language) {
|
if (!language) {
|
||||||
store.dispatch(changeLanguage('en'))
|
store.dispatch(changeLanguage('en'))
|
||||||
|
i18n.changeLanguage('en')
|
||||||
|
} else {
|
||||||
|
i18n.changeLanguage(language)
|
||||||
}
|
}
|
||||||
i18n.changeLanguage(language)
|
|
||||||
return (
|
return (
|
||||||
<ActionSheetProvider>
|
<ActionSheetProvider>
|
||||||
<AccessibilityManager>
|
<AccessibilityManager>
|
||||||
<ThemeManager>
|
<ThemeManager>
|
||||||
<Screens localCorrupt={localCorrupt} />
|
<IntlProvider locale={language}>
|
||||||
|
<Screens localCorrupt={localCorrupt} />
|
||||||
|
</IntlProvider>
|
||||||
</ThemeManager>
|
</ThemeManager>
|
||||||
</AccessibilityManager>
|
</AccessibilityManager>
|
||||||
</ActionSheetProvider>
|
</ActionSheetProvider>
|
||||||
|
|
166
src/Screens.tsx
166
src/Screens.tsx
|
@ -27,8 +27,10 @@ import { addScreenshotListener } from 'expo-screen-capture'
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Alert, Platform, StatusBar } from 'react-native'
|
import { Alert, Platform, StatusBar } from 'react-native'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import ShareMenu from 'react-native-share-menu'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
import * as Sentry from 'sentry-expo'
|
import * as Sentry from 'sentry-expo'
|
||||||
|
import { useAppDispatch } from './store'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<RootStackParamList>()
|
const Stack = createNativeStackNavigator<RootStackParamList>()
|
||||||
|
|
||||||
|
@ -38,7 +40,7 @@ export interface Props {
|
||||||
|
|
||||||
const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
||||||
const { t } = useTranslation('screens')
|
const { t } = useTranslation('screens')
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const instanceActive = useSelector(getInstanceActive)
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
const { colors, theme } = useTheme()
|
const { colors, theme } = useTheme()
|
||||||
|
|
||||||
|
@ -157,6 +159,166 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
||||||
}
|
}
|
||||||
}, [instanceActive, instances, deeplinked])
|
}, [instanceActive, instances, deeplinked])
|
||||||
|
|
||||||
|
// Share Extension
|
||||||
|
const handleShare = useCallback(
|
||||||
|
(
|
||||||
|
item?:
|
||||||
|
| {
|
||||||
|
data: { mimeType: string; data: string }[]
|
||||||
|
mimeType: undefined
|
||||||
|
}
|
||||||
|
| { data: string | string[]; mimeType: string }
|
||||||
|
) => {
|
||||||
|
if (instanceActive < 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!item || !item.data) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let text: string | undefined = undefined
|
||||||
|
let images: { type: string; uri: string }[] = []
|
||||||
|
let video: { type: string; uri: string } | undefined = undefined
|
||||||
|
|
||||||
|
switch (Platform.OS) {
|
||||||
|
case 'ios':
|
||||||
|
if (!Array.isArray(item.data) || !item.data) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
item.data.forEach(d => {
|
||||||
|
if (typeof d === 'string') return
|
||||||
|
const typesImage = ['png', 'jpg', 'jpeg', 'gif']
|
||||||
|
const typesVideo = ['mp4', 'm4v', 'mov', 'webm']
|
||||||
|
const { mimeType, data } = d
|
||||||
|
if (mimeType.startsWith('image/')) {
|
||||||
|
if (!typesImage.includes(mimeType.split('/')[1])) {
|
||||||
|
console.warn(
|
||||||
|
'Image type not supported:',
|
||||||
|
mimeType.split('/')[1]
|
||||||
|
)
|
||||||
|
displayMessage({
|
||||||
|
message: t('shareError.imageNotSupported', {
|
||||||
|
type: mimeType.split('/')[1]
|
||||||
|
}),
|
||||||
|
type: 'error',
|
||||||
|
theme
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
images.push({ type: mimeType.split('/')[1], uri: data })
|
||||||
|
} else if (mimeType.startsWith('video/')) {
|
||||||
|
if (!typesVideo.includes(mimeType.split('/')[1])) {
|
||||||
|
console.warn(
|
||||||
|
'Video type not supported:',
|
||||||
|
mimeType.split('/')[1]
|
||||||
|
)
|
||||||
|
displayMessage({
|
||||||
|
message: t('shareError.videoNotSupported', {
|
||||||
|
type: mimeType.split('/')[1]
|
||||||
|
}),
|
||||||
|
type: 'error',
|
||||||
|
theme
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
video = { type: mimeType.split('/')[1], uri: data }
|
||||||
|
} else {
|
||||||
|
if (typesImage.includes(data.split('.').pop() || '')) {
|
||||||
|
images.push({ type: data.split('.').pop()!, uri: data })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typesVideo.includes(data.split('.').pop() || '')) {
|
||||||
|
video = { type: data.split('.').pop()!, uri: data }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
text = !text ? data : text.concat(text, `\n${data}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'android':
|
||||||
|
if (!item.mimeType) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let tempData: string[]
|
||||||
|
if (!Array.isArray(item.data)) {
|
||||||
|
tempData = [item.data]
|
||||||
|
} else {
|
||||||
|
tempData = item.data
|
||||||
|
}
|
||||||
|
tempData.forEach(d => {
|
||||||
|
const typesImage = ['png', 'jpg', 'jpeg', 'gif']
|
||||||
|
const typesVideo = ['mp4', 'm4v', 'mov', 'webm', 'mpeg']
|
||||||
|
if (item.mimeType!.startsWith('image/')) {
|
||||||
|
if (!typesImage.includes(item.mimeType.split('/')[1])) {
|
||||||
|
console.warn(
|
||||||
|
'Image type not supported:',
|
||||||
|
item.mimeType.split('/')[1]
|
||||||
|
)
|
||||||
|
displayMessage({
|
||||||
|
message: t('shareError.imageNotSupported', {
|
||||||
|
type: item.mimeType.split('/')[1]
|
||||||
|
}),
|
||||||
|
type: 'error',
|
||||||
|
theme
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
images.push({ type: item.mimeType.split('/')[1], uri: d })
|
||||||
|
} else if (item.mimeType.startsWith('video/')) {
|
||||||
|
if (!typesVideo.includes(item.mimeType.split('/')[1])) {
|
||||||
|
console.warn(
|
||||||
|
'Video type not supported:',
|
||||||
|
item.mimeType.split('/')[1]
|
||||||
|
)
|
||||||
|
displayMessage({
|
||||||
|
message: t('shareError.videoNotSupported', {
|
||||||
|
type: item.mimeType.split('/')[1]
|
||||||
|
}),
|
||||||
|
type: 'error',
|
||||||
|
theme
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
video = { type: item.mimeType.split('/')[1], uri: d }
|
||||||
|
} else {
|
||||||
|
if (typesImage.includes(d.split('.').pop() || '')) {
|
||||||
|
images.push({ type: d.split('.').pop()!, uri: d })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typesVideo.includes(d.split('.').pop() || '')) {
|
||||||
|
video = { type: d.split('.').pop()!, uri: d }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
text = !text ? d : text.concat(text, `\n${d}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!text && (!images || !images.length) && !video) {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
navigationRef.navigate('Screen-Compose', {
|
||||||
|
type: 'share',
|
||||||
|
text,
|
||||||
|
images,
|
||||||
|
video
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[instanceActive]
|
||||||
|
)
|
||||||
|
useEffect(() => {
|
||||||
|
ShareMenu.getInitialShare(handleShare)
|
||||||
|
}, [])
|
||||||
|
useEffect(() => {
|
||||||
|
const listener = ShareMenu.addNewShareListener(handleShare)
|
||||||
|
return () => {
|
||||||
|
listener.remove()
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StatusBar backgroundColor={colors.backgroundDefault} />
|
<StatusBar backgroundColor={colors.backgroundDefault} />
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
import Constants from 'expo-constants'
|
import Constants from 'expo-constants'
|
||||||
import * as Sentry from 'sentry-expo'
|
|
||||||
|
|
||||||
const ctx = new chalk.Instance({ level: 3 })
|
const ctx = new chalk.Instance({ level: 3 })
|
||||||
|
|
||||||
|
@ -14,7 +13,6 @@ export type Params = {
|
||||||
}
|
}
|
||||||
headers?: { [key: string]: string }
|
headers?: { [key: string]: string }
|
||||||
body?: FormData | Object
|
body?: FormData | Object
|
||||||
sentry?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiGeneral = async <T = unknown>({
|
const apiGeneral = async <T = unknown>({
|
||||||
|
@ -23,8 +21,7 @@ const apiGeneral = async <T = unknown>({
|
||||||
url,
|
url,
|
||||||
params,
|
params,
|
||||||
headers,
|
headers,
|
||||||
body,
|
body
|
||||||
sentry = true
|
|
||||||
}: Params): Promise<{ body: T }> => {
|
}: Params): Promise<{ body: T }> => {
|
||||||
console.log(
|
console.log(
|
||||||
ctx.bgGreen.bold(' API general ') +
|
ctx.bgGreen.bold(' API general ') +
|
||||||
|
@ -45,7 +42,10 @@ const apiGeneral = async <T = unknown>({
|
||||||
url,
|
url,
|
||||||
params,
|
params,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type':
|
||||||
|
body && body instanceof FormData
|
||||||
|
? 'multipart/form-data'
|
||||||
|
: 'application/json',
|
||||||
'User-Agent': `tooot/${Constants.manifest?.version}`,
|
'User-Agent': `tooot/${Constants.manifest?.version}`,
|
||||||
Accept: '*/*',
|
Accept: '*/*',
|
||||||
...headers
|
...headers
|
||||||
|
|
|
@ -3,7 +3,6 @@ import axios, { AxiosRequestConfig } from 'axios'
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
import Constants from 'expo-constants'
|
import Constants from 'expo-constants'
|
||||||
import li from 'li'
|
import li from 'li'
|
||||||
import * as Sentry from 'sentry-expo'
|
|
||||||
|
|
||||||
const ctx = new chalk.Instance({ level: 3 })
|
const ctx = new chalk.Instance({ level: 3 })
|
||||||
|
|
||||||
|
@ -73,7 +72,10 @@ const apiInstance = async <T = unknown>({
|
||||||
url,
|
url,
|
||||||
params,
|
params,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type':
|
||||||
|
body && body instanceof FormData
|
||||||
|
? 'multipart/form-data'
|
||||||
|
: 'application/json',
|
||||||
'User-Agent': `tooot/${Constants.manifest?.version}`,
|
'User-Agent': `tooot/${Constants.manifest?.version}`,
|
||||||
Accept: '*/*',
|
Accept: '*/*',
|
||||||
...headers,
|
...headers,
|
||||||
|
|
|
@ -48,7 +48,10 @@ const apiTooot = async <T = unknown>({
|
||||||
url: `${url}`,
|
url: `${url}`,
|
||||||
params,
|
params,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type':
|
||||||
|
body && body instanceof FormData
|
||||||
|
? 'multipart/form-data'
|
||||||
|
: 'application/json',
|
||||||
'User-Agent': `tooot/${Constants.manifest?.version}`,
|
'User-Agent': `tooot/${Constants.manifest?.version}`,
|
||||||
Accept: '*/*',
|
Accept: '*/*',
|
||||||
...headers
|
...headers
|
||||||
|
|
|
@ -5,9 +5,10 @@ import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import analytics from './analytics'
|
import analytics from './analytics'
|
||||||
import GracefullyImage from './GracefullyImage'
|
import GracefullyImage from './GracefullyImage'
|
||||||
|
import CustomText from './Text'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
account: Mastodon.Account
|
account: Mastodon.Account
|
||||||
|
@ -32,50 +33,45 @@ const ComponentAccount: React.FC<Props> = ({
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
accessibilityRole='button'
|
accessibilityRole='button'
|
||||||
style={[styles.itemDefault, styles.itemAccount]}
|
style={{
|
||||||
|
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||||
|
paddingVertical: StyleConstants.Spacing.M,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
onPress={customOnPress || onPress}
|
onPress={customOnPress || onPress}
|
||||||
>
|
>
|
||||||
<GracefullyImage
|
<GracefullyImage
|
||||||
uri={{ original: account.avatar, static: account.avatar_static }}
|
uri={{ original: account.avatar, static: account.avatar_static }}
|
||||||
style={styles.itemAccountAvatar}
|
style={{
|
||||||
|
alignSelf: 'flex-start',
|
||||||
|
width: StyleConstants.Avatar.S,
|
||||||
|
height: StyleConstants.Avatar.S,
|
||||||
|
borderRadius: 6,
|
||||||
|
marginRight: StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<View>
|
<View>
|
||||||
<Text numberOfLines={1}>
|
<CustomText numberOfLines={1}>
|
||||||
<ParseEmojis
|
<ParseEmojis
|
||||||
content={account.display_name || account.username}
|
content={account.display_name || account.username}
|
||||||
emojis={account.emojis}
|
emojis={account.emojis}
|
||||||
size='S'
|
size='S'
|
||||||
fontBold
|
fontBold
|
||||||
/>
|
/>
|
||||||
</Text>
|
</CustomText>
|
||||||
<Text
|
<CustomText
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
style={[styles.itemAccountAcct, { color: colors.secondary }]}
|
style={{
|
||||||
|
marginTop: StyleConstants.Spacing.XS,
|
||||||
|
color: colors.secondary
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
@{account.acct}
|
@{account.acct}
|
||||||
</Text>
|
</CustomText>
|
||||||
</View>
|
</View>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
itemDefault: {
|
|
||||||
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
|
||||||
paddingVertical: StyleConstants.Spacing.M
|
|
||||||
},
|
|
||||||
itemAccount: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
itemAccountAvatar: {
|
|
||||||
alignSelf: 'flex-start',
|
|
||||||
width: StyleConstants.Avatar.S,
|
|
||||||
height: StyleConstants.Avatar.S,
|
|
||||||
borderRadius: 6,
|
|
||||||
marginRight: StyleConstants.Spacing.S
|
|
||||||
},
|
|
||||||
itemAccountAcct: { marginTop: StyleConstants.Spacing.XS }
|
|
||||||
})
|
|
||||||
|
|
||||||
export default ComponentAccount
|
export default ComponentAccount
|
||||||
|
|
|
@ -7,12 +7,11 @@ import {
|
||||||
AccessibilityProps,
|
AccessibilityProps,
|
||||||
Pressable,
|
Pressable,
|
||||||
StyleProp,
|
StyleProp,
|
||||||
StyleSheet,
|
|
||||||
Text,
|
|
||||||
View,
|
View,
|
||||||
ViewStyle
|
ViewStyle
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import { Flow } from 'react-native-animated-spinkit'
|
import { Flow } from 'react-native-animated-spinkit'
|
||||||
|
import CustomText from './Text'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
accessibilityLabel?: AccessibilityProps['accessibilityLabel']
|
accessibilityLabel?: AccessibilityProps['accessibilityLabel']
|
||||||
|
@ -116,7 +115,7 @@ const Button: React.FC<Props> = ({
|
||||||
case 'text':
|
case 'text':
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Text
|
<CustomText
|
||||||
style={{
|
style={{
|
||||||
color: mainColor,
|
color: mainColor,
|
||||||
fontSize:
|
fontSize:
|
||||||
|
@ -146,8 +145,10 @@ const Button: React.FC<Props> = ({
|
||||||
busy: loading
|
busy: loading
|
||||||
}}
|
}}
|
||||||
style={[
|
style={[
|
||||||
styles.button,
|
|
||||||
{
|
{
|
||||||
|
borderRadius: 100,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
borderWidth: overlay ? 0 : 1,
|
borderWidth: overlay ? 0 : 1,
|
||||||
borderColor: mainColor,
|
borderColor: mainColor,
|
||||||
backgroundColor: colorBackground,
|
backgroundColor: colorBackground,
|
||||||
|
@ -170,12 +171,4 @@ const Button: React.FC<Props> = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
button: {
|
|
||||||
borderRadius: 100,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default Button
|
export default Button
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import CustomText from '@components/Text'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||||
import { countInstanceEmoji } from '@utils/slices/instancesSlice'
|
import { countInstanceEmoji } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
|
@ -10,18 +12,15 @@ import {
|
||||||
findNodeHandle,
|
findNodeHandle,
|
||||||
Pressable,
|
Pressable,
|
||||||
SectionList,
|
SectionList,
|
||||||
StyleSheet,
|
|
||||||
Text,
|
|
||||||
View
|
View
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import FastImage from 'react-native-fast-image'
|
import FastImage from 'react-native-fast-image'
|
||||||
import { useDispatch } from 'react-redux'
|
|
||||||
import validUrl from 'valid-url'
|
import validUrl from 'valid-url'
|
||||||
import EmojisContext from './helpers/EmojisContext'
|
import EmojisContext from './helpers/EmojisContext'
|
||||||
|
|
||||||
const EmojisList = React.memo(
|
const EmojisList = React.memo(
|
||||||
() => {
|
() => {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const { reduceMotionEnabled } = useAccessibility()
|
const { reduceMotionEnabled } = useAccessibility()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
@ -30,7 +29,12 @@ const EmojisList = React.memo(
|
||||||
|
|
||||||
const listHeader = useCallback(
|
const listHeader = useCallback(
|
||||||
({ section: { title } }) => (
|
({ section: { title } }) => (
|
||||||
<Text style={[styles.group, { color: colors.secondary }]}>{title}</Text>
|
<CustomText
|
||||||
|
fontStyle='S'
|
||||||
|
style={{ position: 'absolute', color: colors.secondary }}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</CustomText>
|
||||||
),
|
),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
@ -38,7 +42,15 @@ const EmojisList = React.memo(
|
||||||
const listItem = useCallback(
|
const listItem = useCallback(
|
||||||
({ index, item }: { item: Mastodon.Emoji[]; index: number }) => {
|
({ index, item }: { item: Mastodon.Emoji[]; index: number }) => {
|
||||||
return (
|
return (
|
||||||
<View key={index} style={styles.emojis}>
|
<View
|
||||||
|
key={index}
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
marginTop: StyleConstants.Spacing.M,
|
||||||
|
marginRight: StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
>
|
||||||
{item.map(emoji => {
|
{item.map(emoji => {
|
||||||
const uri = reduceMotionEnabled ? emoji.static_url : emoji.url
|
const uri = reduceMotionEnabled ? emoji.static_url : emoji.url
|
||||||
if (validUrl.isHttpsUri(uri)) {
|
if (validUrl.isHttpsUri(uri)) {
|
||||||
|
@ -64,7 +76,12 @@ const EmojisList = React.memo(
|
||||||
'screenCompose:content.root.footer.emojis.accessibilityHint'
|
'screenCompose:content.root.footer.emojis.accessibilityHint'
|
||||||
)}
|
)}
|
||||||
source={{ uri }}
|
source={{ uri }}
|
||||||
style={styles.emoji}
|
style={{
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
padding: StyleConstants.Spacing.S,
|
||||||
|
margin: StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
)
|
)
|
||||||
|
@ -104,23 +121,4 @@ const EmojisList = React.memo(
|
||||||
() => true
|
() => true
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
group: {
|
|
||||||
position: 'absolute',
|
|
||||||
...StyleConstants.FontStyle.S
|
|
||||||
},
|
|
||||||
emojis: {
|
|
||||||
flex: 1,
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
marginTop: StyleConstants.Spacing.M,
|
|
||||||
marginRight: StyleConstants.Spacing.S
|
|
||||||
},
|
|
||||||
emoji: {
|
|
||||||
width: 32,
|
|
||||||
height: 32,
|
|
||||||
padding: StyleConstants.Spacing.S,
|
|
||||||
margin: StyleConstants.Spacing.S
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default EmojisList
|
export default EmojisList
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useMemo, useState } from 'react'
|
import React, { useMemo, useState } from 'react'
|
||||||
import {
|
import {
|
||||||
AccessibilityProps,
|
AccessibilityProps,
|
||||||
Image,
|
Image,
|
||||||
|
@ -39,125 +39,103 @@ export interface Props {
|
||||||
>
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
const GracefullyImage = React.memo(
|
const GracefullyImage = ({
|
||||||
({
|
accessibilityLabel,
|
||||||
accessibilityLabel,
|
accessibilityHint,
|
||||||
accessibilityHint,
|
hidden = false,
|
||||||
hidden = false,
|
uri,
|
||||||
uri,
|
blurhash,
|
||||||
blurhash,
|
dimension,
|
||||||
dimension,
|
onPress,
|
||||||
onPress,
|
style,
|
||||||
style,
|
imageStyle,
|
||||||
imageStyle,
|
setImageDimensions
|
||||||
setImageDimensions
|
}: Props) => {
|
||||||
}: Props) => {
|
const { reduceMotionEnabled } = useAccessibility()
|
||||||
const { reduceMotionEnabled } = useAccessibility()
|
const { colors } = useTheme()
|
||||||
const { colors } = useTheme()
|
const [originalFailed, setOriginalFailed] = useState(false)
|
||||||
const [originalFailed, setOriginalFailed] = useState(false)
|
const [imageLoaded, setImageLoaded] = useState(false)
|
||||||
const [imageLoaded, setImageLoaded] = useState(false)
|
|
||||||
|
|
||||||
const source = useMemo(() => {
|
const source = originalFailed
|
||||||
if (originalFailed) {
|
? { uri: uri.remote || undefined }
|
||||||
return { uri: uri.remote || undefined }
|
: {
|
||||||
} else {
|
uri: reduceMotionEnabled && uri.static ? uri.static : uri.original
|
||||||
return {
|
|
||||||
uri: reduceMotionEnabled && uri.static ? uri.static : uri.original
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [originalFailed])
|
|
||||||
|
|
||||||
const onLoad = useCallback(() => {
|
const onLoad = () => {
|
||||||
setImageLoaded(true)
|
setImageLoaded(true)
|
||||||
if (setImageDimensions && source.uri) {
|
if (setImageDimensions && source.uri) {
|
||||||
Image.getSize(source.uri, (width, height) =>
|
Image.getSize(source.uri, (width, height) =>
|
||||||
setImageDimensions({ width, height })
|
setImageDimensions({ width, height })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onError = () => {
|
||||||
|
if (!originalFailed) {
|
||||||
|
setOriginalFailed(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const blurhashView = useMemo(() => {
|
||||||
|
if (hidden || !imageLoaded) {
|
||||||
|
if (blurhash) {
|
||||||
|
return (
|
||||||
|
<Blurhash
|
||||||
|
decodeAsync
|
||||||
|
blurhash={blurhash}
|
||||||
|
style={styles.placeholder}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
} else {
|
||||||
}, [source.uri])
|
return (
|
||||||
const onError = useCallback(() => {
|
<View
|
||||||
if (!originalFailed) {
|
|
||||||
setOriginalFailed(true)
|
|
||||||
}
|
|
||||||
}, [originalFailed])
|
|
||||||
|
|
||||||
const previewView = useMemo(
|
|
||||||
() =>
|
|
||||||
uri.preview && !imageLoaded ? (
|
|
||||||
<Image
|
|
||||||
fadeDuration={0}
|
|
||||||
source={{ uri: uri.preview }}
|
|
||||||
style={[
|
style={[
|
||||||
styles.placeholder,
|
styles.placeholder,
|
||||||
{ backgroundColor: colors.shimmerDefault }
|
{ backgroundColor: colors.shimmerDefault }
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
) : null,
|
)
|
||||||
[]
|
}
|
||||||
)
|
} else {
|
||||||
const originalView = useMemo(
|
return null
|
||||||
() => (
|
}
|
||||||
|
}, [hidden, imageLoaded])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
{...(onPress
|
||||||
|
? { accessibilityRole: 'imagebutton' }
|
||||||
|
: { accessibilityRole: 'image' })}
|
||||||
|
accessibilityLabel={accessibilityLabel}
|
||||||
|
accessibilityHint={accessibilityHint}
|
||||||
|
style={[style, dimension, { backgroundColor: colors.shimmerDefault }]}
|
||||||
|
{...(onPress
|
||||||
|
? hidden
|
||||||
|
? { disabled: true }
|
||||||
|
: { onPress }
|
||||||
|
: { disabled: true })}
|
||||||
|
>
|
||||||
|
{uri.preview && !imageLoaded ? (
|
||||||
<Image
|
<Image
|
||||||
fadeDuration={0}
|
fadeDuration={0}
|
||||||
source={source}
|
source={{ uri: uri.preview }}
|
||||||
style={[{ flex: 1 }, imageStyle]}
|
style={[
|
||||||
onLoad={onLoad}
|
styles.placeholder,
|
||||||
onError={onError}
|
{ backgroundColor: colors.shimmerDefault }
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
),
|
) : null}
|
||||||
[source]
|
<Image
|
||||||
)
|
fadeDuration={0}
|
||||||
const blurhashView = useMemo(() => {
|
source={source}
|
||||||
if (hidden || !imageLoaded) {
|
style={[{ flex: 1 }, imageStyle]}
|
||||||
if (blurhash) {
|
onLoad={onLoad}
|
||||||
return (
|
onError={onError}
|
||||||
<Blurhash
|
/>
|
||||||
decodeAsync
|
{blurhashView}
|
||||||
blurhash={blurhash}
|
</Pressable>
|
||||||
style={styles.placeholder}
|
)
|
||||||
/>
|
}
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
styles.placeholder,
|
|
||||||
{ backgroundColor: colors.shimmerDefault }
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}, [hidden, imageLoaded])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Pressable
|
|
||||||
{...(onPress
|
|
||||||
? { accessibilityRole: 'imagebutton' }
|
|
||||||
: { accessibilityRole: 'image' })}
|
|
||||||
accessibilityLabel={accessibilityLabel}
|
|
||||||
accessibilityHint={accessibilityHint}
|
|
||||||
style={[style, dimension, { backgroundColor: colors.shimmerDefault }]}
|
|
||||||
{...(onPress
|
|
||||||
? hidden
|
|
||||||
? { disabled: true }
|
|
||||||
: { onPress }
|
|
||||||
: { disabled: true })}
|
|
||||||
>
|
|
||||||
{previewView}
|
|
||||||
{originalView}
|
|
||||||
{blurhashView}
|
|
||||||
</Pressable>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
(prev, next) =>
|
|
||||||
prev.hidden === next.hidden &&
|
|
||||||
prev.uri.preview === next.uri.preview &&
|
|
||||||
prev.uri.original === next.uri.original &&
|
|
||||||
prev.uri.remote === next.uri.remote
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
placeholder: {
|
placeholder: {
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { Pressable, StyleSheet, Text } from 'react-native'
|
import { Pressable } from 'react-native'
|
||||||
import analytics from './analytics'
|
import analytics from './analytics'
|
||||||
|
import CustomText from './Text'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
hashtag: Mastodon.Tag
|
hashtag: Mastodon.Tag
|
||||||
|
@ -19,7 +21,7 @@ const ComponentHashtag: React.FC<Props> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
|
|
||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
analytics('search_account_press', { page: origin })
|
analytics('search_account_press', { page: origin })
|
||||||
|
@ -29,23 +31,14 @@ const ComponentHashtag: React.FC<Props> = ({
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
accessibilityRole='button'
|
accessibilityRole='button'
|
||||||
style={styles.itemDefault}
|
style={{ padding: StyleConstants.Spacing.S * 1.5 }}
|
||||||
onPress={customOnPress || onPress}
|
onPress={customOnPress || onPress}
|
||||||
>
|
>
|
||||||
<Text style={[styles.itemHashtag, { color: colors.primaryDefault }]}>
|
<CustomText fontStyle='M' style={{ color: colors.primaryDefault }}>
|
||||||
#{hashtag.name}
|
#{hashtag.name}
|
||||||
</Text>
|
</CustomText>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
itemDefault: {
|
|
||||||
padding: StyleConstants.Spacing.S * 1.5
|
|
||||||
},
|
|
||||||
itemHashtag: {
|
|
||||||
...StyleConstants.FontStyle.M
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default ComponentHashtag
|
export default ComponentHashtag
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import CustomText from '@components/Text'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { StyleSheet, Text } from 'react-native'
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
content: string
|
content: string
|
||||||
|
@ -14,11 +13,12 @@ const HeaderCenter = React.memo(
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text
|
<CustomText
|
||||||
style={[
|
style={{
|
||||||
styles.text,
|
fontSize: 18,
|
||||||
{ color: inverted ? colors.primaryOverlay : colors.primaryDefault }
|
color: inverted ? colors.primaryOverlay : colors.primaryDefault
|
||||||
]}
|
}}
|
||||||
|
fontWeight='Bold'
|
||||||
children={content}
|
children={content}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -26,11 +26,4 @@ const HeaderCenter = React.memo(
|
||||||
(prev, next) => prev.content === next.content
|
(prev, next) => prev.content === next.content
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
text: {
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: StyleConstants.Font.Weight.Bold
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default HeaderCenter
|
export default HeaderCenter
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { Pressable, StyleSheet, Text } from 'react-native'
|
import { Pressable } from 'react-native'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
type?: 'icon' | 'text'
|
type?: 'icon' | 'text'
|
||||||
|
@ -34,8 +35,9 @@ const HeaderLeft: React.FC<Props> = ({
|
||||||
)
|
)
|
||||||
case 'text':
|
case 'text':
|
||||||
return (
|
return (
|
||||||
<Text
|
<CustomText
|
||||||
style={[styles.text, { color: colors.primaryDefault }]}
|
fontStyle='M'
|
||||||
|
style={{ color: colors.primaryDefault }}
|
||||||
children={content}
|
children={content}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -46,38 +48,27 @@ const HeaderLeft: React.FC<Props> = ({
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
children={children}
|
children={children}
|
||||||
style={[
|
style={{
|
||||||
styles.base,
|
flexDirection: 'row',
|
||||||
{
|
justifyContent: 'center',
|
||||||
backgroundColor: background
|
alignItems: 'center',
|
||||||
? colors.backgroundOverlayDefault
|
backgroundColor: background
|
||||||
: undefined,
|
? colors.backgroundOverlayDefault
|
||||||
minHeight: 44,
|
: undefined,
|
||||||
minWidth: 44,
|
minHeight: 44,
|
||||||
marginLeft: native
|
minWidth: 44,
|
||||||
? -StyleConstants.Spacing.S
|
marginLeft: native
|
||||||
: StyleConstants.Spacing.S,
|
? -StyleConstants.Spacing.S
|
||||||
...(type === 'icon' && {
|
: StyleConstants.Spacing.S,
|
||||||
borderRadius: 100
|
...(type === 'icon' && {
|
||||||
}),
|
borderRadius: 100
|
||||||
...(type === 'text' && {
|
}),
|
||||||
paddingHorizontal: StyleConstants.Spacing.S
|
...(type === 'text' && {
|
||||||
})
|
paddingHorizontal: StyleConstants.Spacing.S
|
||||||
}
|
})
|
||||||
]}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
...StyleConstants.FontStyle.M
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default HeaderLeft
|
export default HeaderLeft
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import {
|
import { AccessibilityProps, Pressable, View } from 'react-native'
|
||||||
AccessibilityProps,
|
|
||||||
Pressable,
|
|
||||||
StyleSheet,
|
|
||||||
Text,
|
|
||||||
View
|
|
||||||
} from 'react-native'
|
|
||||||
import { Flow } from 'react-native-animated-spinkit'
|
import { Flow } from 'react-native-animated-spinkit'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -72,14 +67,12 @@ const HeaderRight: React.FC<Props> = ({
|
||||||
case 'text':
|
case 'text':
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Text
|
<CustomText
|
||||||
style={[
|
fontStyle='M'
|
||||||
styles.text,
|
style={{
|
||||||
{
|
color: disabled ? colors.secondary : colors.primaryDefault,
|
||||||
color: disabled ? colors.secondary : colors.primaryDefault,
|
opacity: loading ? 0 : 1
|
||||||
opacity: loading ? 0 : 1
|
}}
|
||||||
}
|
|
||||||
]}
|
|
||||||
children={content}
|
children={content}
|
||||||
/>
|
/>
|
||||||
{loading && loadingSpinkit}
|
{loading && loadingSpinkit}
|
||||||
|
@ -97,38 +90,27 @@ const HeaderRight: React.FC<Props> = ({
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
children={children}
|
children={children}
|
||||||
disabled={disabled || loading}
|
disabled={disabled || loading}
|
||||||
style={[
|
style={{
|
||||||
styles.base,
|
flexDirection: 'row',
|
||||||
{
|
justifyContent: 'center',
|
||||||
backgroundColor: background
|
alignItems: 'center',
|
||||||
? colors.backgroundOverlayDefault
|
backgroundColor: background
|
||||||
: undefined,
|
? colors.backgroundOverlayDefault
|
||||||
minHeight: 44,
|
: undefined,
|
||||||
minWidth: 44,
|
minHeight: 44,
|
||||||
marginRight: native
|
minWidth: 44,
|
||||||
? -StyleConstants.Spacing.S
|
marginRight: native
|
||||||
: StyleConstants.Spacing.S,
|
? -StyleConstants.Spacing.S
|
||||||
...(type === 'icon' && {
|
: StyleConstants.Spacing.S,
|
||||||
borderRadius: 100
|
...(type === 'icon' && {
|
||||||
}),
|
borderRadius: 100
|
||||||
...(type === 'text' && {
|
}),
|
||||||
paddingHorizontal: StyleConstants.Spacing.S
|
...(type === 'text' && {
|
||||||
})
|
paddingHorizontal: StyleConstants.Spacing.S
|
||||||
}
|
})
|
||||||
]}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
...StyleConstants.FontStyle.M
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default HeaderRight
|
export default HeaderRight
|
||||||
|
|
|
@ -9,17 +9,11 @@ import React, {
|
||||||
useRef,
|
useRef,
|
||||||
useState
|
useState
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import {
|
import { Platform, TextInput, TextInputProps, View } from 'react-native'
|
||||||
Platform,
|
|
||||||
StyleSheet,
|
|
||||||
Text,
|
|
||||||
TextInput,
|
|
||||||
TextInputProps,
|
|
||||||
View
|
|
||||||
} from 'react-native'
|
|
||||||
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated'
|
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated'
|
||||||
import { ComponentEmojis, EmojisButton, EmojisList } from './Emojis'
|
import { ComponentEmojis, EmojisButton, EmojisList } from './Emojis'
|
||||||
import EmojisContext from './Emojis/helpers/EmojisContext'
|
import EmojisContext from './Emojis/helpers/EmojisContext'
|
||||||
|
import CustomText from './Text'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
autoFocus?: boolean
|
autoFocus?: boolean
|
||||||
|
@ -106,14 +100,14 @@ const Input: React.FC<Props> = ({
|
||||||
maxLength={options?.maxLength}
|
maxLength={options?.maxLength}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={{
|
||||||
styles.base,
|
borderWidth: 1,
|
||||||
{
|
marginVertical: StyleConstants.Spacing.S,
|
||||||
borderColor: colors.border,
|
padding: StyleConstants.Spacing.S,
|
||||||
flexDirection: multiline ? 'column' : 'row',
|
borderColor: colors.border,
|
||||||
alignItems: 'stretch'
|
flexDirection: multiline ? 'column' : 'row',
|
||||||
}
|
alignItems: 'stretch'
|
||||||
]}
|
}}
|
||||||
>
|
>
|
||||||
<EmojisContext.Consumer>
|
<EmojisContext.Consumer>
|
||||||
{({ emojisDispatch }) => (
|
{({ emojisDispatch }) => (
|
||||||
|
@ -124,16 +118,15 @@ const Input: React.FC<Props> = ({
|
||||||
setInputFocused(false)
|
setInputFocused(false)
|
||||||
emojisDispatch({ type: 'activate', payload: false })
|
emojisDispatch({ type: 'activate', payload: false })
|
||||||
}}
|
}}
|
||||||
style={[
|
style={{
|
||||||
styles.textInput,
|
flex: 1,
|
||||||
{
|
fontSize: StyleConstants.Font.Size.M,
|
||||||
color: colors.primaryDefault,
|
color: colors.primaryDefault,
|
||||||
minHeight:
|
minHeight:
|
||||||
Platform.OS === 'ios' && multiline
|
Platform.OS === 'ios' && multiline
|
||||||
? StyleConstants.Font.LineHeight.M * 5
|
? StyleConstants.Font.LineHeight.M * 5
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}}
|
||||||
]}
|
|
||||||
onChangeText={setValue}
|
onChangeText={setValue}
|
||||||
onSelectionChange={onSelectionChange}
|
onSelectionChange={onSelectionChange}
|
||||||
value={value}
|
value={value}
|
||||||
|
@ -149,16 +142,25 @@ const Input: React.FC<Props> = ({
|
||||||
</EmojisContext.Consumer>
|
</EmojisContext.Consumer>
|
||||||
{title ? (
|
{title ? (
|
||||||
<Animated.Text
|
<Animated.Text
|
||||||
style={[styles.title, animateTitle, { color: colors.secondary }]}
|
style={[
|
||||||
|
animateTitle,
|
||||||
|
{ position: 'absolute', color: colors.secondary }
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</Animated.Text>
|
</Animated.Text>
|
||||||
) : null}
|
) : null}
|
||||||
<View style={{ flexDirection: 'row', alignSelf: 'flex-end' }}>
|
<View style={{ flexDirection: 'row', alignSelf: 'flex-end' }}>
|
||||||
{options?.maxLength && value?.length ? (
|
{options?.maxLength && value?.length ? (
|
||||||
<Text style={[styles.maxLength, { color: colors.secondary }]}>
|
<CustomText
|
||||||
|
fontStyle='S'
|
||||||
|
style={{
|
||||||
|
paddingLeft: StyleConstants.Spacing.XS,
|
||||||
|
color: colors.secondary
|
||||||
|
}}
|
||||||
|
>
|
||||||
{value?.length} / {options.maxLength}
|
{value?.length} / {options.maxLength}
|
||||||
</Text>
|
</CustomText>
|
||||||
) : null}
|
) : null}
|
||||||
{inputFocused ? <EmojisButton /> : null}
|
{inputFocused ? <EmojisButton /> : null}
|
||||||
</View>
|
</View>
|
||||||
|
@ -168,24 +170,4 @@ const Input: React.FC<Props> = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
alignItems: 'flex-end',
|
|
||||||
borderWidth: 1,
|
|
||||||
marginVertical: StyleConstants.Spacing.S,
|
|
||||||
padding: StyleConstants.Spacing.S
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
position: 'absolute'
|
|
||||||
},
|
|
||||||
textInput: {
|
|
||||||
flex: 1,
|
|
||||||
fontSize: StyleConstants.Font.Size.M
|
|
||||||
},
|
|
||||||
maxLength: {
|
|
||||||
...StyleConstants.FontStyle.S,
|
|
||||||
paddingLeft: StyleConstants.Spacing.XS
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default Input
|
export default Input
|
||||||
|
|
|
@ -15,8 +15,6 @@ import {
|
||||||
Image,
|
Image,
|
||||||
KeyboardAvoidingView,
|
KeyboardAvoidingView,
|
||||||
Platform,
|
Platform,
|
||||||
StyleSheet,
|
|
||||||
Text,
|
|
||||||
TextInput,
|
TextInput,
|
||||||
View
|
View
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
|
@ -26,6 +24,7 @@ import { Placeholder } from 'rn-placeholder'
|
||||||
import analytics from './analytics'
|
import analytics from './analytics'
|
||||||
import InstanceAuth from './Instance/Auth'
|
import InstanceAuth from './Instance/Auth'
|
||||||
import InstanceInfo from './Instance/Info'
|
import InstanceInfo from './Instance/Info'
|
||||||
|
import CustomText from './Text'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
scrollViewRef?: RefObject<ScrollView>
|
scrollViewRef?: RefObject<ScrollView>
|
||||||
|
@ -134,40 +133,50 @@ const ComponentInstance: React.FC<Props> = ({
|
||||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||||
>
|
>
|
||||||
{!disableHeaderImage ? (
|
{!disableHeaderImage ? (
|
||||||
<View style={styles.imageContainer}>
|
<View style={{ flexDirection: 'row' }}>
|
||||||
<Image
|
<Image
|
||||||
source={require('assets/images/welcome.png')}
|
source={require('assets/images/welcome.png')}
|
||||||
style={styles.image}
|
style={{ resizeMode: 'contain', flex: 1, aspectRatio: 16 / 9 }}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
<View style={styles.base}>
|
<View
|
||||||
<View style={styles.inputRow}>
|
style={{
|
||||||
|
marginTop: StyleConstants.Spacing.L,
|
||||||
|
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TextInput
|
<TextInput
|
||||||
accessible={false}
|
accessible={false}
|
||||||
accessibilityRole='none'
|
accessibilityRole='none'
|
||||||
style={[
|
style={{
|
||||||
styles.prefix,
|
borderBottomWidth: 1,
|
||||||
{
|
...StyleConstants.FontStyle.M,
|
||||||
color: colors.primaryDefault,
|
color: colors.primaryDefault,
|
||||||
borderBottomColor: instanceQuery.isError
|
borderBottomColor: instanceQuery.isError
|
||||||
? colors.red
|
? colors.red
|
||||||
: colors.border
|
: colors.border
|
||||||
}
|
}}
|
||||||
]}
|
|
||||||
editable={false}
|
editable={false}
|
||||||
defaultValue='https://'
|
defaultValue='https://'
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[
|
style={{
|
||||||
styles.textInput,
|
flex: 1,
|
||||||
{
|
borderBottomWidth: 1,
|
||||||
color: colors.primaryDefault,
|
...StyleConstants.FontStyle.M,
|
||||||
borderBottomColor: instanceQuery.isError
|
marginRight: StyleConstants.Spacing.M,
|
||||||
? colors.red
|
color: colors.primaryDefault,
|
||||||
: colors.border
|
borderBottomColor: instanceQuery.isError
|
||||||
}
|
? colors.red
|
||||||
]}
|
: colors.border
|
||||||
|
}}
|
||||||
onChangeText={onChangeText}
|
onChangeText={onChangeText}
|
||||||
autoCapitalize='none'
|
autoCapitalize='none'
|
||||||
clearButtonMode='never'
|
clearButtonMode='never'
|
||||||
|
@ -205,9 +214,9 @@ const ComponentInstance: React.FC<Props> = ({
|
||||||
content={instanceQuery.data?.title || undefined}
|
content={instanceQuery.data?.title || undefined}
|
||||||
potentialWidth={2}
|
potentialWidth={2}
|
||||||
/>
|
/>
|
||||||
<View style={styles.instanceStats}>
|
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||||
<InstanceInfo
|
<InstanceInfo
|
||||||
style={styles.stat1}
|
style={{ alignItems: 'flex-start' }}
|
||||||
header={t('server.information.accounts')}
|
header={t('server.information.accounts')}
|
||||||
content={
|
content={
|
||||||
instanceQuery.data?.stats?.user_count?.toString() || undefined
|
instanceQuery.data?.stats?.user_count?.toString() || undefined
|
||||||
|
@ -215,7 +224,7 @@ const ComponentInstance: React.FC<Props> = ({
|
||||||
potentialWidth={4}
|
potentialWidth={4}
|
||||||
/>
|
/>
|
||||||
<InstanceInfo
|
<InstanceInfo
|
||||||
style={styles.stat2}
|
style={{ alignItems: 'center' }}
|
||||||
header={t('server.information.statuses')}
|
header={t('server.information.statuses')}
|
||||||
content={
|
content={
|
||||||
instanceQuery.data?.stats?.status_count?.toString() ||
|
instanceQuery.data?.stats?.status_count?.toString() ||
|
||||||
|
@ -224,7 +233,7 @@ const ComponentInstance: React.FC<Props> = ({
|
||||||
potentialWidth={4}
|
potentialWidth={4}
|
||||||
/>
|
/>
|
||||||
<InstanceInfo
|
<InstanceInfo
|
||||||
style={styles.stat3}
|
style={{ alignItems: 'flex-end' }}
|
||||||
header={t('server.information.domains')}
|
header={t('server.information.domains')}
|
||||||
content={
|
content={
|
||||||
instanceQuery.data?.stats?.domain_count?.toString() ||
|
instanceQuery.data?.stats?.domain_count?.toString() ||
|
||||||
|
@ -234,15 +243,28 @@ const ComponentInstance: React.FC<Props> = ({
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</Placeholder>
|
</Placeholder>
|
||||||
<View style={styles.disclaimer}>
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||||
|
marginVertical: StyleConstants.Spacing.M
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Icon
|
<Icon
|
||||||
name='Lock'
|
name='Lock'
|
||||||
size={StyleConstants.Font.Size.S}
|
size={StyleConstants.Font.Size.S}
|
||||||
color={colors.secondary}
|
color={colors.secondary}
|
||||||
style={styles.disclaimerIcon}
|
style={{
|
||||||
|
marginTop:
|
||||||
|
(StyleConstants.Font.LineHeight.S -
|
||||||
|
StyleConstants.Font.Size.S) /
|
||||||
|
2,
|
||||||
|
marginRight: StyleConstants.Spacing.XS
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Text
|
<CustomText
|
||||||
style={[styles.disclaimerText, { color: colors.secondary }]}
|
fontStyle='S'
|
||||||
|
style={{ flex: 1, color: colors.secondary }}
|
||||||
accessibilityRole='link'
|
accessibilityRole='link'
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (screenReaderEnabled) {
|
if (screenReaderEnabled) {
|
||||||
|
@ -254,7 +276,7 @@ const ComponentInstance: React.FC<Props> = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('server.disclaimer.base')}
|
{t('server.disclaimer.base')}
|
||||||
<Text
|
<CustomText
|
||||||
accessible
|
accessible
|
||||||
style={{ color: colors.blue }}
|
style={{ color: colors.blue }}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
|
@ -265,8 +287,8 @@ const ComponentInstance: React.FC<Props> = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('server.disclaimer.privacy')}
|
{t('server.disclaimer.privacy')}
|
||||||
</Text>
|
</CustomText>
|
||||||
</Text>
|
</CustomText>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
@ -276,54 +298,4 @@ const ComponentInstance: React.FC<Props> = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
imageContainer: { flexDirection: 'row' },
|
|
||||||
image: { resizeMode: 'contain', flex: 1, aspectRatio: 16 / 9 },
|
|
||||||
base: {
|
|
||||||
marginTop: StyleConstants.Spacing.L,
|
|
||||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
|
|
||||||
},
|
|
||||||
inputRow: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
|
|
||||||
},
|
|
||||||
prefix: {
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
...StyleConstants.FontStyle.M
|
|
||||||
},
|
|
||||||
textInput: {
|
|
||||||
flex: 1,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
...StyleConstants.FontStyle.M,
|
|
||||||
marginRight: StyleConstants.Spacing.M
|
|
||||||
},
|
|
||||||
instanceStats: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row'
|
|
||||||
},
|
|
||||||
stat1: {
|
|
||||||
alignItems: 'flex-start'
|
|
||||||
},
|
|
||||||
stat2: {
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
stat3: {
|
|
||||||
alignItems: 'flex-end'
|
|
||||||
},
|
|
||||||
disclaimer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
|
||||||
marginVertical: StyleConstants.Spacing.M
|
|
||||||
},
|
|
||||||
disclaimerIcon: {
|
|
||||||
marginTop:
|
|
||||||
(StyleConstants.Font.LineHeight.S - StyleConstants.Font.Size.S) / 2,
|
|
||||||
marginRight: StyleConstants.Spacing.XS
|
|
||||||
},
|
|
||||||
disclaimerText: {
|
|
||||||
flex: 1,
|
|
||||||
...StyleConstants.FontStyle.S
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default ComponentInstance
|
export default ComponentInstance
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { useAppDispatch } from '@root/store'
|
||||||
import { TabMeStackNavigationProp } from '@utils/navigation/navigators'
|
import { TabMeStackNavigationProp } from '@utils/navigation/navigators'
|
||||||
import addInstance from '@utils/slices/instances/add'
|
import addInstance from '@utils/slices/instances/add'
|
||||||
import { Instance } from '@utils/slices/instancesSlice'
|
import { checkInstanceFeature, Instance } from '@utils/slices/instancesSlice'
|
||||||
import * as AuthSession from 'expo-auth-session'
|
import * as AuthSession from 'expo-auth-session'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
import { useDispatch } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
instanceDomain: string
|
instanceDomain: string
|
||||||
|
@ -25,13 +26,18 @@ const InstanceAuth = React.memo(
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<TabMeStackNavigationProp<'Tab-Me-Root' | 'Tab-Me-Switch'>>()
|
useNavigation<TabMeStackNavigationProp<'Tab-Me-Root' | 'Tab-Me-Switch'>>()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
|
const deprecateAuthFollow = useSelector(
|
||||||
|
checkInstanceFeature('deprecate_auth_follow')
|
||||||
|
)
|
||||||
const [request, response, promptAsync] = AuthSession.useAuthRequest(
|
const [request, response, promptAsync] = AuthSession.useAuthRequest(
|
||||||
{
|
{
|
||||||
clientId: appData.clientId,
|
clientId: appData.clientId,
|
||||||
clientSecret: appData.clientSecret,
|
clientSecret: appData.clientSecret,
|
||||||
scopes: ['read', 'write', 'follow', 'push'],
|
scopes: deprecateAuthFollow
|
||||||
|
? ['read', 'write', 'push']
|
||||||
|
: ['read', 'write', 'follow', 'push'],
|
||||||
redirectUri
|
redirectUri
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { StyleSheet, Text, View, ViewStyle } from 'react-native'
|
import { View, ViewStyle } from 'react-native'
|
||||||
import { PlaceholderLine } from 'rn-placeholder'
|
import { PlaceholderLine } from 'rn-placeholder'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -16,14 +17,33 @@ const InstanceInfo = React.memo(
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.base, style]} accessible>
|
<View
|
||||||
<Text style={[styles.header, { color: colors.primaryDefault }]}>
|
style={[
|
||||||
{header}
|
{
|
||||||
</Text>
|
flex: 1,
|
||||||
|
marginTop: StyleConstants.Spacing.M,
|
||||||
|
paddingLeft: StyleConstants.Spacing.Global.PagePadding,
|
||||||
|
paddingRight: StyleConstants.Spacing.Global.PagePadding
|
||||||
|
},
|
||||||
|
style
|
||||||
|
]}
|
||||||
|
accessible
|
||||||
|
>
|
||||||
|
<CustomText
|
||||||
|
fontStyle='S'
|
||||||
|
style={{
|
||||||
|
marginBottom: StyleConstants.Spacing.XS,
|
||||||
|
color: colors.primaryDefault
|
||||||
|
}}
|
||||||
|
fontWeight='Bold'
|
||||||
|
children={header}
|
||||||
|
/>
|
||||||
{content ? (
|
{content ? (
|
||||||
<Text style={[styles.content, { color: colors.primaryDefault }]}>
|
<CustomText
|
||||||
{content}
|
fontStyle='M'
|
||||||
</Text>
|
style={{ color: colors.primaryDefault }}
|
||||||
|
children={content}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<PlaceholderLine
|
<PlaceholderLine
|
||||||
width={
|
width={
|
||||||
|
@ -43,21 +63,4 @@ const InstanceInfo = React.memo(
|
||||||
(prev, next) => prev.content === next.content
|
(prev, next) => prev.content === next.content
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flex: 1,
|
|
||||||
marginTop: StyleConstants.Spacing.M,
|
|
||||||
paddingLeft: StyleConstants.Spacing.Global.PagePadding,
|
|
||||||
paddingRight: StyleConstants.Spacing.Global.PagePadding
|
|
||||||
},
|
|
||||||
header: {
|
|
||||||
...StyleConstants.FontStyle.S,
|
|
||||||
fontWeight: StyleConstants.Font.Weight.Bold,
|
|
||||||
marginBottom: StyleConstants.Spacing.XS
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
...StyleConstants.FontStyle.M
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default InstanceInfo
|
export default InstanceInfo
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { StyleSheet, Text, View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
heading: string
|
heading: string
|
||||||
|
@ -11,20 +12,16 @@ const MenuHeader: React.FC<Props> = ({ heading }) => {
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View style={{ paddingBottom: StyleConstants.Spacing.S }}>
|
||||||
<Text style={[styles.text, { color: colors.secondary }]}>{heading}</Text>
|
<CustomText
|
||||||
|
fontStyle='S'
|
||||||
|
fontWeight='Bold'
|
||||||
|
style={{ color: colors.secondary }}
|
||||||
|
>
|
||||||
|
{heading}
|
||||||
|
</CustomText>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
paddingBottom: StyleConstants.Spacing.S
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
...StyleConstants.FontStyle.S,
|
|
||||||
fontWeight: StyleConstants.Font.Weight.Bold
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default MenuHeader
|
export default MenuHeader
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
@ -99,12 +100,13 @@ const MenuRow: React.FC<Props> = ({
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<View style={styles.main}>
|
<View style={styles.main}>
|
||||||
<Text
|
<CustomText
|
||||||
style={[styles.title, { color: colors.primaryDefault }]}
|
fontStyle='M'
|
||||||
|
style={{ color: colors.primaryDefault }}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</CustomText>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
@ -112,18 +114,15 @@ const MenuRow: React.FC<Props> = ({
|
||||||
<View style={styles.back}>
|
<View style={styles.back}>
|
||||||
{content ? (
|
{content ? (
|
||||||
typeof content === 'string' ? (
|
typeof content === 'string' ? (
|
||||||
<Text
|
<CustomText
|
||||||
style={[
|
style={{
|
||||||
styles.content,
|
color: colors.secondary,
|
||||||
{
|
opacity: !iconBack && loading ? 0 : 1
|
||||||
color: colors.secondary,
|
}}
|
||||||
opacity: !iconBack && loading ? 0 : 1
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</Text>
|
</CustomText>
|
||||||
) : (
|
) : (
|
||||||
content
|
content
|
||||||
)
|
)
|
||||||
|
@ -150,9 +149,9 @@ const MenuRow: React.FC<Props> = ({
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
{description ? (
|
{description ? (
|
||||||
<Text style={[styles.description, { color: colors.secondary }]}>
|
<CustomText fontStyle='S' style={{ color: colors.secondary }}>
|
||||||
{description}
|
{description}
|
||||||
</Text>
|
</CustomText>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
</TapGestureHandler>
|
</TapGestureHandler>
|
||||||
|
@ -167,7 +166,7 @@ const styles = StyleSheet.create({
|
||||||
core: {
|
core: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
paddingVertical: StyleConstants.Spacing.S
|
paddingTop: StyleConstants.Spacing.S
|
||||||
},
|
},
|
||||||
front: {
|
front: {
|
||||||
flex: 2,
|
flex: 2,
|
||||||
|
@ -187,9 +186,6 @@ const styles = StyleSheet.create({
|
||||||
main: {
|
main: {
|
||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
||||||
title: {
|
|
||||||
...StyleConstants.FontStyle.M
|
|
||||||
},
|
|
||||||
description: {
|
description: {
|
||||||
...StyleConstants.FontStyle.S
|
...StyleConstants.FontStyle.S
|
||||||
},
|
},
|
||||||
|
|
|
@ -59,7 +59,7 @@ const displayMessage = ({
|
||||||
|
|
||||||
if (ref) {
|
if (ref) {
|
||||||
ref.current?.showMessage({
|
ref.current?.showMessage({
|
||||||
duration: type === 'error' ? 5000 : duration === 'short' ? 1500 : 3000,
|
duration: type === 'error' ? 8000 : duration === 'short' ? 3000 : 5000,
|
||||||
autoHide,
|
autoHide,
|
||||||
message,
|
message,
|
||||||
description,
|
description,
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||||
import { getSettingsFontsize } from '@utils/slices/settingsSlice'
|
import { getSettingsFontsize } from '@utils/slices/settingsSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { adaptiveScale } from '@utils/styles/scaling'
|
import { adaptiveScale } from '@utils/styles/scaling'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { StyleSheet, Text } from 'react-native'
|
import { StyleSheet } from 'react-native'
|
||||||
import FastImage from 'react-native-fast-image'
|
import FastImage from 'react-native-fast-image'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import validUrl from 'valid-url'
|
import validUrl from 'valid-url'
|
||||||
|
@ -45,8 +46,7 @@ const ParseEmojis = React.memo(
|
||||||
text: {
|
text: {
|
||||||
color: colors.primaryDefault,
|
color: colors.primaryDefault,
|
||||||
fontSize: adaptedFontsize,
|
fontSize: adaptedFontsize,
|
||||||
lineHeight: adaptedLineheight,
|
lineHeight: adaptedLineheight
|
||||||
...(fontBold && { fontWeight: StyleConstants.Font.Weight.Bold })
|
|
||||||
},
|
},
|
||||||
image: {
|
image: {
|
||||||
width: adaptedFontsize,
|
width: adaptedFontsize,
|
||||||
|
@ -57,7 +57,10 @@ const ParseEmojis = React.memo(
|
||||||
}, [theme, adaptiveFontsize])
|
}, [theme, adaptiveFontsize])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text style={styles.text}>
|
<CustomText
|
||||||
|
style={styles.text}
|
||||||
|
fontWeight={fontBold ? 'Bold' : undefined}
|
||||||
|
>
|
||||||
{emojis ? (
|
{emojis ? (
|
||||||
content
|
content
|
||||||
.split(regexEmoji)
|
.split(regexEmoji)
|
||||||
|
@ -69,30 +72,34 @@ const ParseEmojis = React.memo(
|
||||||
return emojiShortcode === `:${emoji.shortcode}:`
|
return emojiShortcode === `:${emoji.shortcode}:`
|
||||||
})
|
})
|
||||||
if (emojiIndex === -1) {
|
if (emojiIndex === -1) {
|
||||||
return <Text key={emojiShortcode + i}>{emojiShortcode}</Text>
|
return (
|
||||||
|
<CustomText key={emojiShortcode + i}>
|
||||||
|
{emojiShortcode}
|
||||||
|
</CustomText>
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
const uri = reduceMotionEnabled
|
const uri = reduceMotionEnabled
|
||||||
? emojis[emojiIndex].static_url
|
? emojis[emojiIndex].static_url
|
||||||
: emojis[emojiIndex].url
|
: emojis[emojiIndex].url
|
||||||
if (validUrl.isHttpsUri(uri)) {
|
if (validUrl.isHttpsUri(uri)) {
|
||||||
return (
|
return (
|
||||||
<Text key={emojiShortcode + i}>
|
<CustomText key={emojiShortcode + i}>
|
||||||
{i === 0 ? ' ' : undefined}
|
{i === 0 ? ' ' : undefined}
|
||||||
<FastImage source={{ uri }} style={styles.image} />
|
<FastImage source={{ uri }} style={styles.image} />
|
||||||
</Text>
|
</CustomText>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return <Text key={i}>{str}</Text>
|
return <CustomText key={i}>{str}</CustomText>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<Text>{content}</Text>
|
<CustomText>{content}</CustomText>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</CustomText>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
(prev, next) => prev.content === next.content
|
(prev, next) => prev.content === next.content
|
||||||
|
|
|
@ -2,8 +2,10 @@ import analytics from '@components/analytics'
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import openLink from '@components/openLink'
|
import openLink from '@components/openLink'
|
||||||
import ParseEmojis from '@components/Parse/Emojis'
|
import ParseEmojis from '@components/Parse/Emojis'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { useNavigation, useRoute } from '@react-navigation/native'
|
import { useNavigation, useRoute } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { getSettingsFontsize } from '@utils/slices/settingsSlice'
|
import { getSettingsFontsize } from '@utils/slices/settingsSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import layoutAnimation from '@utils/styles/layoutAnimation'
|
import layoutAnimation from '@utils/styles/layoutAnimation'
|
||||||
|
@ -11,7 +13,7 @@ import { adaptiveScale } from '@utils/styles/scaling'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useState } from 'react'
|
import React, { useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Pressable, Text, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import HTMLView from 'react-native-htmlview'
|
import HTMLView from 'react-native-htmlview'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
|
@ -35,7 +37,7 @@ const renderNode = ({
|
||||||
index: number
|
index: number
|
||||||
adaptedFontsize: number
|
adaptedFontsize: number
|
||||||
adaptedLineheight: number
|
adaptedLineheight: number
|
||||||
navigation: StackNavigationProp<Nav.TabLocalStackParamList>
|
navigation: StackNavigationProp<TabLocalStackParamList>
|
||||||
mentions?: Mastodon.Mention[]
|
mentions?: Mastodon.Mention[]
|
||||||
tags?: Mastodon.Tag[]
|
tags?: Mastodon.Tag[]
|
||||||
showFullLink: boolean
|
showFullLink: boolean
|
||||||
|
@ -52,7 +54,7 @@ const renderNode = ({
|
||||||
? routeParams.hashtag !== tag[1] && routeParams.hashtag !== tag[2]
|
? routeParams.hashtag !== tag[1] && routeParams.hashtag !== tag[2]
|
||||||
: true
|
: true
|
||||||
return (
|
return (
|
||||||
<Text
|
<CustomText
|
||||||
accessible
|
accessible
|
||||||
key={index}
|
key={index}
|
||||||
style={{
|
style={{
|
||||||
|
@ -71,7 +73,7 @@ const renderNode = ({
|
||||||
>
|
>
|
||||||
{node.children[0].data}
|
{node.children[0].data}
|
||||||
{node.children[1]?.children[0].data}
|
{node.children[1]?.children[0].data}
|
||||||
</Text>
|
</CustomText>
|
||||||
)
|
)
|
||||||
} else if (classes.includes('mention') && mentions) {
|
} else if (classes.includes('mention') && mentions) {
|
||||||
const accountIndex = mentions.findIndex(
|
const accountIndex = mentions.findIndex(
|
||||||
|
@ -81,7 +83,7 @@ const renderNode = ({
|
||||||
? routeParams.account.id !== mentions[accountIndex]?.id
|
? routeParams.account.id !== mentions[accountIndex]?.id
|
||||||
: true
|
: true
|
||||||
return (
|
return (
|
||||||
<Text
|
<CustomText
|
||||||
key={index}
|
key={index}
|
||||||
style={{
|
style={{
|
||||||
color:
|
color:
|
||||||
|
@ -101,7 +103,7 @@ const renderNode = ({
|
||||||
>
|
>
|
||||||
{node.children[0].data}
|
{node.children[0].data}
|
||||||
{node.children[1]?.children[0].data}
|
{node.children[1]?.children[0].data}
|
||||||
</Text>
|
</CustomText>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -112,7 +114,7 @@ const renderNode = ({
|
||||||
const shouldBeTag =
|
const shouldBeTag =
|
||||||
tags && tags.filter(tag => `#${tag.name}` === content).length > 0
|
tags && tags.filter(tag => `#${tag.name}` === content).length > 0
|
||||||
return (
|
return (
|
||||||
<Text
|
<CustomText
|
||||||
key={index}
|
key={index}
|
||||||
style={{
|
style={{
|
||||||
color: colors.blue,
|
color: colors.blue,
|
||||||
|
@ -141,7 +143,7 @@ const renderNode = ({
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Text>
|
</CustomText>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -194,7 +196,7 @@ const ParseHTML = React.memo(
|
||||||
)
|
)
|
||||||
|
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { colors, theme } = useTheme()
|
const { colors, theme } = useTheme()
|
||||||
const { t, i18n } = useTranslation('componentParse')
|
const { t, i18n } = useTranslation('componentParse')
|
||||||
|
@ -251,7 +253,7 @@ const ParseHTML = React.memo(
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{ overflow: 'hidden' }}>
|
<View style={{ overflow: 'hidden' }}>
|
||||||
<Text
|
<CustomText
|
||||||
children={children}
|
children={children}
|
||||||
onTextLayout={onTextLayout}
|
onTextLayout={onTextLayout}
|
||||||
numberOfLines={
|
numberOfLines={
|
||||||
|
@ -274,7 +276,7 @@ const ParseHTML = React.memo(
|
||||||
backgroundColor: colors.backgroundDefault
|
backgroundColor: colors.backgroundDefault
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text
|
<CustomText
|
||||||
style={{
|
style={{
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
...StyleConstants.FontStyle.S,
|
...StyleConstants.FontStyle.S,
|
||||||
|
@ -301,7 +303,7 @@ const ParseHTML = React.memo(
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
() => true
|
(prev, next) => prev.content === next.content
|
||||||
)
|
)
|
||||||
|
|
||||||
export default ParseHTML
|
export default ParseHTML
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { Text } from 'react-native'
|
|
||||||
import TimeAgo from 'react-timeago'
|
|
||||||
// @ts-ignore
|
|
||||||
import buildFormatter from 'react-timeago/lib/formatters/buildFormatter'
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
date: string | number
|
|
||||||
}
|
|
||||||
|
|
||||||
const RelativeTime: React.FC<Props> = ({ date }) => {
|
|
||||||
const { t } = useTranslation('componentRelativeTime')
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TimeAgo
|
|
||||||
date={date}
|
|
||||||
component={Text}
|
|
||||||
formatter={buildFormatter(t('strings', { returnObjects: true }))}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default RelativeTime
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||||
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
|
import { Text, TextProps, TextStyle } from 'react-native'
|
||||||
|
|
||||||
|
type Props =
|
||||||
|
| {
|
||||||
|
style?: Omit<TextStyle, 'fontSize' | 'lineHeight' | 'fontWeight'>
|
||||||
|
fontStyle?: undefined
|
||||||
|
fontSize?: 'S' | 'M' | 'L'
|
||||||
|
lineHeight?: 'S' | 'M' | 'L'
|
||||||
|
fontWeight?: 'Normal' | 'Bold'
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
style?: Omit<TextStyle, 'fontSize' | 'lineHeight' | 'fontWeight'>
|
||||||
|
fontStyle: 'S' | 'M' | 'L'
|
||||||
|
fontSize?: undefined
|
||||||
|
lineHeight?: undefined
|
||||||
|
fontWeight?: 'Normal' | 'Bold'
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomText: React.FC<Props & TextProps> = ({
|
||||||
|
children,
|
||||||
|
style,
|
||||||
|
fontStyle,
|
||||||
|
fontSize,
|
||||||
|
fontWeight,
|
||||||
|
lineHeight,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
|
const { boldTextEnabled } = useAccessibility()
|
||||||
|
|
||||||
|
enum BoldMapping {
|
||||||
|
'Normal' = '600',
|
||||||
|
'Bold' = '800'
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
style,
|
||||||
|
{ ...(fontStyle && StyleConstants.FontStyle[fontStyle]) },
|
||||||
|
{ ...(fontSize && { fontSize: StyleConstants.Font.Size[fontSize] }) },
|
||||||
|
{
|
||||||
|
...(lineHeight && {
|
||||||
|
lineHeight: StyleConstants.Font.LineHeight[lineHeight]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fontWeight: fontWeight
|
||||||
|
? boldTextEnabled
|
||||||
|
? BoldMapping[fontWeight]
|
||||||
|
: StyleConstants.Font.Weight[fontWeight]
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
{...rest}
|
||||||
|
children={children}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomText
|
|
@ -1,10 +1,11 @@
|
||||||
import ComponentSeparator from '@components/Separator'
|
import ComponentSeparator from '@components/Separator'
|
||||||
import { useScrollToTop } from '@react-navigation/native'
|
import { useScrollToTop } from '@react-navigation/native'
|
||||||
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
|
||||||
import {
|
import {
|
||||||
getInstanceActive,
|
QueryKeyTimeline,
|
||||||
updateInstanceTimelineLookback
|
TimelineData,
|
||||||
} from '@utils/slices/instancesSlice'
|
useTimelineQuery
|
||||||
|
} from '@utils/queryHooks/timeline'
|
||||||
|
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { RefObject, useCallback, useRef } from 'react'
|
import React, { RefObject, useCallback, useRef } from 'react'
|
||||||
|
@ -13,22 +14,12 @@ import {
|
||||||
FlatListProps,
|
FlatListProps,
|
||||||
Platform,
|
Platform,
|
||||||
RefreshControl,
|
RefreshControl,
|
||||||
StyleSheet,
|
StyleSheet
|
||||||
ViewabilityConfigCallbackPairs
|
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import Animated, {
|
import { InfiniteData, useQueryClient } from 'react-query'
|
||||||
useAnimatedScrollHandler,
|
import { useSelector } from 'react-redux'
|
||||||
useSharedValue
|
|
||||||
} from 'react-native-reanimated'
|
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
|
||||||
import TimelineEmpty from './Timeline/Empty'
|
import TimelineEmpty from './Timeline/Empty'
|
||||||
import TimelineFooter from './Timeline/Footer'
|
import TimelineFooter from './Timeline/Footer'
|
||||||
import TimelineRefresh, {
|
|
||||||
SEPARATION_Y_1,
|
|
||||||
SEPARATION_Y_2
|
|
||||||
} from './Timeline/Refresh'
|
|
||||||
|
|
||||||
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList)
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
flRef?: RefObject<FlatList<any>>
|
flRef?: RefObject<FlatList<any>>
|
||||||
|
@ -45,17 +36,19 @@ const Timeline: React.FC<Props> = ({
|
||||||
queryKey,
|
queryKey,
|
||||||
disableRefresh = false,
|
disableRefresh = false,
|
||||||
disableInfinity = false,
|
disableInfinity = false,
|
||||||
lookback,
|
|
||||||
customProps
|
customProps
|
||||||
}) => {
|
}) => {
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
|
|
||||||
|
const queryClient = useQueryClient()
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
refetch,
|
refetch,
|
||||||
isFetching,
|
isFetching,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
fetchPreviousPage,
|
||||||
fetchNextPage,
|
fetchNextPage,
|
||||||
|
isFetchingPreviousPage,
|
||||||
isFetchingNextPage
|
isFetchingNextPage
|
||||||
} = useTimelineQuery({
|
} = useTimelineQuery({
|
||||||
...queryKey[1],
|
...queryKey[1],
|
||||||
|
@ -64,6 +57,12 @@ const Timeline: React.FC<Props> = ({
|
||||||
ios: ['dataUpdatedAt', 'isFetching'],
|
ios: ['dataUpdatedAt', 'isFetching'],
|
||||||
android: ['dataUpdatedAt', 'isFetching', 'isLoading']
|
android: ['dataUpdatedAt', 'isFetching', 'isLoading']
|
||||||
}),
|
}),
|
||||||
|
getPreviousPageParam: firstPage =>
|
||||||
|
firstPage?.links?.prev && {
|
||||||
|
min_id: firstPage.links.prev,
|
||||||
|
// https://github.com/facebook/react-native/issues/25239
|
||||||
|
limit: '10'
|
||||||
|
},
|
||||||
getNextPageParam: lastPage =>
|
getNextPageParam: lastPage =>
|
||||||
lastPage?.links?.next && {
|
lastPage?.links?.next && {
|
||||||
max_id: lastPage.links.next
|
max_id: lastPage.links.next
|
||||||
|
@ -92,26 +91,6 @@ const Timeline: React.FC<Props> = ({
|
||||||
)
|
)
|
||||||
|
|
||||||
const flRef = useRef<FlatList>(null)
|
const flRef = useRef<FlatList>(null)
|
||||||
const scrollY = useSharedValue(0)
|
|
||||||
const fetchingType = useSharedValue<0 | 1 | 2>(0)
|
|
||||||
|
|
||||||
const onScroll = useAnimatedScrollHandler(
|
|
||||||
{
|
|
||||||
onScroll: ({ contentOffset: { y } }) => {
|
|
||||||
scrollY.value = y
|
|
||||||
},
|
|
||||||
onEndDrag: ({ contentOffset: { y } }) => {
|
|
||||||
if (!disableRefresh && !isFetching) {
|
|
||||||
if (y <= SEPARATION_Y_2) {
|
|
||||||
fetchingType.value = 2
|
|
||||||
} else if (y <= SEPARATION_Y_1) {
|
|
||||||
fetchingType.value = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[isFetching]
|
|
||||||
)
|
|
||||||
|
|
||||||
const androidRefreshControl = Platform.select({
|
const androidRefreshControl = Platform.select({
|
||||||
android: {
|
android: {
|
||||||
|
@ -127,27 +106,6 @@ const Timeline: React.FC<Props> = ({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const dispatch = useDispatch()
|
|
||||||
const viewabilityPairs = useRef<ViewabilityConfigCallbackPairs>([
|
|
||||||
{
|
|
||||||
viewabilityConfig: {
|
|
||||||
minimumViewTime: 10,
|
|
||||||
viewAreaCoveragePercentThreshold: 10
|
|
||||||
},
|
|
||||||
onViewableItemsChanged: ({ viewableItems }) => {
|
|
||||||
lookback &&
|
|
||||||
dispatch(
|
|
||||||
updateInstanceTimelineLookback({
|
|
||||||
[lookback]: {
|
|
||||||
queryKey,
|
|
||||||
ids: viewableItems.map(item => item.key).slice(0, 3)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
useScrollToTop(flRef)
|
useScrollToTop(flRef)
|
||||||
useSelector(getInstanceActive, (prev, next) => {
|
useSelector(getInstanceActive, (prev, next) => {
|
||||||
if (prev !== next) {
|
if (prev !== next) {
|
||||||
|
@ -157,43 +115,46 @@ const Timeline: React.FC<Props> = ({
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<FlatList
|
||||||
<TimelineRefresh
|
ref={customFLRef || flRef}
|
||||||
flRef={flRef}
|
scrollEventThrottle={16}
|
||||||
queryKey={queryKey}
|
windowSize={7}
|
||||||
scrollY={scrollY}
|
data={flattenData}
|
||||||
fetchingType={fetchingType}
|
initialNumToRender={6}
|
||||||
disableRefresh={disableRefresh}
|
maxToRenderPerBatch={3}
|
||||||
/>
|
style={styles.flatList}
|
||||||
<AnimatedFlatList
|
onEndReached={onEndReached}
|
||||||
ref={customFLRef || flRef}
|
onEndReachedThreshold={0.75}
|
||||||
scrollEventThrottle={16}
|
ListFooterComponent={
|
||||||
onScroll={onScroll}
|
<TimelineFooter queryKey={queryKey} disableInfinity={disableInfinity} />
|
||||||
windowSize={7}
|
}
|
||||||
data={flattenData}
|
ListEmptyComponent={<TimelineEmpty queryKey={queryKey} />}
|
||||||
initialNumToRender={6}
|
ItemSeparatorComponent={ItemSeparatorComponent}
|
||||||
maxToRenderPerBatch={3}
|
{...(isFetchingPreviousPage && {
|
||||||
style={styles.flatList}
|
maintainVisibleContentPosition: { minIndexForVisible: 0 }
|
||||||
onEndReached={onEndReached}
|
})}
|
||||||
onEndReachedThreshold={0.75}
|
refreshing={isFetchingPreviousPage}
|
||||||
ListFooterComponent={
|
onRefresh={() => {
|
||||||
<TimelineFooter
|
if (!disableRefresh && !isFetchingPreviousPage) {
|
||||||
queryKey={queryKey}
|
queryClient.setQueryData<InfiniteData<TimelineData> | undefined>(
|
||||||
disableInfinity={disableInfinity}
|
queryKey,
|
||||||
/>
|
data => {
|
||||||
|
if (data?.pages[0] && data.pages[0].body.length === 0) {
|
||||||
|
return {
|
||||||
|
pages: data.pages.slice(1),
|
||||||
|
pageParams: data.pageParams.slice(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
fetchPreviousPage()
|
||||||
}
|
}
|
||||||
ListEmptyComponent={<TimelineEmpty queryKey={queryKey} />}
|
}}
|
||||||
ItemSeparatorComponent={ItemSeparatorComponent}
|
{...androidRefreshControl}
|
||||||
maintainVisibleContentPosition={{
|
{...customProps}
|
||||||
minIndexForVisible: 0
|
/>
|
||||||
}}
|
|
||||||
{...(lookback && {
|
|
||||||
viewabilityConfigCallbackPairs: viewabilityPairs.current
|
|
||||||
})}
|
|
||||||
{...androidRefreshControl}
|
|
||||||
{...customProps}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,14 @@ import analytics from '@components/analytics'
|
||||||
import GracefullyImage from '@components/GracefullyImage'
|
import GracefullyImage from '@components/GracefullyImage'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
import { isEqual } from 'lodash'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import { useMutation, useQueryClient } from 'react-query'
|
import { useMutation, useQueryClient } from 'react-query'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import TimelineActions from './Shared/Actions'
|
import TimelineActions from './Shared/Actions'
|
||||||
|
@ -53,117 +55,107 @@ export interface Props {
|
||||||
highlighted?: boolean
|
highlighted?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimelineConversation: React.FC<Props> = ({
|
const TimelineConversation = React.memo(
|
||||||
conversation,
|
({ conversation, queryKey, highlighted = false }: Props) => {
|
||||||
queryKey,
|
const instanceAccount = useSelector(
|
||||||
highlighted = false
|
getInstanceAccount,
|
||||||
}) => {
|
(prev, next) => prev?.id === next?.id
|
||||||
const instanceAccount = useSelector(
|
)
|
||||||
getInstanceAccount,
|
const { colors } = useTheme()
|
||||||
(prev, next) => prev?.id === next?.id
|
|
||||||
)
|
|
||||||
const { colors } = useTheme()
|
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const fireMutation = useCallback(() => {
|
const fireMutation = useCallback(() => {
|
||||||
return apiInstance<Mastodon.Conversation>({
|
return apiInstance<Mastodon.Conversation>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: `conversations/${conversation.id}/read`
|
url: `conversations/${conversation.id}/read`
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
const { mutate } = useMutation(fireMutation, {
|
|
||||||
onSettled: () => {
|
|
||||||
queryClient.invalidateQueries(queryKey)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const navigation =
|
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
|
||||||
const onPress = useCallback(() => {
|
|
||||||
analytics('timeline_conversation_press')
|
|
||||||
if (conversation.last_status) {
|
|
||||||
conversation.unread && mutate()
|
|
||||||
navigation.push('Tab-Shared-Toot', {
|
|
||||||
toot: conversation.last_status,
|
|
||||||
rootQueryKey: queryKey
|
|
||||||
})
|
})
|
||||||
}
|
}, [])
|
||||||
}, [])
|
const { mutate } = useMutation(fireMutation, {
|
||||||
|
onSettled: () => {
|
||||||
|
queryClient.invalidateQueries(queryKey)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
const navigation =
|
||||||
<Pressable
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
style={[
|
const onPress = useCallback(() => {
|
||||||
styles.base,
|
analytics('timeline_conversation_press')
|
||||||
{ backgroundColor: colors.backgroundDefault },
|
if (conversation.last_status) {
|
||||||
conversation.unread && {
|
conversation.unread && mutate()
|
||||||
borderLeftWidth: StyleConstants.Spacing.XS,
|
navigation.push('Tab-Shared-Toot', {
|
||||||
borderLeftColor: colors.blue,
|
toot: conversation.last_status,
|
||||||
paddingLeft:
|
rootQueryKey: queryKey
|
||||||
StyleConstants.Spacing.Global.PagePadding -
|
})
|
||||||
StyleConstants.Spacing.XS
|
}
|
||||||
}
|
}, [])
|
||||||
]}
|
|
||||||
onPress={onPress}
|
|
||||||
>
|
|
||||||
<View style={styles.header}>
|
|
||||||
<Avatars accounts={conversation.accounts} />
|
|
||||||
<TimelineHeaderConversation
|
|
||||||
queryKey={queryKey}
|
|
||||||
conversation={conversation}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{conversation.last_status ? (
|
return (
|
||||||
<>
|
<Pressable
|
||||||
<View
|
style={[
|
||||||
style={{
|
{
|
||||||
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
flex: 1,
|
||||||
paddingLeft: highlighted
|
flexDirection: 'column',
|
||||||
? 0
|
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||||
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
paddingBottom: 0,
|
||||||
}}
|
backgroundColor: colors.backgroundDefault
|
||||||
>
|
},
|
||||||
<TimelineContent
|
conversation.unread && {
|
||||||
|
borderLeftWidth: StyleConstants.Spacing.XS,
|
||||||
|
borderLeftColor: colors.blue,
|
||||||
|
paddingLeft:
|
||||||
|
StyleConstants.Spacing.Global.PagePadding -
|
||||||
|
StyleConstants.Spacing.XS
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
onPress={onPress}
|
||||||
|
>
|
||||||
|
<View style={{ flex: 1, width: '100%', flexDirection: 'row' }}>
|
||||||
|
<Avatars accounts={conversation.accounts} />
|
||||||
|
<TimelineHeaderConversation
|
||||||
|
queryKey={queryKey}
|
||||||
|
conversation={conversation}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{conversation.last_status ? (
|
||||||
|
<>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
||||||
|
paddingLeft: highlighted
|
||||||
|
? 0
|
||||||
|
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TimelineContent
|
||||||
|
status={conversation.last_status}
|
||||||
|
highlighted={highlighted}
|
||||||
|
/>
|
||||||
|
{conversation.last_status.poll ? (
|
||||||
|
<TimelinePoll
|
||||||
|
queryKey={queryKey}
|
||||||
|
statusId={conversation.last_status.id}
|
||||||
|
poll={conversation.last_status.poll}
|
||||||
|
reblog={false}
|
||||||
|
sameAccount={
|
||||||
|
conversation.last_status.id === instanceAccount?.id
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
<TimelineActions
|
||||||
|
queryKey={queryKey}
|
||||||
status={conversation.last_status}
|
status={conversation.last_status}
|
||||||
highlighted={highlighted}
|
highlighted={highlighted}
|
||||||
|
accts={conversation.accounts.map(account => account.acct)}
|
||||||
|
reblog={false}
|
||||||
/>
|
/>
|
||||||
{conversation.last_status.poll ? (
|
</>
|
||||||
<TimelinePoll
|
) : null}
|
||||||
queryKey={queryKey}
|
</Pressable>
|
||||||
statusId={conversation.last_status.id}
|
)
|
||||||
poll={conversation.last_status.poll}
|
|
||||||
reblog={false}
|
|
||||||
sameAccount={
|
|
||||||
conversation.last_status.id === instanceAccount?.id
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</View>
|
|
||||||
<TimelineActions
|
|
||||||
queryKey={queryKey}
|
|
||||||
status={conversation.last_status}
|
|
||||||
highlighted={highlighted}
|
|
||||||
accts={conversation.accounts.map(account => account.acct)}
|
|
||||||
reblog={false}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</Pressable>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'column',
|
|
||||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
|
||||||
paddingBottom: 0
|
|
||||||
},
|
},
|
||||||
header: {
|
(prev, next) => isEqual(prev.conversation, next.conversation)
|
||||||
flex: 1,
|
)
|
||||||
width: '100%',
|
|
||||||
flexDirection: 'row'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineConversation
|
export default TimelineConversation
|
||||||
|
|
|
@ -9,15 +9,16 @@ import TimelineHeaderDefault from '@components/Timeline/Shared/HeaderDefault'
|
||||||
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { uniqBy } from 'lodash'
|
import { isEqual, uniqBy } from 'lodash'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import TimelineActionsUsers from './Shared/ActionsUsers'
|
import TimelineFeedback from './Shared/Feedback'
|
||||||
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
||||||
import TimelineFullConversation from './Shared/FullConversation'
|
import TimelineFullConversation from './Shared/FullConversation'
|
||||||
import TimelineTranslate from './Shared/Translate'
|
import TimelineTranslate from './Shared/Translate'
|
||||||
|
@ -33,148 +34,144 @@ export interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the poll is long
|
// When the poll is long
|
||||||
const TimelineDefault: React.FC<Props> = ({
|
const TimelineDefault = React.memo(
|
||||||
item,
|
({
|
||||||
queryKey,
|
item,
|
||||||
rootQueryKey,
|
queryKey,
|
||||||
origin,
|
rootQueryKey,
|
||||||
highlighted = false,
|
origin,
|
||||||
disableDetails = false,
|
highlighted = false,
|
||||||
disableOnPress = false
|
disableDetails = false,
|
||||||
}) => {
|
disableOnPress = false
|
||||||
const { colors } = useTheme()
|
}: Props) => {
|
||||||
const instanceAccount = useSelector(getInstanceAccount, () => true)
|
const { colors } = useTheme()
|
||||||
const navigation =
|
const instanceAccount = useSelector(getInstanceAccount, () => true)
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
const navigation =
|
||||||
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
|
|
||||||
const actualStatus = item.reblog ? item.reblog : item
|
const actualStatus = item.reblog ? item.reblog : item
|
||||||
|
|
||||||
const ownAccount = actualStatus.account?.id === instanceAccount?.id
|
const ownAccount = actualStatus.account?.id === instanceAccount?.id
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!highlighted &&
|
|
||||||
queryKey &&
|
|
||||||
shouldFilter({ status: actualStatus, queryKey })
|
|
||||||
) {
|
|
||||||
return <TimelineFiltered />
|
|
||||||
}
|
|
||||||
|
|
||||||
const onPress = useCallback(() => {
|
|
||||||
analytics('timeline_default_press', {
|
|
||||||
page: queryKey ? queryKey[1].page : origin
|
|
||||||
})
|
|
||||||
!disableOnPress &&
|
|
||||||
!highlighted &&
|
!highlighted &&
|
||||||
navigation.push('Tab-Shared-Toot', {
|
queryKey &&
|
||||||
toot: actualStatus,
|
shouldFilter({ status: actualStatus, queryKey })
|
||||||
rootQueryKey: queryKey
|
) {
|
||||||
})
|
return <TimelineFiltered />
|
||||||
}, [])
|
}
|
||||||
|
|
||||||
return (
|
const onPress = useCallback(() => {
|
||||||
<Pressable
|
analytics('timeline_default_press', {
|
||||||
accessible={highlighted ? false : true}
|
page: queryKey ? queryKey[1].page : origin
|
||||||
style={[
|
})
|
||||||
styles.statusView,
|
!disableOnPress &&
|
||||||
{
|
!highlighted &&
|
||||||
|
navigation.push('Tab-Shared-Toot', {
|
||||||
|
toot: actualStatus,
|
||||||
|
rootQueryKey: queryKey
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
accessible={highlighted ? false : true}
|
||||||
|
style={{
|
||||||
|
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||||
backgroundColor: colors.backgroundDefault,
|
backgroundColor: colors.backgroundDefault,
|
||||||
paddingBottom:
|
paddingBottom:
|
||||||
disableDetails && disableOnPress
|
disableDetails && disableOnPress
|
||||||
? StyleConstants.Spacing.Global.PagePadding
|
? StyleConstants.Spacing.Global.PagePadding
|
||||||
: 0
|
: 0
|
||||||
}
|
|
||||||
]}
|
|
||||||
onPress={onPress}
|
|
||||||
>
|
|
||||||
{item.reblog ? (
|
|
||||||
<TimelineActioned action='reblog' account={item.account} />
|
|
||||||
) : item._pinned ? (
|
|
||||||
<TimelineActioned action='pinned' account={item.account} />
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
<View style={styles.header}>
|
|
||||||
<TimelineAvatar
|
|
||||||
queryKey={disableOnPress ? undefined : queryKey}
|
|
||||||
account={actualStatus.account}
|
|
||||||
highlighted={highlighted}
|
|
||||||
/>
|
|
||||||
<TimelineHeaderDefault
|
|
||||||
queryKey={disableOnPress ? undefined : queryKey}
|
|
||||||
rootQueryKey={disableOnPress ? undefined : rootQueryKey}
|
|
||||||
status={actualStatus}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
|
||||||
paddingLeft: highlighted
|
|
||||||
? 0
|
|
||||||
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
|
||||||
}}
|
}}
|
||||||
|
onPress={onPress}
|
||||||
>
|
>
|
||||||
{typeof actualStatus.content === 'string' &&
|
{item.reblog ? (
|
||||||
actualStatus.content.length > 0 ? (
|
<TimelineActioned action='reblog' account={item.account} />
|
||||||
<TimelineContent
|
) : item._pinned ? (
|
||||||
|
<TimelineActioned action='pinned' account={item.account} />
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<View style={{ flex: 1, width: '100%', flexDirection: 'row' }}>
|
||||||
|
<TimelineAvatar
|
||||||
|
queryKey={disableOnPress ? undefined : queryKey}
|
||||||
|
account={actualStatus.account}
|
||||||
|
highlighted={highlighted}
|
||||||
|
/>
|
||||||
|
<TimelineHeaderDefault
|
||||||
|
queryKey={disableOnPress ? undefined : queryKey}
|
||||||
|
rootQueryKey={disableOnPress ? undefined : rootQueryKey}
|
||||||
status={actualStatus}
|
status={actualStatus}
|
||||||
highlighted={highlighted}
|
highlighted={highlighted}
|
||||||
disableDetails={disableDetails}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
</View>
|
||||||
{queryKey && actualStatus.poll ? (
|
|
||||||
<TimelinePoll
|
<View
|
||||||
|
style={{
|
||||||
|
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
||||||
|
paddingLeft: highlighted
|
||||||
|
? 0
|
||||||
|
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{typeof actualStatus.content === 'string' &&
|
||||||
|
actualStatus.content.length > 0 ? (
|
||||||
|
<TimelineContent
|
||||||
|
status={actualStatus}
|
||||||
|
highlighted={highlighted}
|
||||||
|
disableDetails={disableDetails}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{queryKey && actualStatus.poll ? (
|
||||||
|
<TimelinePoll
|
||||||
|
queryKey={queryKey}
|
||||||
|
rootQueryKey={rootQueryKey}
|
||||||
|
statusId={actualStatus.id}
|
||||||
|
poll={actualStatus.poll}
|
||||||
|
reblog={item.reblog ? true : false}
|
||||||
|
sameAccount={ownAccount}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{!disableDetails &&
|
||||||
|
Array.isArray(actualStatus.media_attachments) &&
|
||||||
|
actualStatus.media_attachments.length ? (
|
||||||
|
<TimelineAttachment status={actualStatus} />
|
||||||
|
) : null}
|
||||||
|
{!disableDetails && actualStatus.card ? (
|
||||||
|
<TimelineCard card={actualStatus.card} />
|
||||||
|
) : null}
|
||||||
|
{!disableDetails ? (
|
||||||
|
<TimelineFullConversation
|
||||||
|
queryKey={queryKey}
|
||||||
|
status={actualStatus}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<TimelineTranslate status={actualStatus} highlighted={highlighted} />
|
||||||
|
<TimelineFeedback status={actualStatus} highlighted={highlighted} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{queryKey && !disableDetails ? (
|
||||||
|
<TimelineActions
|
||||||
queryKey={queryKey}
|
queryKey={queryKey}
|
||||||
rootQueryKey={rootQueryKey}
|
rootQueryKey={rootQueryKey}
|
||||||
statusId={actualStatus.id}
|
highlighted={highlighted}
|
||||||
poll={actualStatus.poll}
|
status={actualStatus}
|
||||||
|
accts={uniqBy(
|
||||||
|
(
|
||||||
|
[actualStatus.account] as Mastodon.Account[] &
|
||||||
|
Mastodon.Mention[]
|
||||||
|
)
|
||||||
|
.concat(actualStatus.mentions)
|
||||||
|
.filter(d => d?.id !== instanceAccount?.id),
|
||||||
|
d => d?.id
|
||||||
|
).map(d => d?.acct)}
|
||||||
reblog={item.reblog ? true : false}
|
reblog={item.reblog ? true : false}
|
||||||
sameAccount={ownAccount}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{!disableDetails &&
|
</Pressable>
|
||||||
Array.isArray(actualStatus.media_attachments) &&
|
)
|
||||||
actualStatus.media_attachments.length ? (
|
|
||||||
<TimelineAttachment status={actualStatus} />
|
|
||||||
) : null}
|
|
||||||
{!disableDetails && actualStatus.card ? (
|
|
||||||
<TimelineCard card={actualStatus.card} />
|
|
||||||
) : null}
|
|
||||||
{!disableDetails ? (
|
|
||||||
<TimelineFullConversation queryKey={queryKey} status={actualStatus} />
|
|
||||||
) : null}
|
|
||||||
<TimelineTranslate status={actualStatus} highlighted={highlighted} />
|
|
||||||
<TimelineActionsUsers status={actualStatus} highlighted={highlighted} />
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{queryKey && !disableDetails ? (
|
|
||||||
<TimelineActions
|
|
||||||
queryKey={queryKey}
|
|
||||||
rootQueryKey={rootQueryKey}
|
|
||||||
highlighted={highlighted}
|
|
||||||
status={actualStatus}
|
|
||||||
accts={uniqBy(
|
|
||||||
([actualStatus.account] as Mastodon.Account[] & Mastodon.Mention[])
|
|
||||||
.concat(actualStatus.mentions)
|
|
||||||
.filter(d => d?.id !== instanceAccount?.id),
|
|
||||||
d => d?.id
|
|
||||||
).map(d => d?.acct)}
|
|
||||||
reblog={item.reblog ? true : false}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Pressable>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
statusView: {
|
|
||||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
|
||||||
paddingBottom: 0
|
|
||||||
},
|
},
|
||||||
header: {
|
(prev, next) => isEqual(prev.item, next.item)
|
||||||
flex: 1,
|
)
|
||||||
width: '100%',
|
|
||||||
flexDirection: 'row'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineDefault
|
export default TimelineDefault
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import Button from '@components/Button'
|
import Button from '@components/Button'
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useMemo } from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyleSheet, Text, View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import { Circle } from 'react-native-animated-spinkit'
|
import { Circle } from 'react-native-animated-spinkit'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -20,10 +21,10 @@ const TimelineEmpty = React.memo(
|
||||||
options: { notifyOnChangeProps: ['status'] }
|
options: { notifyOnChangeProps: ['status'] }
|
||||||
})
|
})
|
||||||
|
|
||||||
const { colors, theme } = useTheme()
|
const { colors } = useTheme()
|
||||||
const { t, i18n } = useTranslation('componentTimeline')
|
const { t } = useTranslation('componentTimeline')
|
||||||
|
|
||||||
const children = useMemo(() => {
|
const children = () => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'loading':
|
case 'loading':
|
||||||
return (
|
return (
|
||||||
|
@ -40,9 +41,16 @@ const TimelineEmpty = React.memo(
|
||||||
size={StyleConstants.Font.Size.L}
|
size={StyleConstants.Font.Size.L}
|
||||||
color={colors.primaryDefault}
|
color={colors.primaryDefault}
|
||||||
/>
|
/>
|
||||||
<Text style={[styles.error, { color: colors.primaryDefault }]}>
|
<CustomText
|
||||||
|
fontStyle='M'
|
||||||
|
style={{
|
||||||
|
marginTop: StyleConstants.Spacing.S,
|
||||||
|
marginBottom: StyleConstants.Spacing.L,
|
||||||
|
color: colors.primaryDefault
|
||||||
|
}}
|
||||||
|
>
|
||||||
{t('empty.error.message')}
|
{t('empty.error.message')}
|
||||||
</Text>
|
</CustomText>
|
||||||
<Button
|
<Button
|
||||||
type='text'
|
type='text'
|
||||||
content={t('empty.error.button')}
|
content={t('empty.error.button')}
|
||||||
|
@ -61,35 +69,35 @@ const TimelineEmpty = React.memo(
|
||||||
size={StyleConstants.Font.Size.L}
|
size={StyleConstants.Font.Size.L}
|
||||||
color={colors.primaryDefault}
|
color={colors.primaryDefault}
|
||||||
/>
|
/>
|
||||||
<Text style={[styles.error, { color: colors.primaryDefault }]}>
|
<CustomText
|
||||||
|
fontStyle='M'
|
||||||
|
style={{
|
||||||
|
marginTop: StyleConstants.Spacing.S,
|
||||||
|
marginBottom: StyleConstants.Spacing.L,
|
||||||
|
color: colors.primaryDefault
|
||||||
|
}}
|
||||||
|
>
|
||||||
{t('empty.success.message')}
|
{t('empty.success.message')}
|
||||||
</Text>
|
</CustomText>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, [theme, i18n.language, status])
|
}
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[styles.base, { backgroundColor: colors.backgroundDefault }]}
|
style={{
|
||||||
children={children}
|
flex: 1,
|
||||||
/>
|
minHeight: '100%',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: colors.backgroundDefault
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children()}
|
||||||
|
</View>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
() => true
|
() => true
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flex: 1,
|
|
||||||
minHeight: '100%',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
...StyleConstants.FontStyle.M,
|
|
||||||
marginTop: StyleConstants.Spacing.S,
|
|
||||||
marginBottom: StyleConstants.Spacing.L
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineEmpty
|
export default TimelineEmpty
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Trans } from 'react-i18next'
|
import { Trans } from 'react-i18next'
|
||||||
import { StyleSheet, Text, View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import { Circle } from 'react-native-animated-spinkit'
|
import { Circle } from 'react-native-animated-spinkit'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -27,11 +28,18 @@ const TimelineFooter = React.memo(
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: StyleConstants.Spacing.M
|
||||||
|
}}
|
||||||
|
>
|
||||||
{!disableInfinity && hasNextPage ? (
|
{!disableInfinity && hasNextPage ? (
|
||||||
<Circle size={StyleConstants.Font.Size.L} color={colors.secondary} />
|
<Circle size={StyleConstants.Font.Size.L} color={colors.secondary} />
|
||||||
) : (
|
) : (
|
||||||
<Text style={[styles.text, { color: colors.secondary }]}>
|
<CustomText fontStyle='S' style={{ color: colors.secondary }}>
|
||||||
<Trans
|
<Trans
|
||||||
i18nKey='componentTimeline:end.message'
|
i18nKey='componentTimeline:end.message'
|
||||||
components={[
|
components={[
|
||||||
|
@ -42,7 +50,7 @@ const TimelineFooter = React.memo(
|
||||||
/>
|
/>
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Text>
|
</CustomText>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
@ -50,16 +58,4 @@ const TimelineFooter = React.memo(
|
||||||
() => true
|
() => true
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
padding: StyleConstants.Spacing.M
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
...StyleConstants.FontStyle.S
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineFooter
|
export default TimelineFooter
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
|
||||||
import React from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { StyleSheet, Text, View } from 'react-native'
|
|
||||||
|
|
||||||
const TimelineLookback = React.memo(
|
|
||||||
() => {
|
|
||||||
const { t } = useTranslation('componentTimeline')
|
|
||||||
const { colors } = useTheme()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={[styles.base, { backgroundColor: colors.backgroundDefault }]}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
style={[StyleConstants.FontStyle.S, { color: colors.primaryDefault }]}
|
|
||||||
>
|
|
||||||
{t('lookback.message')}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
() => true
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
padding: StyleConstants.Spacing.S
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
...StyleConstants.FontStyle.S
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineLookback
|
|
|
@ -9,13 +9,14 @@ import TimelineHeaderNotification from '@components/Timeline/Shared/HeaderNotifi
|
||||||
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
import TimelinePoll from '@components/Timeline/Shared/Poll'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { uniqBy } from 'lodash'
|
import { isEqual, uniqBy } from 'lodash'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
import TimelineFiltered, { shouldFilter } from './Shared/Filtered'
|
||||||
import TimelineFullConversation from './Shared/FullConversation'
|
import TimelineFullConversation from './Shared/FullConversation'
|
||||||
|
@ -26,151 +27,137 @@ export interface Props {
|
||||||
highlighted?: boolean
|
highlighted?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimelineNotifications: React.FC<Props> = ({
|
const TimelineNotifications = React.memo(
|
||||||
notification,
|
({ notification, queryKey, highlighted = false }: Props) => {
|
||||||
queryKey,
|
if (
|
||||||
highlighted = false
|
notification.status &&
|
||||||
}) => {
|
shouldFilter({ status: notification.status, queryKey })
|
||||||
if (
|
) {
|
||||||
notification.status &&
|
return <TimelineFiltered />
|
||||||
shouldFilter({ status: notification.status, queryKey })
|
}
|
||||||
) {
|
|
||||||
return <TimelineFiltered />
|
|
||||||
}
|
|
||||||
|
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const instanceAccount = useSelector(
|
const instanceAccount = useSelector(
|
||||||
getInstanceAccount,
|
getInstanceAccount,
|
||||||
(prev, next) => prev?.id === next?.id
|
(prev, next) => prev?.id === next?.id
|
||||||
)
|
)
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
|
|
||||||
const actualAccount = notification.status
|
const actualAccount = notification.status
|
||||||
? notification.status.account
|
? notification.status.account
|
||||||
: notification.account
|
: notification.account
|
||||||
|
|
||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
analytics('timeline_notification_press')
|
analytics('timeline_notification_press')
|
||||||
notification.status &&
|
notification.status &&
|
||||||
navigation.push('Tab-Shared-Toot', {
|
navigation.push('Tab-Shared-Toot', {
|
||||||
toot: notification.status,
|
toot: notification.status,
|
||||||
rootQueryKey: queryKey
|
rootQueryKey: queryKey
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
style={[
|
style={{
|
||||||
styles.notificationView,
|
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||||
{
|
|
||||||
backgroundColor: colors.backgroundDefault,
|
backgroundColor: colors.backgroundDefault,
|
||||||
paddingBottom: notification.status
|
paddingBottom: notification.status
|
||||||
? 0
|
? 0
|
||||||
: StyleConstants.Spacing.Global.PagePadding
|
: StyleConstants.Spacing.Global.PagePadding
|
||||||
}
|
|
||||||
]}
|
|
||||||
onPress={onPress}
|
|
||||||
>
|
|
||||||
{notification.type !== 'mention' ? (
|
|
||||||
<TimelineActioned
|
|
||||||
action={notification.type}
|
|
||||||
account={notification.account}
|
|
||||||
notification
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
opacity:
|
|
||||||
notification.type === 'follow' ||
|
|
||||||
notification.type === 'follow_request' ||
|
|
||||||
notification.type === 'mention' ||
|
|
||||||
notification.type === 'status'
|
|
||||||
? 1
|
|
||||||
: 0.5
|
|
||||||
}}
|
}}
|
||||||
|
onPress={onPress}
|
||||||
>
|
>
|
||||||
<View style={styles.header}>
|
{notification.type !== 'mention' ? (
|
||||||
<TimelineAvatar
|
<TimelineActioned
|
||||||
queryKey={queryKey}
|
action={notification.type}
|
||||||
account={actualAccount}
|
account={notification.account}
|
||||||
highlighted={highlighted}
|
notification
|
||||||
/>
|
|
||||||
<TimelineHeaderNotification
|
|
||||||
queryKey={queryKey}
|
|
||||||
notification={notification}
|
|
||||||
/>
|
/>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
opacity:
|
||||||
|
notification.type === 'follow' ||
|
||||||
|
notification.type === 'follow_request' ||
|
||||||
|
notification.type === 'mention' ||
|
||||||
|
notification.type === 'status'
|
||||||
|
? 1
|
||||||
|
: 0.5
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View style={{ flex: 1, width: '100%', flexDirection: 'row' }}>
|
||||||
|
<TimelineAvatar
|
||||||
|
queryKey={queryKey}
|
||||||
|
account={actualAccount}
|
||||||
|
highlighted={highlighted}
|
||||||
|
/>
|
||||||
|
<TimelineHeaderNotification
|
||||||
|
queryKey={queryKey}
|
||||||
|
notification={notification}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{notification.status ? (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
||||||
|
paddingLeft: highlighted
|
||||||
|
? 0
|
||||||
|
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{notification.status.content.length > 0 ? (
|
||||||
|
<TimelineContent
|
||||||
|
status={notification.status}
|
||||||
|
highlighted={highlighted}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{notification.status.poll ? (
|
||||||
|
<TimelinePoll
|
||||||
|
queryKey={queryKey}
|
||||||
|
statusId={notification.status.id}
|
||||||
|
poll={notification.status.poll}
|
||||||
|
reblog={false}
|
||||||
|
sameAccount={notification.account.id === instanceAccount?.id}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{notification.status.media_attachments.length > 0 ? (
|
||||||
|
<TimelineAttachment status={notification.status} />
|
||||||
|
) : null}
|
||||||
|
{notification.status.card ? (
|
||||||
|
<TimelineCard card={notification.status.card} />
|
||||||
|
) : null}
|
||||||
|
<TimelineFullConversation
|
||||||
|
queryKey={queryKey}
|
||||||
|
status={notification.status}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{notification.status ? (
|
{notification.status ? (
|
||||||
<View
|
<TimelineActions
|
||||||
style={{
|
queryKey={queryKey}
|
||||||
paddingTop: highlighted ? StyleConstants.Spacing.S : 0,
|
status={notification.status}
|
||||||
paddingLeft: highlighted
|
highlighted={highlighted}
|
||||||
? 0
|
accts={uniqBy(
|
||||||
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
(
|
||||||
}}
|
[notification.status.account] as Mastodon.Account[] &
|
||||||
>
|
Mastodon.Mention[]
|
||||||
{notification.status.content.length > 0 ? (
|
)
|
||||||
<TimelineContent
|
.concat(notification.status.mentions)
|
||||||
status={notification.status}
|
.filter(d => d?.id !== instanceAccount?.id),
|
||||||
highlighted={highlighted}
|
d => d?.id
|
||||||
/>
|
).map(d => d?.acct)}
|
||||||
) : null}
|
reblog={false}
|
||||||
{notification.status.poll ? (
|
/>
|
||||||
<TimelinePoll
|
|
||||||
queryKey={queryKey}
|
|
||||||
statusId={notification.status.id}
|
|
||||||
poll={notification.status.poll}
|
|
||||||
reblog={false}
|
|
||||||
sameAccount={notification.account.id === instanceAccount?.id}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{notification.status.media_attachments.length > 0 ? (
|
|
||||||
<TimelineAttachment status={notification.status} />
|
|
||||||
) : null}
|
|
||||||
{notification.status.card ? (
|
|
||||||
<TimelineCard card={notification.status.card} />
|
|
||||||
) : null}
|
|
||||||
<TimelineFullConversation
|
|
||||||
queryKey={queryKey}
|
|
||||||
status={notification.status}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</Pressable>
|
||||||
|
)
|
||||||
{notification.status ? (
|
|
||||||
<TimelineActions
|
|
||||||
queryKey={queryKey}
|
|
||||||
status={notification.status}
|
|
||||||
highlighted={highlighted}
|
|
||||||
accts={uniqBy(
|
|
||||||
(
|
|
||||||
[notification.status.account] as Mastodon.Account[] &
|
|
||||||
Mastodon.Mention[]
|
|
||||||
)
|
|
||||||
.concat(notification.status.mentions)
|
|
||||||
.filter(d => d?.id !== instanceAccount?.id),
|
|
||||||
d => d?.id
|
|
||||||
).map(d => d?.acct)}
|
|
||||||
reblog={false}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Pressable>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
notificationView: {
|
|
||||||
padding: StyleConstants.Spacing.Global.PagePadding
|
|
||||||
},
|
},
|
||||||
header: {
|
(prev, next) => isEqual(prev.notification, next.notification)
|
||||||
flex: 1,
|
)
|
||||||
width: '100%',
|
|
||||||
flexDirection: 'row'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineNotifications
|
export default TimelineNotifications
|
||||||
|
|
|
@ -1,323 +0,0 @@
|
||||||
import haptics from '@components/haptics'
|
|
||||||
import Icon from '@components/Icon'
|
|
||||||
import {
|
|
||||||
QueryKeyTimeline,
|
|
||||||
TimelineData,
|
|
||||||
useTimelineQuery
|
|
||||||
} from '@utils/queryHooks/timeline'
|
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
|
||||||
import React, { RefObject, useCallback, useRef, useState } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { FlatList, Platform, StyleSheet, Text, View } from 'react-native'
|
|
||||||
import { Circle } from 'react-native-animated-spinkit'
|
|
||||||
import Animated, {
|
|
||||||
Extrapolate,
|
|
||||||
interpolate,
|
|
||||||
runOnJS,
|
|
||||||
useAnimatedReaction,
|
|
||||||
useAnimatedStyle,
|
|
||||||
useSharedValue,
|
|
||||||
withTiming
|
|
||||||
} from 'react-native-reanimated'
|
|
||||||
import { InfiniteData, useQueryClient } from 'react-query'
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
flRef: RefObject<FlatList<any>>
|
|
||||||
queryKey: QueryKeyTimeline
|
|
||||||
scrollY: Animated.SharedValue<number>
|
|
||||||
fetchingType: Animated.SharedValue<0 | 1 | 2>
|
|
||||||
disableRefresh?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const CONTAINER_HEIGHT = StyleConstants.Spacing.M * 2.5
|
|
||||||
export const SEPARATION_Y_1 = -(
|
|
||||||
CONTAINER_HEIGHT / 2 +
|
|
||||||
StyleConstants.Font.Size.S / 2
|
|
||||||
)
|
|
||||||
export const SEPARATION_Y_2 = -(
|
|
||||||
CONTAINER_HEIGHT * 1.5 +
|
|
||||||
StyleConstants.Font.Size.S / 2
|
|
||||||
)
|
|
||||||
|
|
||||||
const TimelineRefresh: React.FC<Props> = ({
|
|
||||||
flRef,
|
|
||||||
queryKey,
|
|
||||||
scrollY,
|
|
||||||
fetchingType,
|
|
||||||
disableRefresh = false
|
|
||||||
}) => {
|
|
||||||
if (Platform.OS !== 'ios') {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (disableRefresh) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchingLatestIndex = useRef(0)
|
|
||||||
const refetchActive = useRef(false)
|
|
||||||
|
|
||||||
const {
|
|
||||||
refetch,
|
|
||||||
isFetching,
|
|
||||||
isLoading,
|
|
||||||
fetchPreviousPage,
|
|
||||||
hasPreviousPage,
|
|
||||||
isFetchingNextPage
|
|
||||||
} = useTimelineQuery({
|
|
||||||
...queryKey[1],
|
|
||||||
options: {
|
|
||||||
getPreviousPageParam: firstPage =>
|
|
||||||
firstPage?.links?.prev && {
|
|
||||||
min_id: firstPage.links.prev,
|
|
||||||
// https://github.com/facebook/react-native/issues/25239#issuecomment-731100372
|
|
||||||
limit: '5'
|
|
||||||
},
|
|
||||||
select: data => {
|
|
||||||
if (refetchActive.current) {
|
|
||||||
data.pageParams = [data.pageParams[0]]
|
|
||||||
data.pages = [data.pages[0]]
|
|
||||||
refetchActive.current = false
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
if (fetchingLatestIndex.current > 0) {
|
|
||||||
if (fetchingLatestIndex.current > 5) {
|
|
||||||
clearFirstPage()
|
|
||||||
fetchingLatestIndex.current = 0
|
|
||||||
} else {
|
|
||||||
if (hasPreviousPage) {
|
|
||||||
fetchPreviousPage()
|
|
||||||
fetchingLatestIndex.current++
|
|
||||||
} else {
|
|
||||||
clearFirstPage()
|
|
||||||
fetchingLatestIndex.current = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const { t } = useTranslation('componentTimeline')
|
|
||||||
const { colors } = useTheme()
|
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
|
||||||
const clearFirstPage = () => {
|
|
||||||
queryClient.setQueryData<InfiniteData<TimelineData> | undefined>(
|
|
||||||
queryKey,
|
|
||||||
data => {
|
|
||||||
if (data?.pages[0] && data.pages[0].body.length === 0) {
|
|
||||||
return {
|
|
||||||
pages: data.pages.slice(1),
|
|
||||||
pageParams: data.pageParams.slice(1)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const prepareRefetch = () => {
|
|
||||||
refetchActive.current = true
|
|
||||||
queryClient.setQueryData<InfiniteData<TimelineData> | undefined>(
|
|
||||||
queryKey,
|
|
||||||
data => {
|
|
||||||
if (data) {
|
|
||||||
data.pageParams = [undefined]
|
|
||||||
const newFirstPage: TimelineData = { body: [] }
|
|
||||||
for (let page of data.pages) {
|
|
||||||
// @ts-ignore
|
|
||||||
newFirstPage.body.push(...page.body)
|
|
||||||
if (newFirstPage.body.length > 10) break
|
|
||||||
}
|
|
||||||
data.pages = [newFirstPage]
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const callRefetch = async () => {
|
|
||||||
await refetch()
|
|
||||||
setTimeout(() => flRef.current?.scrollToOffset({ offset: 1 }), 50)
|
|
||||||
}
|
|
||||||
|
|
||||||
const [textRight, setTextRight] = useState(0)
|
|
||||||
const arrowY = useAnimatedStyle(() => ({
|
|
||||||
transform: [
|
|
||||||
{
|
|
||||||
translateY: interpolate(
|
|
||||||
scrollY.value,
|
|
||||||
[0, SEPARATION_Y_1],
|
|
||||||
[
|
|
||||||
-CONTAINER_HEIGHT / 2 - StyleConstants.Font.Size.M / 2,
|
|
||||||
CONTAINER_HEIGHT / 2 - StyleConstants.Font.Size.S / 2
|
|
||||||
],
|
|
||||||
Extrapolate.CLAMP
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}))
|
|
||||||
const arrowTop = useAnimatedStyle(() => ({
|
|
||||||
marginTop:
|
|
||||||
scrollY.value < SEPARATION_Y_2
|
|
||||||
? withTiming(CONTAINER_HEIGHT)
|
|
||||||
: withTiming(0)
|
|
||||||
}))
|
|
||||||
|
|
||||||
const arrowStage = useSharedValue(0)
|
|
||||||
const onLayout = useCallback(
|
|
||||||
({ nativeEvent }) => {
|
|
||||||
if (nativeEvent.layout.x + nativeEvent.layout.width > textRight) {
|
|
||||||
setTextRight(nativeEvent.layout.x + nativeEvent.layout.width)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[textRight]
|
|
||||||
)
|
|
||||||
useAnimatedReaction(
|
|
||||||
() => {
|
|
||||||
if (isFetching) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch (arrowStage.value) {
|
|
||||||
case 0:
|
|
||||||
if (scrollY.value < SEPARATION_Y_1) {
|
|
||||||
arrowStage.value = 1
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
case 1:
|
|
||||||
if (scrollY.value < SEPARATION_Y_2) {
|
|
||||||
arrowStage.value = 2
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (scrollY.value > SEPARATION_Y_1) {
|
|
||||||
arrowStage.value = 0
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
case 2:
|
|
||||||
if (scrollY.value > SEPARATION_Y_2) {
|
|
||||||
arrowStage.value = 1
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data => {
|
|
||||||
if (data) {
|
|
||||||
runOnJS(haptics)('Light')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[isFetching]
|
|
||||||
)
|
|
||||||
const wrapperStartLatest = () => {
|
|
||||||
fetchingLatestIndex.current = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
useAnimatedReaction(
|
|
||||||
() => {
|
|
||||||
return fetchingType.value
|
|
||||||
},
|
|
||||||
data => {
|
|
||||||
fetchingType.value = 0
|
|
||||||
switch (data) {
|
|
||||||
case 1:
|
|
||||||
runOnJS(wrapperStartLatest)()
|
|
||||||
runOnJS(clearFirstPage)()
|
|
||||||
runOnJS(fetchPreviousPage)()
|
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
runOnJS(prepareRefetch)()
|
|
||||||
runOnJS(callRefetch)()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
const headerPadding = useAnimatedStyle(
|
|
||||||
() => ({
|
|
||||||
paddingTop:
|
|
||||||
fetchingLatestIndex.current !== 0 ||
|
|
||||||
(isFetching && !isLoading && !isFetchingNextPage)
|
|
||||||
? withTiming(StyleConstants.Spacing.M * 2.5)
|
|
||||||
: withTiming(0)
|
|
||||||
}),
|
|
||||||
[fetchingLatestIndex.current, isFetching, isFetchingNextPage, isLoading]
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Animated.View style={headerPadding}>
|
|
||||||
<View style={styles.base}>
|
|
||||||
{isFetching ? (
|
|
||||||
<View style={styles.container2}>
|
|
||||||
<Circle
|
|
||||||
size={StyleConstants.Font.Size.L}
|
|
||||||
color={colors.secondary}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<View style={styles.container1}>
|
|
||||||
<Text
|
|
||||||
style={[styles.explanation, { color: colors.primaryDefault }]}
|
|
||||||
onLayout={onLayout}
|
|
||||||
children={t('refresh.fetchPreviousPage')}
|
|
||||||
/>
|
|
||||||
<Animated.View
|
|
||||||
style={[
|
|
||||||
{
|
|
||||||
position: 'absolute',
|
|
||||||
left: textRight + StyleConstants.Spacing.S
|
|
||||||
},
|
|
||||||
arrowY,
|
|
||||||
arrowTop
|
|
||||||
]}
|
|
||||||
children={
|
|
||||||
<Icon
|
|
||||||
name='ArrowLeft'
|
|
||||||
size={StyleConstants.Font.Size.M}
|
|
||||||
color={colors.primaryDefault}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={styles.container2}>
|
|
||||||
<Text
|
|
||||||
style={[styles.explanation, { color: colors.primaryDefault }]}
|
|
||||||
onLayout={onLayout}
|
|
||||||
children={t('refresh.refetch')}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</Animated.View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
height: CONTAINER_HEIGHT * 2,
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
container1: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
height: CONTAINER_HEIGHT
|
|
||||||
},
|
|
||||||
container2: { height: CONTAINER_HEIGHT, justifyContent: 'center' },
|
|
||||||
explanation: {
|
|
||||||
fontSize: StyleConstants.Font.Size.S,
|
|
||||||
lineHeight: CONTAINER_HEIGHT
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineRefresh
|
|
|
@ -3,6 +3,7 @@ import Icon from '@components/Icon'
|
||||||
import { ParseEmojis } from '@components/Parse'
|
import { ParseEmojis } from '@components/Parse'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useMemo } from 'react'
|
import React, { useCallback, useMemo } from 'react'
|
||||||
|
@ -20,7 +21,7 @@ const TimelineActioned = React.memo(
|
||||||
const { t } = useTranslation('componentTimeline')
|
const { t } = useTranslation('componentTimeline')
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
const name = account.display_name || account.username
|
const name = account.display_name || account.username
|
||||||
const iconColor = colors.primaryDefault
|
const iconColor = colors.primaryDefault
|
||||||
|
|
||||||
|
@ -143,19 +144,23 @@ const TimelineActioned = React.memo(
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return <View style={styles.actioned} children={children} />
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: StyleConstants.Spacing.S,
|
||||||
|
paddingLeft: StyleConstants.Avatar.M - StyleConstants.Font.Size.S,
|
||||||
|
paddingRight: StyleConstants.Spacing.Global.PagePadding
|
||||||
|
}}
|
||||||
|
children={children}
|
||||||
|
/>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
() => true
|
() => true
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
actioned: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginBottom: StyleConstants.Spacing.S,
|
|
||||||
paddingLeft: StyleConstants.Avatar.M - StyleConstants.Font.Size.S,
|
|
||||||
paddingRight: StyleConstants.Spacing.Global.PagePadding
|
|
||||||
},
|
|
||||||
icon: {
|
icon: {
|
||||||
marginRight: StyleConstants.Spacing.S
|
marginRight: StyleConstants.Spacing.S
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import { displayMessage } from '@components/Message'
|
import { displayMessage } from '@components/Message'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||||
import {
|
import {
|
||||||
MutationVarsTimelineUpdateStatusProperty,
|
MutationVarsTimelineUpdateStatusProperty,
|
||||||
QueryKeyTimeline,
|
QueryKeyTimeline,
|
||||||
|
@ -11,7 +14,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useMemo } from 'react'
|
import React, { useCallback, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
import { Pressable, StyleSheet, View } from 'react-native'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -31,7 +34,7 @@ const TimelineActions: React.FC<Props> = ({
|
||||||
accts,
|
accts,
|
||||||
reblog
|
reblog
|
||||||
}) => {
|
}) => {
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
|
||||||
const { t } = useTranslation('componentTimeline')
|
const { t } = useTranslation('componentTimeline')
|
||||||
const { colors, theme } = useTheme()
|
const { colors, theme } = useTheme()
|
||||||
const iconColor = colors.secondary
|
const iconColor = colors.secondary
|
||||||
|
@ -183,7 +186,7 @@ const TimelineActions: React.FC<Props> = ({
|
||||||
size={StyleConstants.Font.Size.L}
|
size={StyleConstants.Font.Size.L}
|
||||||
/>
|
/>
|
||||||
{status.replies_count > 0 ? (
|
{status.replies_count > 0 ? (
|
||||||
<Text
|
<CustomText
|
||||||
style={{
|
style={{
|
||||||
color: colors.secondary,
|
color: colors.secondary,
|
||||||
fontSize: StyleConstants.Font.Size.M,
|
fontSize: StyleConstants.Font.Size.M,
|
||||||
|
@ -191,7 +194,7 @@ const TimelineActions: React.FC<Props> = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{status.replies_count}
|
{status.replies_count}
|
||||||
</Text>
|
</CustomText>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
@ -204,14 +207,14 @@ const TimelineActions: React.FC<Props> = ({
|
||||||
<Icon
|
<Icon
|
||||||
name='Repeat'
|
name='Repeat'
|
||||||
color={
|
color={
|
||||||
status.visibility === 'private' || status.visibility === 'direct'
|
status.visibility === 'direct'
|
||||||
? colors.disabled
|
? colors.disabled
|
||||||
: color(status.reblogged)
|
: color(status.reblogged)
|
||||||
}
|
}
|
||||||
size={StyleConstants.Font.Size.L}
|
size={StyleConstants.Font.Size.L}
|
||||||
/>
|
/>
|
||||||
{status.reblogs_count > 0 ? (
|
{status.reblogs_count > 0 ? (
|
||||||
<Text
|
<CustomText
|
||||||
style={{
|
style={{
|
||||||
color: color(status.reblogged),
|
color: color(status.reblogged),
|
||||||
fontSize: StyleConstants.Font.Size.M,
|
fontSize: StyleConstants.Font.Size.M,
|
||||||
|
@ -219,7 +222,7 @@ const TimelineActions: React.FC<Props> = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{status.reblogs_count}
|
{status.reblogs_count}
|
||||||
</Text>
|
</CustomText>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -234,7 +237,7 @@ const TimelineActions: React.FC<Props> = ({
|
||||||
size={StyleConstants.Font.Size.L}
|
size={StyleConstants.Font.Size.L}
|
||||||
/>
|
/>
|
||||||
{status.favourites_count > 0 ? (
|
{status.favourites_count > 0 ? (
|
||||||
<Text
|
<CustomText
|
||||||
style={{
|
style={{
|
||||||
color: color(status.favourited),
|
color: color(status.favourited),
|
||||||
fontSize: StyleConstants.Font.Size.M,
|
fontSize: StyleConstants.Font.Size.M,
|
||||||
|
@ -243,7 +246,7 @@ const TimelineActions: React.FC<Props> = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{status.favourites_count}
|
{status.favourites_count}
|
||||||
</Text>
|
</CustomText>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -267,7 +270,7 @@ const TimelineActions: React.FC<Props> = ({
|
||||||
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View style={styles.actions}>
|
<View style={{ flexDirection: 'row' }}>
|
||||||
<Pressable
|
<Pressable
|
||||||
{...(highlighted
|
{...(highlighted
|
||||||
? {
|
? {
|
||||||
|
@ -294,9 +297,7 @@ const TimelineActions: React.FC<Props> = ({
|
||||||
style={styles.action}
|
style={styles.action}
|
||||||
onPress={onPressReblog}
|
onPress={onPressReblog}
|
||||||
children={childrenReblog}
|
children={childrenReblog}
|
||||||
disabled={
|
disabled={status.visibility === 'direct'}
|
||||||
status.visibility === 'private' || status.visibility === 'direct'
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Pressable
|
<Pressable
|
||||||
|
@ -332,9 +333,6 @@ const TimelineActions: React.FC<Props> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
actions: {
|
|
||||||
flexDirection: 'row'
|
|
||||||
},
|
|
||||||
action: {
|
action: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
|
|
@ -1,108 +0,0 @@
|
||||||
import analytics from '@components/analytics'
|
|
||||||
import { useNavigation } from '@react-navigation/native'
|
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
|
||||||
import React from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { StyleSheet, Text, View } from 'react-native'
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
status: Mastodon.Status
|
|
||||||
highlighted: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const TimelineActionsUsers = React.memo(
|
|
||||||
({ status, highlighted }: Props) => {
|
|
||||||
if (!highlighted) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const { t } = useTranslation('componentTimeline')
|
|
||||||
const { colors } = useTheme()
|
|
||||||
const navigation =
|
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={styles.base}>
|
|
||||||
{status.reblogs_count > 0 ? (
|
|
||||||
<Text
|
|
||||||
accessibilityLabel={t(
|
|
||||||
'shared.actionsUsers.reblogged_by.accessibilityLabel',
|
|
||||||
{
|
|
||||||
count: status.reblogs_count
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
accessibilityHint={t(
|
|
||||||
'shared.actionsUsers.reblogged_by.accessibilityHint'
|
|
||||||
)}
|
|
||||||
accessibilityRole='button'
|
|
||||||
style={[styles.text, { color: colors.blue }]}
|
|
||||||
onPress={() => {
|
|
||||||
analytics('timeline_shared_actionsusers_press_boosted', {
|
|
||||||
count: status.reblogs_count
|
|
||||||
})
|
|
||||||
navigation.push('Tab-Shared-Users', {
|
|
||||||
reference: 'statuses',
|
|
||||||
id: status.id,
|
|
||||||
type: 'reblogged_by',
|
|
||||||
count: status.reblogs_count
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('shared.actionsUsers.reblogged_by.text', {
|
|
||||||
count: status.reblogs_count
|
|
||||||
})}
|
|
||||||
</Text>
|
|
||||||
) : null}
|
|
||||||
{status.favourites_count > 0 ? (
|
|
||||||
<Text
|
|
||||||
accessibilityLabel={t(
|
|
||||||
'shared.actionsUsers.favourited_by.accessibilityLabel',
|
|
||||||
{
|
|
||||||
count: status.reblogs_count
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
accessibilityHint={t(
|
|
||||||
'shared.actionsUsers.favourited_by.accessibilityHint'
|
|
||||||
)}
|
|
||||||
accessibilityRole='button'
|
|
||||||
style={[styles.text, { color: colors.blue }]}
|
|
||||||
onPress={() => {
|
|
||||||
analytics('timeline_shared_actionsusers_press_boosted', {
|
|
||||||
count: status.favourites_count
|
|
||||||
})
|
|
||||||
navigation.push('Tab-Shared-Users', {
|
|
||||||
reference: 'statuses',
|
|
||||||
id: status.id,
|
|
||||||
type: 'favourited_by',
|
|
||||||
count: status.favourites_count
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('shared.actionsUsers.favourited_by.text', {
|
|
||||||
count: status.favourites_count
|
|
||||||
})}
|
|
||||||
</Text>
|
|
||||||
) : null}
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
(prev, next) =>
|
|
||||||
prev.status.reblogs_count === next.status.reblogs_count &&
|
|
||||||
prev.status.favourites_count === next.status.favourites_count
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flexDirection: 'row'
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
...StyleConstants.FontStyle.M,
|
|
||||||
padding: StyleConstants.Spacing.S,
|
|
||||||
paddingLeft: 0,
|
|
||||||
marginRight: StyleConstants.Spacing.S
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineActionsUsers
|
|
|
@ -6,13 +6,13 @@ import AttachmentImage from '@components/Timeline/Shared/Attachment/Image'
|
||||||
import AttachmentUnsupported from '@components/Timeline/Shared/Attachment/Unsupported'
|
import AttachmentUnsupported from '@components/Timeline/Shared/Attachment/Unsupported'
|
||||||
import AttachmentVideo from '@components/Timeline/Shared/Attachment/Video'
|
import AttachmentVideo from '@components/Timeline/Shared/Attachment/Video'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
import { RootStackParamList } from '@utils/navigation/navigators'
|
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import layoutAnimation from '@utils/styles/layoutAnimation'
|
import layoutAnimation from '@utils/styles/layoutAnimation'
|
||||||
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
import React, { useRef, useState } from 'react'
|
||||||
import { useEffect } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
status: Pick<Mastodon.Status, 'media_attachments' | 'sensitive'>
|
status: Pick<Mastodon.Status, 'media_attachments' | 'sensitive'>
|
||||||
|
@ -23,24 +23,13 @@ const TimelineAttachment = React.memo(
|
||||||
const { t } = useTranslation('componentTimeline')
|
const { t } = useTranslation('componentTimeline')
|
||||||
|
|
||||||
const [sensitiveShown, setSensitiveShown] = useState(status.sensitive)
|
const [sensitiveShown, setSensitiveShown] = useState(status.sensitive)
|
||||||
const onPressBlurView = useCallback(() => {
|
|
||||||
analytics('timeline_shared_attachment_blurview_press_show')
|
|
||||||
layoutAnimation()
|
|
||||||
setSensitiveShown(false)
|
|
||||||
haptics('Light')
|
|
||||||
}, [])
|
|
||||||
const onPressShow = useCallback(() => {
|
|
||||||
analytics('timeline_shared_attachment_blurview_press_hide')
|
|
||||||
setSensitiveShown(true)
|
|
||||||
haptics('Light')
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const imageUrls = useRef<
|
const imageUrls = useRef<
|
||||||
RootStackParamList['Screen-ImagesViewer']['imageUrls']
|
RootStackParamList['Screen-ImagesViewer']['imageUrls']
|
||||||
>([])
|
>([])
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
|
||||||
useEffect(() => {
|
const navigateToImagesViewer = (id: string) => {
|
||||||
status.media_attachments.forEach((attachment, index) => {
|
status.media_attachments.forEach(attachment => {
|
||||||
switch (attachment.type) {
|
switch (attachment.type) {
|
||||||
case 'image':
|
case 'image':
|
||||||
imageUrls.current.push({
|
imageUrls.current.push({
|
||||||
|
@ -54,117 +43,136 @@ const TimelineAttachment = React.memo(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [])
|
|
||||||
const navigateToImagesViewer = (id: string) =>
|
|
||||||
navigation.navigate('Screen-ImagesViewer', {
|
navigation.navigate('Screen-ImagesViewer', {
|
||||||
imageUrls: imageUrls.current,
|
imageUrls: imageUrls.current,
|
||||||
id
|
id
|
||||||
})
|
})
|
||||||
const attachments = useMemo(
|
}
|
||||||
() =>
|
|
||||||
status.media_attachments.map((attachment, index) => {
|
return (
|
||||||
switch (attachment.type) {
|
<View>
|
||||||
case 'image':
|
<View
|
||||||
return (
|
style={{
|
||||||
<AttachmentImage
|
marginTop: StyleConstants.Spacing.S,
|
||||||
key={index}
|
flex: 1,
|
||||||
total={status.media_attachments.length}
|
flexDirection: 'row',
|
||||||
index={index}
|
flexWrap: 'wrap',
|
||||||
sensitiveShown={sensitiveShown}
|
justifyContent: 'center',
|
||||||
image={attachment}
|
alignContent: 'stretch'
|
||||||
navigateToImagesViewer={navigateToImagesViewer}
|
}}
|
||||||
/>
|
>
|
||||||
)
|
{status.media_attachments.map((attachment, index) => {
|
||||||
case 'video':
|
switch (attachment.type) {
|
||||||
return (
|
case 'image':
|
||||||
<AttachmentVideo
|
|
||||||
key={index}
|
|
||||||
total={status.media_attachments.length}
|
|
||||||
index={index}
|
|
||||||
sensitiveShown={sensitiveShown}
|
|
||||||
video={attachment}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
case 'gifv':
|
|
||||||
return (
|
|
||||||
<AttachmentVideo
|
|
||||||
key={index}
|
|
||||||
total={status.media_attachments.length}
|
|
||||||
index={index}
|
|
||||||
sensitiveShown={sensitiveShown}
|
|
||||||
video={attachment}
|
|
||||||
gifv
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
case 'audio':
|
|
||||||
return (
|
|
||||||
<AttachmentAudio
|
|
||||||
key={index}
|
|
||||||
total={status.media_attachments.length}
|
|
||||||
index={index}
|
|
||||||
sensitiveShown={sensitiveShown}
|
|
||||||
audio={attachment}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
if (
|
|
||||||
attachment.preview_url?.endsWith('.jpg') ||
|
|
||||||
attachment.preview_url?.endsWith('.jpeg') ||
|
|
||||||
attachment.preview_url?.endsWith('.png') ||
|
|
||||||
attachment.preview_url?.endsWith('.gif') ||
|
|
||||||
attachment.remote_url?.endsWith('.jpg') ||
|
|
||||||
attachment.remote_url?.endsWith('.jpeg') ||
|
|
||||||
attachment.remote_url?.endsWith('.png') ||
|
|
||||||
attachment.remote_url?.endsWith('.gif')
|
|
||||||
) {
|
|
||||||
imageUrls.current.push({
|
|
||||||
id: attachment.id,
|
|
||||||
preview_url: attachment.preview_url,
|
|
||||||
url: attachment.url,
|
|
||||||
remote_url: attachment.remote_url,
|
|
||||||
blurhash: attachment.blurhash,
|
|
||||||
width: attachment.meta?.original?.width,
|
|
||||||
height: attachment.meta?.original?.height
|
|
||||||
})
|
|
||||||
return (
|
return (
|
||||||
<AttachmentImage
|
<AttachmentImage
|
||||||
key={index}
|
key={index}
|
||||||
total={status.media_attachments.length}
|
total={status.media_attachments.length}
|
||||||
index={index}
|
index={index}
|
||||||
sensitiveShown={sensitiveShown}
|
sensitiveShown={sensitiveShown}
|
||||||
// @ts-ignore
|
|
||||||
image={attachment}
|
image={attachment}
|
||||||
navigateToImagesViewer={navigateToImagesViewer}
|
navigateToImagesViewer={navigateToImagesViewer}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else {
|
case 'video':
|
||||||
return (
|
return (
|
||||||
<AttachmentUnsupported
|
<AttachmentVideo
|
||||||
key={index}
|
key={index}
|
||||||
total={status.media_attachments.length}
|
total={status.media_attachments.length}
|
||||||
index={index}
|
index={index}
|
||||||
sensitiveShown={sensitiveShown}
|
sensitiveShown={sensitiveShown}
|
||||||
attachment={attachment}
|
video={attachment}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
case 'gifv':
|
||||||
}
|
return (
|
||||||
}),
|
<AttachmentVideo
|
||||||
[sensitiveShown]
|
key={index}
|
||||||
)
|
total={status.media_attachments.length}
|
||||||
|
index={index}
|
||||||
return (
|
sensitiveShown={sensitiveShown}
|
||||||
<View>
|
video={attachment}
|
||||||
<View style={styles.container} children={attachments} />
|
gifv
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
case 'audio':
|
||||||
|
return (
|
||||||
|
<AttachmentAudio
|
||||||
|
key={index}
|
||||||
|
total={status.media_attachments.length}
|
||||||
|
index={index}
|
||||||
|
sensitiveShown={sensitiveShown}
|
||||||
|
audio={attachment}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
if (
|
||||||
|
attachment.preview_url?.endsWith('.jpg') ||
|
||||||
|
attachment.preview_url?.endsWith('.jpeg') ||
|
||||||
|
attachment.preview_url?.endsWith('.png') ||
|
||||||
|
attachment.preview_url?.endsWith('.gif') ||
|
||||||
|
attachment.remote_url?.endsWith('.jpg') ||
|
||||||
|
attachment.remote_url?.endsWith('.jpeg') ||
|
||||||
|
attachment.remote_url?.endsWith('.png') ||
|
||||||
|
attachment.remote_url?.endsWith('.gif')
|
||||||
|
) {
|
||||||
|
imageUrls.current.push({
|
||||||
|
id: attachment.id,
|
||||||
|
preview_url: attachment.preview_url,
|
||||||
|
url: attachment.url,
|
||||||
|
remote_url: attachment.remote_url,
|
||||||
|
blurhash: attachment.blurhash,
|
||||||
|
width: attachment.meta?.original?.width,
|
||||||
|
height: attachment.meta?.original?.height
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<AttachmentImage
|
||||||
|
key={index}
|
||||||
|
total={status.media_attachments.length}
|
||||||
|
index={index}
|
||||||
|
sensitiveShown={sensitiveShown}
|
||||||
|
// @ts-ignore
|
||||||
|
image={attachment}
|
||||||
|
navigateToImagesViewer={navigateToImagesViewer}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<AttachmentUnsupported
|
||||||
|
key={index}
|
||||||
|
total={status.media_attachments.length}
|
||||||
|
index={index}
|
||||||
|
sensitiveShown={sensitiveShown}
|
||||||
|
attachment={attachment}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
|
||||||
{status.sensitive &&
|
{status.sensitive &&
|
||||||
(sensitiveShown ? (
|
(sensitiveShown ? (
|
||||||
<Pressable style={styles.sensitiveBlur}>
|
<Pressable
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
type='text'
|
type='text'
|
||||||
content={t('shared.attachment.sensitive.button')}
|
content={t('shared.attachment.sensitive.button')}
|
||||||
overlay
|
overlay
|
||||||
onPress={onPressBlurView}
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_attachment_blurview_press_show')
|
||||||
|
layoutAnimation()
|
||||||
|
setSensitiveShown(false)
|
||||||
|
haptics('Light')
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
) : (
|
) : (
|
||||||
|
@ -173,7 +181,11 @@ const TimelineAttachment = React.memo(
|
||||||
content='EyeOff'
|
content='EyeOff'
|
||||||
round
|
round
|
||||||
overlay
|
overlay
|
||||||
onPress={onPressShow}
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_attachment_blurview_press_hide')
|
||||||
|
setSensitiveShown(true)
|
||||||
|
haptics('Light')
|
||||||
|
}}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: StyleConstants.Spacing.S * 2,
|
top: StyleConstants.Spacing.S * 2,
|
||||||
|
@ -184,33 +196,28 @@ const TimelineAttachment = React.memo(
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
() => true
|
(prev, next) => {
|
||||||
|
let isEqual = true
|
||||||
|
|
||||||
|
if (
|
||||||
|
prev.status.media_attachments.length !==
|
||||||
|
next.status.media_attachments.length
|
||||||
|
) {
|
||||||
|
isEqual = false
|
||||||
|
return isEqual
|
||||||
|
}
|
||||||
|
|
||||||
|
prev.status.media_attachments.forEach((attachment, index) => {
|
||||||
|
if (
|
||||||
|
attachment.preview_url !==
|
||||||
|
next.status.media_attachments[index].preview_url
|
||||||
|
) {
|
||||||
|
isEqual = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return isEqual
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
marginTop: StyleConstants.Spacing.S,
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignContent: 'stretch'
|
|
||||||
},
|
|
||||||
sensitiveBlur: {
|
|
||||||
position: 'absolute',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
sensitiveBlurButton: {
|
|
||||||
padding: StyleConstants.Spacing.S,
|
|
||||||
borderRadius: 6
|
|
||||||
},
|
|
||||||
sensitiveText: {
|
|
||||||
...StyleConstants.FontStyle.M
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineAttachment
|
export default TimelineAttachment
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import GracefullyImage from '@components/GracefullyImage'
|
import GracefullyImage from '@components/GracefullyImage'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import React, { useCallback } from 'react'
|
import React from 'react'
|
||||||
import { StyleSheet, View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import attachmentAspectRatio from './aspectRatio'
|
import attachmentAspectRatio from './aspectRatio'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -13,44 +13,43 @@ export interface Props {
|
||||||
navigateToImagesViewer: (imageIndex: string) => void
|
navigateToImagesViewer: (imageIndex: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const AttachmentImage = React.memo(
|
const AttachmentImage = ({
|
||||||
({ total, index, sensitiveShown, image, navigateToImagesViewer }: Props) => {
|
total,
|
||||||
const onPress = useCallback(() => {
|
index,
|
||||||
analytics('timeline_shared_attachment_image_press', { id: image.id })
|
sensitiveShown,
|
||||||
navigateToImagesViewer(image.id)
|
image,
|
||||||
}, [])
|
navigateToImagesViewer
|
||||||
|
}: Props) => {
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View
|
||||||
<GracefullyImage
|
style={{
|
||||||
accessibilityLabel={image.description}
|
flex: 1,
|
||||||
hidden={sensitiveShown}
|
flexBasis: '50%',
|
||||||
uri={{ original: image.preview_url, remote: image.remote_url }}
|
padding: StyleConstants.Spacing.XS / 2
|
||||||
blurhash={image.blurhash}
|
}}
|
||||||
onPress={onPress}
|
>
|
||||||
style={{
|
<GracefullyImage
|
||||||
aspectRatio:
|
accessibilityLabel={image.description}
|
||||||
total > 1 ||
|
hidden={sensitiveShown}
|
||||||
!image.meta?.original?.width ||
|
uri={{ original: image.preview_url, remote: image.remote_url }}
|
||||||
!image.meta?.original?.height
|
blurhash={image.blurhash}
|
||||||
? attachmentAspectRatio({ total, index })
|
onPress={() => {
|
||||||
: image.meta.original.height / image.meta.original.width > 1
|
analytics('timeline_shared_attachment_image_press', { id: image.id })
|
||||||
? 1
|
navigateToImagesViewer(image.id)
|
||||||
: image.meta.original.width / image.meta.original.height
|
}}
|
||||||
}}
|
style={{
|
||||||
/>
|
aspectRatio:
|
||||||
</View>
|
total > 1 ||
|
||||||
)
|
!image.meta?.original?.width ||
|
||||||
},
|
!image.meta?.original?.height
|
||||||
(prev, next) => prev.sensitiveShown === next.sensitiveShown
|
? attachmentAspectRatio({ total, index })
|
||||||
)
|
: image.meta.original.height / image.meta.original.width > 1
|
||||||
|
? 1
|
||||||
const styles = StyleSheet.create({
|
: image.meta.original.width / image.meta.original.height
|
||||||
base: {
|
}}
|
||||||
flex: 1,
|
/>
|
||||||
flexBasis: '50%',
|
</View>
|
||||||
padding: StyleConstants.Spacing.XS / 2
|
)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
export default AttachmentImage
|
export default AttachmentImage
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import Button from '@components/Button'
|
import Button from '@components/Button'
|
||||||
import openLink from '@components/openLink'
|
import openLink from '@components/openLink'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyleSheet, Text, View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import { Blurhash } from 'react-native-blurhash'
|
import { Blurhash } from 'react-native-blurhash'
|
||||||
import attachmentAspectRatio from './aspectRatio'
|
import attachmentAspectRatio from './aspectRatio'
|
||||||
|
|
||||||
|
@ -27,10 +28,14 @@ const AttachmentUnsupported: React.FC<Props> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={{
|
||||||
styles.base,
|
flex: 1,
|
||||||
{ aspectRatio: attachmentAspectRatio({ total, index }) }
|
flexBasis: '50%',
|
||||||
]}
|
padding: StyleConstants.Spacing.XS / 2,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
aspectRatio: attachmentAspectRatio({ total, index })
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{attachment.blurhash ? (
|
{attachment.blurhash ? (
|
||||||
<Blurhash
|
<Blurhash
|
||||||
|
@ -44,18 +49,18 @@ const AttachmentUnsupported: React.FC<Props> = ({
|
||||||
) : null}
|
) : null}
|
||||||
{!sensitiveShown ? (
|
{!sensitiveShown ? (
|
||||||
<>
|
<>
|
||||||
<Text
|
<CustomText
|
||||||
style={[
|
fontStyle='S'
|
||||||
styles.text,
|
style={{
|
||||||
{
|
textAlign: 'center',
|
||||||
color: attachment.blurhash
|
marginBottom: StyleConstants.Spacing.S,
|
||||||
? colors.backgroundDefault
|
color: attachment.blurhash
|
||||||
: colors.primaryDefault
|
? colors.backgroundDefault
|
||||||
}
|
: colors.primaryDefault
|
||||||
]}
|
}}
|
||||||
>
|
>
|
||||||
{t('shared.attachment.unsupported.text')}
|
{t('shared.attachment.unsupported.text')}
|
||||||
</Text>
|
</CustomText>
|
||||||
{attachment.remote_url ? (
|
{attachment.remote_url ? (
|
||||||
<Button
|
<Button
|
||||||
type='text'
|
type='text'
|
||||||
|
@ -74,19 +79,4 @@ const AttachmentUnsupported: React.FC<Props> = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flex: 1,
|
|
||||||
flexBasis: '50%',
|
|
||||||
padding: StyleConstants.Spacing.XS / 2,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
...StyleConstants.FontStyle.S,
|
|
||||||
textAlign: 'center',
|
|
||||||
marginBottom: StyleConstants.Spacing.S
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default AttachmentUnsupported
|
export default AttachmentUnsupported
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Button from '@components/Button'
|
import Button from '@components/Button'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { Video } from 'expo-av'
|
import { ResizeMode, Video, VideoFullscreenUpdate } from 'expo-av'
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import {
|
import {
|
||||||
AppState,
|
AppState,
|
||||||
|
@ -110,15 +110,14 @@ const AttachmentVideo: React.FC<Props> = ({
|
||||||
source: { uri: video.url }
|
source: { uri: video.url }
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
resizeMode: 'cover',
|
resizeMode: ResizeMode.COVER,
|
||||||
posterSource: { uri: video.preview_url },
|
posterSource: { uri: video.preview_url },
|
||||||
posterStyle: { resizeMode: 'cover' }
|
posterStyle: { resizeMode: ResizeMode.COVER }
|
||||||
})}
|
})}
|
||||||
useNativeControls={false}
|
useNativeControls={false}
|
||||||
onFullscreenUpdate={async event => {
|
onFullscreenUpdate={async event => {
|
||||||
if (
|
if (
|
||||||
event.fullscreenUpdate ===
|
event.fullscreenUpdate === VideoFullscreenUpdate.PLAYER_DID_DISMISS
|
||||||
Video.FULLSCREEN_UPDATE_PLAYER_DID_DISMISS
|
|
||||||
) {
|
) {
|
||||||
if (gifv) {
|
if (gifv) {
|
||||||
await videoPlayer.current?.pauseAsync()
|
await videoPlayer.current?.pauseAsync()
|
||||||
|
|
|
@ -2,6 +2,7 @@ import analytics from '@components/analytics'
|
||||||
import GracefullyImage from '@components/GracefullyImage'
|
import GracefullyImage from '@components/GracefullyImage'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
|
@ -17,7 +18,7 @@ const TimelineAvatar = React.memo(
|
||||||
({ queryKey, account, highlighted }: Props) => {
|
({ queryKey, account, highlighted }: Props) => {
|
||||||
const { t } = useTranslation('componentTimeline')
|
const { t } = useTranslation('componentTimeline')
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
// Need to fix go back root
|
// Need to fix go back root
|
||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
analytics('timeline_shared_avatar_press', {
|
analytics('timeline_shared_avatar_press', {
|
||||||
|
@ -49,8 +50,7 @@ const TimelineAvatar = React.memo(
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
() => true
|
|
||||||
)
|
)
|
||||||
|
|
||||||
export default TimelineAvatar
|
export default TimelineAvatar
|
||||||
|
|
|
@ -1,106 +1,88 @@
|
||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import GracefullyImage from '@components/GracefullyImage'
|
import GracefullyImage from '@components/GracefullyImage'
|
||||||
import openLink from '@components/openLink'
|
import openLink from '@components/openLink'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
import { Pressable, StyleSheet, View } from 'react-native'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
card: Mastodon.Card
|
card: Pick<
|
||||||
|
Mastodon.Card,
|
||||||
|
'url' | 'image' | 'blurhash' | 'title' | 'description'
|
||||||
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimelineCard = React.memo(
|
const TimelineCard = React.memo(({ card }: Props) => {
|
||||||
({ card }: Props) => {
|
const { colors } = useTheme()
|
||||||
const { colors } = useTheme()
|
const navigation = useNavigation()
|
||||||
const navigation = useNavigation()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
accessible
|
accessible
|
||||||
accessibilityRole='link'
|
accessibilityRole='link'
|
||||||
style={[styles.card, { borderColor: colors.border }]}
|
style={{
|
||||||
onPress={async () => {
|
flex: 1,
|
||||||
analytics('timeline_shared_card_press')
|
flexDirection: 'row',
|
||||||
await openLink(card.url, navigation)
|
height: StyleConstants.Font.LineHeight.M * 5,
|
||||||
}}
|
marginTop: StyleConstants.Spacing.M,
|
||||||
testID='base'
|
borderWidth: StyleSheet.hairlineWidth,
|
||||||
>
|
borderRadius: 6,
|
||||||
{card.image ? (
|
overflow: 'hidden',
|
||||||
<GracefullyImage
|
borderColor: colors.border
|
||||||
uri={{ original: card.image }}
|
}}
|
||||||
blurhash={card.blurhash}
|
onPress={async () => {
|
||||||
style={styles.left}
|
analytics('timeline_shared_card_press')
|
||||||
imageStyle={styles.image}
|
await openLink(card.url, navigation)
|
||||||
/>
|
}}
|
||||||
) : null}
|
testID='base'
|
||||||
<View style={styles.right}>
|
>
|
||||||
<Text
|
{card.image ? (
|
||||||
numberOfLines={2}
|
<GracefullyImage
|
||||||
style={[styles.rightTitle, { color: colors.primaryDefault }]}
|
uri={{ original: card.image }}
|
||||||
testID='title'
|
blurhash={card.blurhash}
|
||||||
>
|
style={{ flexBasis: StyleConstants.Font.LineHeight.M * 5 }}
|
||||||
{card.title}
|
imageStyle={{ borderTopLeftRadius: 6, borderBottomLeftRadius: 6 }}
|
||||||
</Text>
|
/>
|
||||||
{card.description ? (
|
) : null}
|
||||||
<Text
|
<View style={{ flex: 1, padding: StyleConstants.Spacing.S }}>
|
||||||
numberOfLines={1}
|
<CustomText
|
||||||
style={[
|
fontStyle='S'
|
||||||
styles.rightDescription,
|
numberOfLines={2}
|
||||||
{ color: colors.primaryDefault }
|
style={{
|
||||||
]}
|
marginBottom: StyleConstants.Spacing.XS,
|
||||||
testID='description'
|
color: colors.primaryDefault
|
||||||
>
|
}}
|
||||||
{card.description}
|
fontWeight='Bold'
|
||||||
</Text>
|
testID='title'
|
||||||
) : null}
|
>
|
||||||
<Text
|
{card.title}
|
||||||
|
</CustomText>
|
||||||
|
{card.description ? (
|
||||||
|
<CustomText
|
||||||
|
fontStyle='S'
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
style={[styles.rightLink, { color: colors.secondary }]}
|
style={{
|
||||||
|
marginBottom: StyleConstants.Spacing.XS,
|
||||||
|
color: colors.primaryDefault
|
||||||
|
}}
|
||||||
|
testID='description'
|
||||||
>
|
>
|
||||||
{card.url}
|
{card.description}
|
||||||
</Text>
|
</CustomText>
|
||||||
</View>
|
) : null}
|
||||||
</Pressable>
|
<CustomText
|
||||||
)
|
fontStyle='S'
|
||||||
},
|
numberOfLines={1}
|
||||||
() => true
|
style={{ color: colors.secondary }}
|
||||||
)
|
>
|
||||||
|
{card.url}
|
||||||
const styles = StyleSheet.create({
|
</CustomText>
|
||||||
card: {
|
</View>
|
||||||
flex: 1,
|
</Pressable>
|
||||||
flexDirection: 'row',
|
)
|
||||||
height: StyleConstants.Font.LineHeight.M * 5,
|
|
||||||
marginTop: StyleConstants.Spacing.M,
|
|
||||||
borderWidth: StyleSheet.hairlineWidth,
|
|
||||||
borderRadius: 6,
|
|
||||||
overflow: 'hidden'
|
|
||||||
},
|
|
||||||
left: {
|
|
||||||
flexBasis: StyleConstants.Font.LineHeight.M * 5
|
|
||||||
},
|
|
||||||
image: {
|
|
||||||
borderTopLeftRadius: 6,
|
|
||||||
borderBottomLeftRadius: 6
|
|
||||||
},
|
|
||||||
right: {
|
|
||||||
flex: 1,
|
|
||||||
padding: StyleConstants.Spacing.S
|
|
||||||
},
|
|
||||||
rightTitle: {
|
|
||||||
...StyleConstants.FontStyle.S,
|
|
||||||
marginBottom: StyleConstants.Spacing.XS,
|
|
||||||
fontWeight: StyleConstants.Font.Weight.Bold
|
|
||||||
},
|
|
||||||
rightDescription: {
|
|
||||||
...StyleConstants.FontStyle.S,
|
|
||||||
marginBottom: StyleConstants.Spacing.XS
|
|
||||||
},
|
|
||||||
rightLink: {
|
|
||||||
...StyleConstants.FontStyle.S
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default TimelineCard
|
export default TimelineCard
|
||||||
|
|
|
@ -5,7 +5,10 @@ import { useTranslation } from 'react-i18next'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
status: Mastodon.Status
|
status: Pick<Mastodon.Status, 'content' | 'spoiler_text' | 'emojis'> & {
|
||||||
|
mentions?: Mastodon.Status['mentions']
|
||||||
|
tags?: Mastodon.Status['tags']
|
||||||
|
}
|
||||||
numberOfLines?: number
|
numberOfLines?: number
|
||||||
highlighted?: boolean
|
highlighted?: boolean
|
||||||
disableDetails?: boolean
|
disableDetails?: boolean
|
||||||
|
@ -69,7 +72,9 @@ const TimelineContent = React.memo(
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
() => true
|
(prev, next) =>
|
||||||
|
prev.status.content === next.status.content &&
|
||||||
|
prev.status.spoiler_text === next.status.spoiler_text
|
||||||
)
|
)
|
||||||
|
|
||||||
export default TimelineContent
|
export default TimelineContent
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
import analytics from '@components/analytics'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||||
|
import { useStatusHistory } from '@utils/queryHooks/statusesHistory'
|
||||||
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { StyleSheet, View } from 'react-native'
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
status: Pick<
|
||||||
|
Mastodon.Status,
|
||||||
|
'id' | 'edited_at' | 'reblogs_count' | 'favourites_count'
|
||||||
|
>
|
||||||
|
highlighted: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const TimelineFeedback = React.memo(
|
||||||
|
({ status, highlighted }: Props) => {
|
||||||
|
if (!highlighted) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = useTranslation('componentTimeline')
|
||||||
|
const { colors } = useTheme()
|
||||||
|
const navigation =
|
||||||
|
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||||
|
|
||||||
|
const { data } = useStatusHistory({
|
||||||
|
id: status.id,
|
||||||
|
options: { enabled: status.edited_at !== undefined }
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||||
|
<View style={{ flexDirection: 'row' }}>
|
||||||
|
{status.reblogs_count > 0 ? (
|
||||||
|
<CustomText
|
||||||
|
accessibilityLabel={t(
|
||||||
|
'shared.actionsUsers.reblogged_by.accessibilityLabel',
|
||||||
|
{
|
||||||
|
count: status.reblogs_count
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
accessibilityHint={t(
|
||||||
|
'shared.actionsUsers.reblogged_by.accessibilityHint'
|
||||||
|
)}
|
||||||
|
accessibilityRole='button'
|
||||||
|
style={[styles.text, { color: colors.blue }]}
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_feedback_press_reblog', {
|
||||||
|
count: status.reblogs_count
|
||||||
|
})
|
||||||
|
navigation.push('Tab-Shared-Users', {
|
||||||
|
reference: 'statuses',
|
||||||
|
id: status.id,
|
||||||
|
type: 'reblogged_by',
|
||||||
|
count: status.reblogs_count
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('shared.actionsUsers.reblogged_by.text', {
|
||||||
|
count: status.reblogs_count
|
||||||
|
})}
|
||||||
|
</CustomText>
|
||||||
|
) : null}
|
||||||
|
{status.favourites_count > 0 ? (
|
||||||
|
<CustomText
|
||||||
|
accessibilityLabel={t(
|
||||||
|
'shared.actionsUsers.favourited_by.accessibilityLabel',
|
||||||
|
{
|
||||||
|
count: status.reblogs_count
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
accessibilityHint={t(
|
||||||
|
'shared.actionsUsers.favourited_by.accessibilityHint'
|
||||||
|
)}
|
||||||
|
accessibilityRole='button'
|
||||||
|
style={[styles.text, { color: colors.blue }]}
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_feedback_press_favourite', {
|
||||||
|
count: status.favourites_count
|
||||||
|
})
|
||||||
|
navigation.push('Tab-Shared-Users', {
|
||||||
|
reference: 'statuses',
|
||||||
|
id: status.id,
|
||||||
|
type: 'favourited_by',
|
||||||
|
count: status.favourites_count
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('shared.actionsUsers.favourited_by.text', {
|
||||||
|
count: status.favourites_count
|
||||||
|
})}
|
||||||
|
</CustomText>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
{data && data.length > 1 ? (
|
||||||
|
<CustomText
|
||||||
|
accessibilityLabel={t(
|
||||||
|
'shared.actionsUsers.history.accessibilityLabel',
|
||||||
|
{
|
||||||
|
count: data.length - 1
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
accessibilityHint={t(
|
||||||
|
'shared.actionsUsers.history.accessibilityHint'
|
||||||
|
)}
|
||||||
|
accessibilityRole='button'
|
||||||
|
style={[styles.text, { marginRight: 0, color: colors.blue }]}
|
||||||
|
onPress={() => {
|
||||||
|
analytics('timeline_shared_feedback_press_history', {
|
||||||
|
count: data.length - 1
|
||||||
|
})
|
||||||
|
navigation.push('Tab-Shared-History', { id: status.id })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('shared.actionsUsers.history.text', {
|
||||||
|
count: data.length - 1
|
||||||
|
})}
|
||||||
|
</CustomText>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
(prev, next) =>
|
||||||
|
prev.status.edited_at === next.status.edited_at &&
|
||||||
|
prev.status.reblogs_count === next.status.reblogs_count &&
|
||||||
|
prev.status.favourites_count === next.status.favourites_count
|
||||||
|
)
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
text: {
|
||||||
|
...StyleConstants.FontStyle.M,
|
||||||
|
padding: StyleConstants.Spacing.S,
|
||||||
|
paddingLeft: 0,
|
||||||
|
marginRight: StyleConstants.Spacing.S
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default TimelineFeedback
|
|
@ -1,3 +1,4 @@
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { store } from '@root/store'
|
import { store } from '@root/store'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { getInstance, getInstanceAccount } from '@utils/slices/instancesSlice'
|
import { getInstance, getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
|
@ -6,7 +7,7 @@ import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import htmlparser2 from 'htmlparser2-without-node-native'
|
import htmlparser2 from 'htmlparser2-without-node-native'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Text, View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
|
|
||||||
const TimelineFiltered = React.memo(
|
const TimelineFiltered = React.memo(
|
||||||
() => {
|
() => {
|
||||||
|
@ -15,9 +16,9 @@ const TimelineFiltered = React.memo(
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{ backgroundColor: colors.backgroundDefault }}>
|
<View style={{ backgroundColor: colors.backgroundDefault }}>
|
||||||
<Text
|
<CustomText
|
||||||
|
fontStyle='S'
|
||||||
style={{
|
style={{
|
||||||
...StyleConstants.FontStyle.S,
|
|
||||||
color: colors.secondary,
|
color: colors.secondary,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
paddingVertical: StyleConstants.Spacing.S,
|
paddingVertical: StyleConstants.Spacing.S,
|
||||||
|
@ -25,7 +26,7 @@ const TimelineFiltered = React.memo(
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('shared.filtered')}
|
{t('shared.filtered')}
|
||||||
</Text>
|
</CustomText>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Text } from 'react-native'
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
queryKey?: QueryKeyTimeline
|
queryKey?: QueryKeyTimeline
|
||||||
|
@ -22,15 +22,15 @@ const TimelineFullConversation = React.memo(
|
||||||
status.mentions.filter(
|
status.mentions.filter(
|
||||||
mention => mention.id !== status.in_reply_to_account_id
|
mention => mention.id !== status.in_reply_to_account_id
|
||||||
).length) ? (
|
).length) ? (
|
||||||
<Text
|
<CustomText
|
||||||
|
fontStyle='S'
|
||||||
style={{
|
style={{
|
||||||
...StyleConstants.FontStyle.S,
|
|
||||||
color: colors.blue,
|
color: colors.blue,
|
||||||
marginTop: StyleConstants.Spacing.S
|
marginTop: StyleConstants.Spacing.S
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('shared.fullConversation')}
|
{t('shared.fullConversation')}
|
||||||
</Text>
|
</CustomText>
|
||||||
) : null
|
) : null
|
||||||
},
|
},
|
||||||
() => true
|
() => true
|
||||||
|
|
|
@ -2,6 +2,7 @@ import analytics from '@components/analytics'
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import { displayMessage } from '@components/Message'
|
import { displayMessage } from '@components/Message'
|
||||||
import { ParseEmojis } from '@components/Parse'
|
import { ParseEmojis } from '@components/Parse'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import {
|
import {
|
||||||
QueryKeyTimeline,
|
QueryKeyTimeline,
|
||||||
useTimelineMutation
|
useTimelineMutation
|
||||||
|
@ -10,141 +11,116 @@ import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useMemo } from 'react'
|
import React, { useCallback, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
import HeaderSharedCreated from './HeaderShared/Created'
|
import HeaderSharedCreated from './HeaderShared/Created'
|
||||||
import HeaderSharedMuted from './HeaderShared/Muted'
|
import HeaderSharedMuted from './HeaderShared/Muted'
|
||||||
|
|
||||||
const Names = React.memo(
|
const Names = ({ accounts }: { accounts: Mastodon.Account[] }) => {
|
||||||
({ accounts }: { accounts: Mastodon.Account[] }) => {
|
const { t } = useTranslation('componentTimeline')
|
||||||
const { t } = useTranslation('componentTimeline')
|
const { colors } = useTheme()
|
||||||
const { colors } = useTheme()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text
|
<CustomText
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
style={[styles.namesLeading, { color: colors.secondary }]}
|
style={{ ...StyleConstants.FontStyle.M, color: colors.secondary }}
|
||||||
>
|
>
|
||||||
<Text>{t('shared.header.conversation.withAccounts')}</Text>
|
<CustomText>{t('shared.header.conversation.withAccounts')}</CustomText>
|
||||||
{accounts.map((account, index) => (
|
{accounts.map((account, index) => (
|
||||||
<Text key={account.id} numberOfLines={1}>
|
<CustomText key={account.id} numberOfLines={1}>
|
||||||
{index !== 0 ? t('common:separator') : undefined}
|
{index !== 0 ? t('common:separator') : undefined}
|
||||||
<ParseEmojis
|
<ParseEmojis
|
||||||
content={account.display_name || account.username}
|
content={account.display_name || account.username}
|
||||||
emojis={account.emojis}
|
emojis={account.emojis}
|
||||||
fontBold
|
fontBold
|
||||||
/>
|
/>
|
||||||
</Text>
|
</CustomText>
|
||||||
))}
|
))}
|
||||||
</Text>
|
</CustomText>
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
() => true
|
|
||||||
)
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
queryKey: QueryKeyTimeline
|
queryKey: QueryKeyTimeline
|
||||||
conversation: Mastodon.Conversation
|
conversation: Mastodon.Conversation
|
||||||
}
|
}
|
||||||
|
|
||||||
const HeaderConversation = React.memo(
|
const HeaderConversation = ({ queryKey, conversation }: Props) => {
|
||||||
({ queryKey, conversation }: Props) => {
|
const { colors, theme } = useTheme()
|
||||||
const { colors, theme } = useTheme()
|
const { t } = useTranslation('componentTimeline')
|
||||||
const { t } = useTranslation('componentTimeline')
|
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const mutation = useTimelineMutation({
|
const mutation = useTimelineMutation({
|
||||||
onMutate: true,
|
onMutate: true,
|
||||||
onError: (err: any, _, oldData) => {
|
onError: (err: any, _, oldData) => {
|
||||||
displayMessage({
|
displayMessage({
|
||||||
theme,
|
theme,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: t('common:message.error.message', {
|
message: t('common:message.error.message', {
|
||||||
function: t(`shared.header.conversation.delete.function`)
|
function: t(`shared.header.conversation.delete.function`)
|
||||||
}),
|
}),
|
||||||
...(err.status &&
|
...(err.status &&
|
||||||
typeof err.status === 'number' &&
|
typeof err.status === 'number' &&
|
||||||
err.data &&
|
err.data &&
|
||||||
err.data.error &&
|
err.data.error &&
|
||||||
typeof err.data.error === 'string' && {
|
typeof err.data.error === 'string' && {
|
||||||
description: err.data.error
|
description: err.data.error
|
||||||
})
|
})
|
||||||
})
|
|
||||||
queryClient.setQueryData(queryKey, oldData)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const actionOnPress = useCallback(() => {
|
|
||||||
analytics('timeline_conversation_delete_press')
|
|
||||||
mutation.mutate({
|
|
||||||
type: 'deleteItem',
|
|
||||||
source: 'conversations',
|
|
||||||
queryKey,
|
|
||||||
id: conversation.id
|
|
||||||
})
|
})
|
||||||
}, [])
|
queryClient.setQueryData(queryKey, oldData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const actionChildren = useMemo(
|
const actionOnPress = useCallback(() => {
|
||||||
() => (
|
analytics('timeline_conversation_delete_press')
|
||||||
<Icon
|
mutation.mutate({
|
||||||
name='Trash'
|
type: 'deleteItem',
|
||||||
color={colors.secondary}
|
source: 'conversations',
|
||||||
size={StyleConstants.Font.Size.L}
|
queryKey,
|
||||||
/>
|
id: conversation.id
|
||||||
),
|
})
|
||||||
[]
|
}, [])
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
const actionChildren = useMemo(
|
||||||
<View style={styles.base}>
|
() => (
|
||||||
<View style={styles.nameAndMeta}>
|
<Icon
|
||||||
<Names accounts={conversation.accounts} />
|
name='Trash'
|
||||||
<View style={styles.meta}>
|
color={colors.secondary}
|
||||||
{conversation.last_status?.created_at ? (
|
size={StyleConstants.Font.Size.L}
|
||||||
<HeaderSharedCreated
|
/>
|
||||||
created_at={conversation.last_status?.created_at}
|
),
|
||||||
/>
|
[]
|
||||||
) : null}
|
)
|
||||||
<HeaderSharedMuted muted={conversation.last_status?.muted} />
|
|
||||||
</View>
|
return (
|
||||||
|
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||||
|
<View style={{ flex: 3 }}>
|
||||||
|
<Names accounts={conversation.accounts} />
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: StyleConstants.Spacing.XS,
|
||||||
|
marginBottom: StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{conversation.last_status?.created_at ? (
|
||||||
|
<HeaderSharedCreated
|
||||||
|
created_at={conversation.last_status?.created_at}
|
||||||
|
edited_at={conversation.last_status?.edited_at}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<HeaderSharedMuted muted={conversation.last_status?.muted} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Pressable
|
|
||||||
style={styles.action}
|
|
||||||
onPress={actionOnPress}
|
|
||||||
children={actionChildren}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
|
||||||
},
|
|
||||||
() => true
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
<Pressable
|
||||||
base: {
|
style={{ flex: 1, flexDirection: 'row', justifyContent: 'center' }}
|
||||||
flex: 1,
|
onPress={actionOnPress}
|
||||||
flexDirection: 'row'
|
children={actionChildren}
|
||||||
},
|
/>
|
||||||
nameAndMeta: {
|
</View>
|
||||||
flex: 3
|
)
|
||||||
},
|
}
|
||||||
meta: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginTop: StyleConstants.Spacing.XS,
|
|
||||||
marginBottom: StyleConstants.Spacing.S
|
|
||||||
},
|
|
||||||
created_at: {
|
|
||||||
...StyleConstants.FontStyle.S
|
|
||||||
},
|
|
||||||
action: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center'
|
|
||||||
},
|
|
||||||
namesLeading: {
|
|
||||||
...StyleConstants.FontStyle.M
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default HeaderConversation
|
export default HeaderConversation
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import HeaderSharedAccount from './HeaderShared/Account'
|
import HeaderSharedAccount from './HeaderShared/Account'
|
||||||
import HeaderSharedApplication from './HeaderShared/Application'
|
import HeaderSharedApplication from './HeaderShared/Application'
|
||||||
import HeaderSharedCreated from './HeaderShared/Created'
|
import HeaderSharedCreated from './HeaderShared/Created'
|
||||||
|
@ -16,77 +18,70 @@ export interface Props {
|
||||||
queryKey?: QueryKeyTimeline
|
queryKey?: QueryKeyTimeline
|
||||||
rootQueryKey?: QueryKeyTimeline
|
rootQueryKey?: QueryKeyTimeline
|
||||||
status: Mastodon.Status
|
status: Mastodon.Status
|
||||||
|
highlighted: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimelineHeaderDefault = React.memo(
|
const TimelineHeaderDefault = ({
|
||||||
({ queryKey, rootQueryKey, status }: Props) => {
|
queryKey,
|
||||||
const { t } = useTranslation('componentTimeline')
|
rootQueryKey,
|
||||||
const navigation = useNavigation()
|
status,
|
||||||
const { colors } = useTheme()
|
highlighted
|
||||||
|
}: Props) => {
|
||||||
|
const { t } = useTranslation('componentTimeline')
|
||||||
|
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
|
||||||
|
const { colors } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||||
<View style={styles.accountAndMeta}>
|
<View style={{ flex: 5 }}>
|
||||||
<HeaderSharedAccount account={status.account} />
|
<HeaderSharedAccount account={status.account} />
|
||||||
<View style={styles.meta}>
|
<View
|
||||||
<HeaderSharedCreated created_at={status.created_at} />
|
style={{
|
||||||
<HeaderSharedVisibility visibility={status.visibility} />
|
flexDirection: 'row',
|
||||||
<HeaderSharedMuted muted={status.muted} />
|
alignItems: 'center',
|
||||||
<HeaderSharedApplication application={status.application} />
|
marginTop: StyleConstants.Spacing.XS,
|
||||||
</View>
|
marginBottom: StyleConstants.Spacing.S
|
||||||
</View>
|
}}
|
||||||
|
>
|
||||||
{queryKey ? (
|
<HeaderSharedCreated
|
||||||
<Pressable
|
created_at={status.created_at}
|
||||||
accessibilityHint={t('shared.header.actions.accessibilityHint')}
|
edited_at={status.edited_at}
|
||||||
style={styles.action}
|
highlighted={highlighted}
|
||||||
onPress={() =>
|
|
||||||
navigation.navigate('Screen-Actions', {
|
|
||||||
queryKey,
|
|
||||||
rootQueryKey,
|
|
||||||
status,
|
|
||||||
url: status.url || status.uri,
|
|
||||||
type: 'status'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
children={
|
|
||||||
<Icon
|
|
||||||
name='MoreHorizontal'
|
|
||||||
color={colors.secondary}
|
|
||||||
size={StyleConstants.Font.Size.L}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
<HeaderSharedVisibility visibility={status.visibility} />
|
||||||
|
<HeaderSharedMuted muted={status.muted} />
|
||||||
|
<HeaderSharedApplication application={status.application} />
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
|
||||||
},
|
|
||||||
() => true
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
{queryKey ? (
|
||||||
base: {
|
<Pressable
|
||||||
flex: 1,
|
accessibilityHint={t('shared.header.actions.accessibilityHint')}
|
||||||
flexDirection: 'row'
|
style={{
|
||||||
},
|
flex: 1,
|
||||||
accountAndMeta: {
|
flexDirection: 'row',
|
||||||
flex: 5
|
justifyContent: 'center',
|
||||||
},
|
paddingBottom: StyleConstants.Spacing.S
|
||||||
meta: {
|
}}
|
||||||
flexDirection: 'row',
|
onPress={() =>
|
||||||
alignItems: 'center',
|
navigation.navigate('Screen-Actions', {
|
||||||
marginTop: StyleConstants.Spacing.XS,
|
queryKey,
|
||||||
marginBottom: StyleConstants.Spacing.S
|
rootQueryKey,
|
||||||
},
|
status,
|
||||||
created_at: {
|
type: 'status'
|
||||||
...StyleConstants.FontStyle.S
|
})
|
||||||
},
|
}
|
||||||
action: {
|
children={
|
||||||
flex: 1,
|
<Icon
|
||||||
flexDirection: 'row',
|
name='MoreHorizontal'
|
||||||
justifyContent: 'center',
|
color={colors.secondary}
|
||||||
paddingBottom: StyleConstants.Spacing.S
|
size={StyleConstants.Font.Size.L}
|
||||||
}
|
/>
|
||||||
})
|
}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default TimelineHeaderDefault
|
export default TimelineHeaderDefault
|
||||||
|
|
|
@ -4,11 +4,13 @@ import {
|
||||||
RelationshipOutgoing
|
RelationshipOutgoing
|
||||||
} from '@components/Relationship'
|
} from '@components/Relationship'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { Pressable, StyleSheet, View } from 'react-native'
|
import { Pressable, View } from 'react-native'
|
||||||
import HeaderSharedAccount from './HeaderShared/Account'
|
import HeaderSharedAccount from './HeaderShared/Account'
|
||||||
import HeaderSharedApplication from './HeaderShared/Application'
|
import HeaderSharedApplication from './HeaderShared/Application'
|
||||||
import HeaderSharedCreated from './HeaderShared/Created'
|
import HeaderSharedCreated from './HeaderShared/Created'
|
||||||
|
@ -20,113 +22,103 @@ export interface Props {
|
||||||
notification: Mastodon.Notification
|
notification: Mastodon.Notification
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimelineHeaderNotification = React.memo(
|
const TimelineHeaderNotification = ({ queryKey, notification }: Props) => {
|
||||||
({ queryKey, notification }: Props) => {
|
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
|
||||||
const navigation = useNavigation()
|
const { colors } = useTheme()
|
||||||
const { colors } = useTheme()
|
|
||||||
|
|
||||||
const actions = useMemo(() => {
|
const actions = useMemo(() => {
|
||||||
switch (notification.type) {
|
switch (notification.type) {
|
||||||
case 'follow':
|
case 'follow':
|
||||||
return <RelationshipOutgoing id={notification.account.id} />
|
return <RelationshipOutgoing id={notification.account.id} />
|
||||||
case 'follow_request':
|
case 'follow_request':
|
||||||
return <RelationshipIncoming id={notification.account.id} />
|
return <RelationshipIncoming id={notification.account.id} />
|
||||||
default:
|
default:
|
||||||
if (notification.status) {
|
if (notification.status) {
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
paddingBottom: StyleConstants.Spacing.S
|
paddingBottom: StyleConstants.Spacing.S
|
||||||
}}
|
}}
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
navigation.navigate('Screen-Actions', {
|
navigation.navigate('Screen-Actions', {
|
||||||
queryKey,
|
queryKey,
|
||||||
status: notification.status,
|
status: notification.status!,
|
||||||
url: notification.status?.url || notification.status?.uri,
|
type: 'status'
|
||||||
type: 'status'
|
})
|
||||||
})
|
}
|
||||||
}
|
children={
|
||||||
children={
|
<Icon
|
||||||
<Icon
|
name='MoreHorizontal'
|
||||||
name='MoreHorizontal'
|
color={colors.secondary}
|
||||||
color={colors.secondary}
|
size={StyleConstants.Font.Size.L}
|
||||||
size={StyleConstants.Font.Size.L}
|
/>
|
||||||
/>
|
}
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [notification.type])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={styles.base}>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flex:
|
|
||||||
notification.type === 'follow' ||
|
|
||||||
notification.type === 'follow_request'
|
|
||||||
? 1
|
|
||||||
: 4
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<HeaderSharedAccount
|
|
||||||
account={
|
|
||||||
notification.status
|
|
||||||
? notification.status.account
|
|
||||||
: notification.account
|
|
||||||
}
|
|
||||||
{...((notification.type === 'follow' ||
|
|
||||||
notification.type === 'follow_request') && { withoutName: true })}
|
|
||||||
/>
|
|
||||||
<View style={styles.meta}>
|
|
||||||
<HeaderSharedCreated created_at={notification.created_at} />
|
|
||||||
{notification.status?.visibility ? (
|
|
||||||
<HeaderSharedVisibility
|
|
||||||
visibility={notification.status.visibility}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<HeaderSharedMuted muted={notification.status?.muted} />
|
|
||||||
<HeaderSharedApplication
|
|
||||||
application={notification.status?.application}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
)
|
||||||
</View>
|
}
|
||||||
|
}
|
||||||
|
}, [notification.type])
|
||||||
|
|
||||||
<View
|
return (
|
||||||
style={[
|
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||||
styles.relationship,
|
<View
|
||||||
|
style={{
|
||||||
|
flex:
|
||||||
notification.type === 'follow' ||
|
notification.type === 'follow' ||
|
||||||
notification.type === 'follow_request'
|
notification.type === 'follow_request'
|
||||||
? { flexShrink: 1 }
|
? 1
|
||||||
: { flex: 1 }
|
: 4
|
||||||
]}
|
}}
|
||||||
|
>
|
||||||
|
<HeaderSharedAccount
|
||||||
|
account={
|
||||||
|
notification.status
|
||||||
|
? notification.status.account
|
||||||
|
: notification.account
|
||||||
|
}
|
||||||
|
{...((notification.type === 'follow' ||
|
||||||
|
notification.type === 'follow_request') && { withoutName: true })}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: StyleConstants.Spacing.XS,
|
||||||
|
marginBottom: StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{actions}
|
<HeaderSharedCreated
|
||||||
|
created_at={notification.created_at}
|
||||||
|
edited_at={notification.status?.edited_at}
|
||||||
|
/>
|
||||||
|
{notification.status?.visibility ? (
|
||||||
|
<HeaderSharedVisibility
|
||||||
|
visibility={notification.status.visibility}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<HeaderSharedMuted muted={notification.status?.muted} />
|
||||||
|
<HeaderSharedApplication
|
||||||
|
application={notification.status?.application}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
|
||||||
},
|
|
||||||
() => true
|
|
||||||
)
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
<View
|
||||||
base: {
|
style={[
|
||||||
flex: 1,
|
{ marginLeft: StyleConstants.Spacing.M },
|
||||||
flexDirection: 'row'
|
notification.type === 'follow' ||
|
||||||
},
|
notification.type === 'follow_request'
|
||||||
meta: {
|
? { flexShrink: 1 }
|
||||||
flexDirection: 'row',
|
: { flex: 1 }
|
||||||
alignItems: 'center',
|
]}
|
||||||
marginTop: StyleConstants.Spacing.XS,
|
>
|
||||||
marginBottom: StyleConstants.Spacing.S
|
{actions}
|
||||||
},
|
</View>
|
||||||
relationship: {
|
</View>
|
||||||
marginLeft: StyleConstants.Spacing.M
|
)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
export default TimelineHeaderNotification
|
export default TimelineHeaderNotification
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { ParseEmojis } from '@root/components/Parse'
|
import { ParseEmojis } from '@root/components/Parse'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyleSheet, Text, View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
account: Mastodon.Account
|
account: Mastodon.Account
|
||||||
|
@ -16,48 +17,35 @@ const HeaderSharedAccount = React.memo(
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||||
{withoutName ? null : (
|
{withoutName ? null : (
|
||||||
<Text
|
<CustomText
|
||||||
accessibilityHint={t(
|
accessibilityHint={t(
|
||||||
'shared.header.shared.account.name.accessibilityHint'
|
'shared.header.shared.account.name.accessibilityHint'
|
||||||
)}
|
)}
|
||||||
style={styles.name}
|
style={{ marginRight: StyleConstants.Spacing.XS }}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
<ParseEmojis
|
<ParseEmojis
|
||||||
content={account.display_name || account.username}
|
content={account?.display_name || account.username}
|
||||||
emojis={account.emojis}
|
emojis={account.emojis}
|
||||||
fontBold
|
fontBold
|
||||||
/>
|
/>
|
||||||
</Text>
|
</CustomText>
|
||||||
)}
|
)}
|
||||||
<Text
|
<CustomText
|
||||||
accessibilityHint={t(
|
accessibilityHint={t(
|
||||||
'shared.header.shared.account.account.accessibilityHint'
|
'shared.header.shared.account.account.accessibilityHint'
|
||||||
)}
|
)}
|
||||||
style={[styles.acct, { color: colors.secondary }]}
|
style={{ flexShrink: 1, color: colors.secondary }}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
@{account.acct}
|
@{account.acct}
|
||||||
</Text>
|
</CustomText>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
() => true
|
() => true
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
marginRight: StyleConstants.Spacing.XS
|
|
||||||
},
|
|
||||||
acct: {
|
|
||||||
flexShrink: 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default HeaderSharedAccount
|
export default HeaderSharedAccount
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import openLink from '@components/openLink'
|
import openLink from '@components/openLink'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyleSheet, Text } from 'react-native'
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
application?: Mastodon.Application
|
application?: Mastodon.Application
|
||||||
|
@ -16,7 +16,8 @@ const HeaderSharedApplication = React.memo(
|
||||||
const { t } = useTranslation('componentTimeline')
|
const { t } = useTranslation('componentTimeline')
|
||||||
|
|
||||||
return application && application.name !== 'Web' ? (
|
return application && application.name !== 'Web' ? (
|
||||||
<Text
|
<CustomText
|
||||||
|
fontStyle='S'
|
||||||
accessibilityRole='link'
|
accessibilityRole='link'
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
analytics('timeline_shared_header_application_press', {
|
analytics('timeline_shared_header_application_press', {
|
||||||
|
@ -24,24 +25,20 @@ const HeaderSharedApplication = React.memo(
|
||||||
})
|
})
|
||||||
application.website && (await openLink(application.website))
|
application.website && (await openLink(application.website))
|
||||||
}}
|
}}
|
||||||
style={[styles.application, { color: colors.secondary }]}
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
marginLeft: StyleConstants.Spacing.S,
|
||||||
|
color: colors.secondary
|
||||||
|
}}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
{t('shared.header.shared.application', {
|
{t('shared.header.shared.application', {
|
||||||
application: application.name
|
application: application.name
|
||||||
})}
|
})}
|
||||||
</Text>
|
</CustomText>
|
||||||
) : null
|
) : null
|
||||||
},
|
},
|
||||||
() => true
|
() => true
|
||||||
)
|
)
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
application: {
|
|
||||||
flex: 1,
|
|
||||||
...StyleConstants.FontStyle.S,
|
|
||||||
marginLeft: StyleConstants.Spacing.S
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default HeaderSharedApplication
|
export default HeaderSharedApplication
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue