Compare commits

...

99 Commits

Author SHA1 Message Date
uburoiubu
f25ce87e55
v. 0.23.17 2023-07-26 17:58:27 +02:00
Evgenii Kozlov
f02fd6046c
DROID-1392 Analytics | Fix | Add onboarding analytics for create-account event (#252) 2023-07-26 17:56:36 +02:00
Evgenii Kozlov
9b360a7a93
DROID-539 App | Fix | Library updates for release (#251) 2023-07-26 17:35:13 +02:00
uburoiubu
defffb1c20
v. 0.23.16 2023-07-25 16:48:24 +02:00
Evgenii Kozlov
9098f50987
DROID-1407 Search | Fix | Hot-fix for corner cases where the same object is opened twice due to navigation through global search (#246) 2023-07-25 16:47:21 +02:00
uburoiubu
23da18c499
v. 0.23.15 2023-07-25 13:19:05 +02:00
Sergey Fuksman
73612da190
Prepare anytype-kotlin for contributing (#238) 2023-07-25 13:18:23 +02:00
Sergey Fuksman
0eb4e5a36c
Update LICENSE.md 2023-07-25 13:18:21 +02:00
Evgenii Kozlov
15156bda28
DROID-1504 App | Stability | Fix or try/catch sentry crashes from 0.23.14 (#245) 2023-07-25 13:17:22 +02:00
Evgenii Kozlov
682d094644
DROID-1407 App | Enhancement | Do not return to homescreen when closing global-search screen (#244) 2023-07-25 12:37:49 +02:00
Evgenii Kozlov
e1d7bbcb2f
DROID-151 Onboarding | Tech | Release DI components when completing onboarding (#243) 2023-07-25 12:37:36 +02:00
Evgenii Kozlov
6d2df4ef00
DROID-1412 App | Tech | Remove unused libraries (#241) 2023-07-24 21:53:18 +02:00
Evgenii Kozlov
5240249e16
DROID-1517 Sets or Collections | Fix | Drag-and-dropping of view's relations does not work correctly when placing a relation on top of the list (#239) 2023-07-24 15:37:20 +02:00
Evgenii Kozlov
d5ac6dde2b
DROID-1523 Editor | Fix | There is no empty space at the bottom of the page filled with no content - no possibility to create a new text block (#237) 2023-07-24 14:15:48 +02:00
Evgenii Kozlov
97d3306548
DROID-1583 Editor | Fix | Incorrect click handling in object type toolbar results in entering in multi-select mode (#233) 2023-07-20 16:59:08 +02:00
Evgenii Kozlov
1730e056b5
DROID-1504 Relations | Fix | Remove redundant logging (#232) 2023-07-20 15:51:28 +02:00
uburoiubu
de84f4192d
v. 0.23.14 2023-07-19 20:16:59 +02:00
Evgenii Kozlov
2b4f433c5f
DROID-1578 Editor | Enhancement | Keyboard should close when user create media black via slash menu (#231) 2023-07-19 20:04:45 +02:00
uburoiubu
940086d4c8
v. 0.23.13 2023-07-19 15:52:45 +02:00
Evgenii Kozlov
14f9e48f22
DROID-1568 Onboarding | Fix | Move operation off main thread when needed (#230) 2023-07-19 15:51:33 +02:00
Evgenii Kozlov
43c7d065bf
DROID-1568 App | Tech | Moving calls off main thread when needed (#229) 2023-07-19 15:42:43 +02:00
uburoiubu
54a346fc32
v. 0.23.12 2023-07-19 08:55:56 +02:00
Evgenii Kozlov
cd8010cd1a
DROID-1576 Protocol | Enhancement | MW 0.27.15 (#225) 2023-07-19 08:54:54 +02:00
uburoiubu
f0c24fe5ad
v. 0.23.11 2023-07-18 19:42:44 +02:00
Evgenii Kozlov
6e5b06c44b
DROID-1573 Onboarding | Fix | Anytype identity name should not break screen layout when it is too long (#224) 2023-07-18 19:41:15 +02:00
uburoiubu
6be14283eb
v. 0.23.10 2023-07-18 17:11:15 +02:00
Evgenii Kozlov
04ff6862e9
DROID-1504 App | Fix | Minor fixes based on reports from Sentry (#223) 2023-07-18 17:09:43 +02:00
Evgenii Kozlov
1c3f1d1f20
DROID-1553 Onboarding | Enhancement | Colorise recovery phrase with on "Join" flow (#221) 2023-07-18 14:47:55 +02:00
Evgenii Kozlov
be2f2f50ce
DROID-1500 App | Tech | Updated RecyclerView version (#219) 2023-07-18 12:43:55 +02:00
Konstantin Ivanov
2f7db40cc5
Droid-1509 Editor | Fix | Changing the style via the slash menu (#220) 2023-07-18 12:37:27 +02:00
uburoiubu
9c77d316f4
v. 0.23.9 2023-07-17 18:52:30 +02:00
Evgenii Kozlov
ee34a38b7c
DROID-1551 Widgets | Enhancement | Fix broken tests (#216) 2023-07-17 18:51:15 +02:00
Konstantin Ivanov
8b33f58330
DROID-1524 Editor | Fix | Mention for untitled objects isn't visible in block (#215)
Co-authored-by: Evgenii Kozlov <ubuphobos@gmail.com>
2023-07-17 18:50:57 +02:00
Evgenii Kozlov
f0eb782366
DROID-1504 App | Tech | Fix workflow script (#218) 2023-07-17 18:49:05 +02:00
Evgenii Kozlov
8a2745a20a
DROID-1504 App | Tech | Run github actions checks on release branches (#217) 2023-07-17 18:43:52 +02:00
Evgenii Kozlov
1d6b2a412f
DROID-1550 Settings | Fix | Try/catch processing navigation command to open app settings (#214) 2023-07-17 17:08:21 +02:00
Konstantin Ivanov
bf1fe10865
DROID-1546 Editor | Fix | Link-to-object block should not crash when applying search-on-page highlights (#213) 2023-07-17 14:59:21 +02:00
Evgenii Kozlov
30eccc5bce
DROID-1549 Widgets | Fix | Prevent crashes when failed to get config for home screen (#212) 2023-07-17 14:42:09 +02:00
Evgenii Kozlov
d77b95cebe
DROID-1551 App | Fix | Do not crash when 'iconImage' relation value is stored using incorrect type format (#211) 2023-07-17 14:30:22 +02:00
Evgenii Kozlov
c1f00252fa
DROID-1548 App | Fix | Try/catch 'setInterfaceGetter' call to Middleware service (#210) 2023-07-17 14:30:06 +02:00
Evgenii Kozlov
085958916f
DROID-1504 Widgets | Fix | Layout fixes in tree or compact list widgets (#209) 2023-07-17 13:50:35 +02:00
Evgenii Kozlov
1dfab0bd07
DROID-1539 Widgets | Enhancement | Use search-by-id request for elements of widgets with favorite objects (#208) 2023-07-17 13:50:08 +02:00
uburoiubu
543f32516d
v. 0.23.8 2023-07-14 22:00:03 +02:00
Konstantin Ivanov
a1f4b8bf0e
DROID-1541 Protocol | Enhancement | MW 0.27.13 (#207) 2023-07-14 21:09:50 +02:00
uburoiubu
3835801a52
v. 0.23.7 2023-07-14 14:10:58 +02:00
Konstantin Ivanov
de5c31d44b
DROID-1327 Widgets | Fix | Collections, sorting by last modified date (#206) 2023-07-14 13:57:50 +02:00
Evgenii Kozlov
841448cec3
DROID-1539 Widgets | Fix | Items in favorites list has different order then items in favorites full-screen widget (#205) 2023-07-14 13:29:04 +02:00
Konstantin Ivanov
d573005ced
DROID-1538 Protocol | Enhancement | MW 0.27.12 (#203) 2023-07-14 13:12:11 +02:00
Konstantin Ivanov
c5de11ed74
DROID-1507 Settings | Fix | Space icon, remove button #201 (#202) 2023-07-14 12:12:44 +02:00
Konstantin Ivanov
1db5a1ed98
DROID-1525 Design | Last object in object search screens isn't displayed 2 (#200) 2023-07-14 11:38:03 +02:00
Evgenii Kozlov
4a3670a82d
DROID-1504 Settings | Fix | Skip "collapsed" state on about-app screen (#199) 2023-07-14 11:37:20 +02:00
uburoiubu
a9507debf7
v. 0.23.6 2023-07-13 20:21:43 +02:00
Evgenii Kozlov
9396ae88b0
DROID-1451 App | Tech | Remove legacy related to 'Space debug' option on 'Profile' screen (#198) 2023-07-13 20:20:47 +02:00
Evgenii Kozlov
5058a2b2ed
DROID-1451 Space | Enhancement | Move 'Space debug' button to the 'Space' screen + Refact (#197) 2023-07-13 19:45:12 +02:00
Konstantin Ivanov
05747aa413
DROID-1526 Set | Fix | Hide bottom toolbar on back pressed (#196) 2023-07-13 19:28:10 +02:00
Konstantin Ivanov
7ae3e7697c
DROID-1525 Design | Last object in object search screens isn't displayed (#195) 2023-07-13 19:23:31 +02:00
Konstantin Ivanov
b1c14a278e
DROID-1533 Editor | Fix | Slash menu contains Type, Video, Image, File, Audio (#194) 2023-07-13 18:53:37 +02:00
Evgenii Kozlov
0750ef29e2
DROID-1451 App | Tech | Space-debug refactoring (Part 1) (#192) 2023-07-13 16:49:23 +02:00
Evgenii Kozlov
0b7acee9ab
DROID-1473 Onboarding | Enhancement | Remove exit transition for onboarding screen container (#191) 2023-07-13 15:36:19 +02:00
Evgenii Kozlov
293e112aa3
DROID-1529 App | Fix | Do not clear repository data on logout (#190) 2023-07-13 15:35:56 +02:00
uburoiubu
7b4a846866
v. 0.23.5 2023-07-12 23:05:05 +02:00
Evgenii Kozlov
9a458514a9
DROID-1486 Onboarding | Fix | Do not show toast when discarding the sign-up flow (#188) 2023-07-12 23:03:33 +02:00
Konstantin Ivanov
e7c5c63a9e
DROID-1527 App | Design | Icon background (#186) 2023-07-12 22:52:55 +02:00
Konstantin Ivanov
4c62d96eed
DROID-1392 Onboarding | Analytics | Linear onboarding events (#185) 2023-07-12 21:26:43 +02:00
Evgenii Kozlov
ffb3086db6
DROID-1486 Onboarding | Fix | Loading after success delay (#184) 2023-07-12 18:35:28 +02:00
Evgenii Kozlov
99fec87996
DROID-1486 Onboarding | Fix | Do not clear repository data when interrupting sign-up flow (#183) 2023-07-12 18:17:43 +02:00
Evgenii Kozlov
5b63a8b65f
DROID-1486 Onboarding | Enhancement | Show three-dots loading indicator when creating new account (#182) 2023-07-12 18:17:29 +02:00
Evgenii Kozlov
813eda1b34
DROID-1257 Onboarding | Fix | Update wording for QR-code request (#181) 2023-07-12 17:35:07 +02:00
Evgenii Kozlov
2c63b64352
DROID-1528 Widgets | Enhancement | Increase click are for list and tree widget items (#180) 2023-07-12 17:25:59 +02:00
uburoiubu
4521a60507
v. 0.23.4 2023-07-12 16:15:08 +02:00
Evgenii Kozlov
659d06bcdc
DROID Onboarding | Enhancement | Smoother keyboard transitions for the sign-up flow (#179) 2023-07-12 16:11:26 +02:00
Konstantin Ivanov
53f2b97c68
DROID-1521 Onboarding | Fix | Migration screen (#178) 2023-07-12 12:28:54 +02:00
uburoiubu
f00ddc38ce
v. 0.23.3 2023-07-12 12:15:42 +02:00
Evgenii Kozlov
1fa1acf468
DROID-1465 App | Fix | Fix version text color on the new splash screen (#177) 2023-07-12 11:59:29 +02:00
Evgenii Kozlov
369780906e
DROID-1470 Onboarding | Enhancement | Workaround to prevent background circle with video shrinking on Android 10 (#176) 2023-07-12 11:47:22 +02:00
Evgenii Kozlov
67aa3abf27
DROID-1475 Onboarding | Enhancement | Add dots loading indicator for the sign-in flow (#175) 2023-07-12 11:13:18 +02:00
Evgenii Kozlov
29f635590b
DROID-1512 Protocol | Enhancement | MW 0.27.11 (#174) 2023-07-12 10:26:13 +02:00
uburoiubu
409354b7cc
v. 0.23.2 2023-07-11 22:49:33 +02:00
Konstantin Ivanov
df833b7e9c
DROID-1503 Sample | Design | Base dialog (#170) 2023-07-11 22:46:26 +02:00
Evgenii Kozlov
946155110f
DROID-1475 Onboarding | Enhancement | Blocking back navigation when logging out after during sign-up (#171) 2023-07-11 22:40:31 +02:00
Evgenii Kozlov
db0a39104c
DROID-1472 Onboarding | Fix | Avoid inconsistent state in navigation when pressing "Back" button twice when returning back from keychain-phrase-login screen (#173) 2023-07-11 22:39:42 +02:00
Konstantin Ivanov
8f1ea59930
DROID-1413 App | Fix | Search screen should not stay in navigation stack (#172) 2023-07-11 21:48:14 +02:00
Konstantin Ivanov
ca7f746c68
DROID-1503 App | Design | Scan Qr Code, base dialog (#169) 2023-07-11 19:04:30 +02:00
Evgenii Kozlov
8dd99f93d8
DROID-1475 Onboarding | Enhancement | Blocking back button when sign-up is in progress (#168) 2023-07-11 16:37:58 +02:00
Konstantin Ivanov
8e9c78a99b
DROID-1499 App | Design | Splash screen has icon with cutted corners (#167) 2023-07-11 13:49:01 +02:00
Evgenii Kozlov
49f1827dbf
DROID-1472 Onboarding | Tech | Setup new component (#166) 2023-07-11 13:42:13 +02:00
Evgenii Kozlov
a7faa20c99
DROID-1500 App | Tech | Revert library updates (#165) 2023-07-11 10:58:58 +02:00
Konstantin Ivanov
e1f5001a3d
DROID-1465 App | Design | Splash screen (#163) 2023-07-10 19:41:05 +02:00
uburoiubu
8ced4f7e1a
v. 0.23.1 2023-07-10 19:32:42 +02:00
Evgenii Kozlov
394528abc8
DROID-1502 Protocol | Enhancement | MW 0.27.9 (#162) 2023-07-10 18:56:33 +02:00
Evgenii Kozlov
14bb49c50b
DROID-1434 Onboarding | Fix | Fixed text colors (#161) 2023-07-10 18:46:50 +02:00
Evgenii Kozlov
cca7791a90
DROID-1236 Onboarding | Fix | Fix horizontal padding for description text on soul-creation screen (#160) 2023-07-10 18:20:36 +02:00
Konstantin Ivanov
8756d18d1a
DROID-1401 Editor | Design | Blocks design, tests (#158) 2023-07-10 17:25:23 +02:00
Evgenii Kozlov
c5abf0ba0d
DROID-1500 App | Tech | Update Espresso testing library (#159) 2023-07-10 16:49:12 +02:00
Evgenii Kozlov
da8151655e
DROID-1501 App | Tech | Update Jetpack Compose (#157) 2023-07-10 16:41:49 +02:00
Evgenii Kozlov
5f2fc71371
DROID-1500 App | Tech | Update Kotlin to 1.7.20 (#156) 2023-07-10 16:26:19 +02:00
Evgenii Kozlov
32028f654c
DROID-1459 Onboarding | Fix | Fix placeholder text on soul-creation screen (#155) 2023-07-10 15:26:43 +02:00
Evgenii Kozlov
e8f22ed9b3
DROID-1493 Onboarding | Fix | Change the content vertical position on the start screen (#153) 2023-07-10 15:14:26 +02:00
Evgenii Kozlov
c5a4907572
DROID-1492 Onboarding | Fix | Keychain phrase should be lowercase (#152) 2023-07-10 14:36:14 +02:00
181 changed files with 3365 additions and 1396 deletions

View File

@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Actual behavior**
A clear and concise description of what actually happened.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Smartphone (please complete the following information):**
- Device: [e.g. Pixel 5]
- OS: [e.g. Android 11]
**App version**
- [e.g. 0.1.0]
**Additional context**
Add any other context about the problem here.

61
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@ -0,0 +1,61 @@
name: Bug report
description: File a bug
labels: ["bug", "triage"]
body:
- type: checkboxes
attributes:
label: Have you read a contributing guide?
description: Please read [CONTRINUTING.md](../blob/main/docs/CONTRIBUTING.md) before proceeding. Check if there are existing [bug reports](https://community.anytype.io/c/bug-reports/l/latest?board=default) and upvote them instead. Follow [this article](https://doc.anytype.io/d/community/community-forum) if you don't know how to join the community.
options:
- label: I have read CONTRIBUTING.md
required: true
- label: I have searched the existing issues and didn't find any that were similar
required: true
- label: I have considered creating a pull request with fixes instead of a bug report and want to proceed
required: true
- type: textarea
attributes:
label: Current Behavior
description: A concise description of what you're experiencing.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: A concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Steps To Reproduce
description: Steps to reproduce the behavior.
placeholder: |
1. In this environment...
2. With this config...
3. Run '...'
4. See error...
validations:
required: true
- type: textarea
attributes:
label: Environment
description: |
For example,
- **OS**: macOS 14
- **Version**: 0.35.4
value: |
- OS:
- Version:
render: markdown
validations:
required: true
- type: textarea
attributes:
label: Anything else?
description: |
Screenshots? Videos? Links? References? Anything that will give us more context about the issue you are encountering!
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: false

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Contributors Community
url: https://github.com/orgs/anyproto/discussions
about: A better place for contributing discussions.
- name: Anytype Community
url: https://community.anytype.io
about: Existing feature requests and bug reports from Anytype users.

View File

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -0,0 +1,38 @@
name: Feature request
description: Suggest an idea
labels: ["enhancement", "triage"]
body:
- type: checkboxes
attributes:
label: Have you read a contributing guide?
description: Please read [CONTRINUTING.md](../blob/main/docs/CONTRIBUTING.md) before proceeding. Check if there are existing [feature requests](https://community.anytype.io/c/feature-requests/l/latest?board=default) and upvote them instead. Follow [this article](https://doc.anytype.io/d/community/community-forum) if you don't know how to join the community.
options:
- label: I have read CONTRIBUTING.md
required: true
- label: I have searched the existing requests and didn't find any that were similar
required: true
- label: I have considered creating a pull request instead and want to proceed
required: true
- type: textarea
id: feature-description
attributes:
label: Clear and concise description of the problem
validations:
required: true
- type: textarea
id: suggested-solution
attributes:
label: Suggested solution
validations:
required: true
- type: textarea
id: alternative
attributes:
label: Alternative
description: Clear and concise description of any alternative solutions or features you've considered.
- type: textarea
id: additional-context
attributes:
label: Additional context
description: Any other context or screenshots about the feature request here.

View File

@ -0,0 +1,57 @@
<!-- This template inspired by https://github.com/open-sauced/.github/blob/main/.github/PULL_REQUEST_TEMPLATE.md?plain=1 -->
---
- [ ] I understand that contributing to this repository will require me to agree with the [CLA](https://github.com/anyproto/open/blob/main/templates/CLA.md)
<!-- The bot will prompt you to accept the CLA by replying with a pre-composed message in the comments. If you have already accepted the CLA, you won't need to do it again. -->
---
### Description
<!--
Please do not leave this blank
This PR [adds/removes/fixes/replaces] the [feature/bug/etc].
-->
### What type of PR is this? (check all applicable)
- [ ] 🍕 Feature
- [ ] 🐛 Bug Fix
- [ ] 📝 Documentation Update
- [ ] 🎨 Style
- [ ] 🧑‍💻 Code Refactor
- [ ] 🔥 Performance Improvements
- [ ] ✅ Test
- [ ] 🤖 Build
- [ ] 🔁 CI
### Related Tickets & Documents
<!--
Please provide links to issues, community forum posts, or other sources
-->
### Mobile & Desktop Screenshots/Recordings
<!-- Visual changes require screenshots -->
### Added tests?
- [ ] 👍 yes
- [ ] 🙅 no, because they aren't needed
- [ ] 🙋 no, because I need help
### Added to documentation?
- [ ] 📜 README.md
- [ ] 📓 [tech-docs](https://github.com/anyproto/tech-docs)
- [ ] 🙅 no documentation needed
### [optional] Are there any post-deployment tasks we need to perform?
<!--
For Work In Progress Pull Requests, please use the Draft PR feature,
see https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests for further details.
-->

View File

@ -3,7 +3,7 @@ on:
pull_request:
# add "synchronize" in "types", in order to trigger workflow for pull request commit(s) pushes.
types: [ opened ]
branches: [ main ]
branches: [ main, release/**]
name: Run debug unit tests
jobs:
setup-android:

45
.github/workflows/cla.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: "CLA Assistant"
on:
issue_comment:
types: [created]
pull_request_target:
types: [opened,closed,synchronize]
permissions:
actions: write
contents: write
pull-requests: write
statuses: write
jobs:
CLAAssistant:
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
runs-on: ubuntu-latest
steps:
- name: Get Org Members
id: members
uses: octokit/request-action@v2.x
with:
route: GET /orgs/anyproto/members
env:
GITHUB_TOKEN: ${{ secrets.ANY_CLA_TOKEN }}
- name: Extract Member Logins
run: |
echo '${{ steps.members.outputs.data }}' | jq -r '.[].login' | tr '\n' ',' > members.txt
echo "dependabot" >> members.txt
echo "members=$(cat members.txt)" >> "$GITHUB_ENV"
- name: CLA Assistant
uses: contributor-assistant/github-action@v2.3.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PERSONAL_ACCESS_TOKEN: ${{ secrets.ANY_CLA_TOKEN }}
with:
custom-notsigned-prcomment: '<br/>Thank you for your pull request, we really appreciate it!<br/><br/>Please sign our [Contributor License Agreement](https://github.com/anyproto/open/blob/main/templates/CLA.md) before we can accept your contribution.<br/>You can sign the CLA by simply commenting on this pull request with the following text.<br/>'
path-to-document: "https://github.com/anyproto/open/blob/main/templates/CLA.md"
remote-organization-name: "anyproto"
remote-repository-name: "cla"
branch: "main"
path-to-signatures: "signatures.json"
allowlist: ${{ env.members }}

View File

@ -20,7 +20,7 @@ rights to anyone else. These terms do not imply any other licenses not
expressly granted in this license.
If Licensee violates any of these terms, or uses the Software in a way not
uthorized under this license, the license granted to Licensee ends
authorized under this license, the license granted to Licensee ends
immediately.
If Licensee makes, or authorizes any other person to make, any written claim
that the Software, or any other Any Association Product, infringes or

View File

@ -80,11 +80,15 @@ Prerequisite: `brew install jq`
3. Make sure to update `middlewareVersion` version in `libs.versions.toml`.
## Contribution
Thank you for your desire to develop Anytype together.
Thank you for your desire to develop Anytype together!
Currently, we're not ready to accept PRs, but we will in the nearest future.
❤️ This project and everyone involved in it is governed by the [Code of Conduct](docs/CODE_OF_CONDUCT.md).
Follow us on [Github](https://github.com/anyproto) and join the [Contributors Community](https://github.com/orgs/anyproto/discussions).
🧑‍💻 Check out our [contributing guide](docs/CONTRIBUTING.md) to learn about asking questions, creating issues, or submitting pull requests.
🫢 For security findings, please email [security@anytype.io](mailto:security@anytype.io) and refer to our [security guide](docs/SECURITY.md) for more information.
🤝 Follow us on [Github](https://github.com/anyproto) and join the [Contributors Community](https://github.com/orgs/anyproto/discussions).
---
Made by Any — a Swiss association 🇨🇭

View File

@ -148,6 +148,29 @@ object EventsDictionary {
const val screenHome = "ScreenHome"
const val selectHomeTab = "SelectHomeTab"
// Onboarding events
const val screenOnboarding = "ScreenOnboarding"
const val clickOnboarding = "ClickOnboarding"
const val clickLogin = "ClickLogin"
enum class ScreenOnboardingStep(val value: String) {
VOID("Void"),
PHRASE("Phrase"),
SOUL("Soul"),
SOUL_CREATING("SoulCreating"),
SPACE_CREATING("SpaceCreating")
}
enum class ClickOnboardingButton(val value: String) {
SHOW_AND_COPY("ShowAndCopy"),
CHECK_LATER("CheckLater")
}
enum class ClickLoginButton(val value: String) {
PHRASE("Phrase"),
QR("Qr"),
}
// Routes
object Routes {
const val home = "ScreenHome"
@ -216,4 +239,5 @@ object EventsPropertiesKey {
const val align = "align"
const val originalId = "originalId"
const val view = "view"
const val step = "step"
}

View File

@ -3,7 +3,6 @@ plugins {
id "kotlin-android"
id "kotlin-kapt"
id "androidx.navigation.safeargs.kotlin"
id "com.google.firebase.crashlytics"
id "com.google.firebase.appdistribution"
}
@ -106,7 +105,7 @@ android {
}
composeOptions {
kotlinCompilerExtensionVersion libs.versions.androidxComposeVersion1.get()
kotlinCompilerExtensionVersion libs.versions.composeKotlinCompilerVersion.get()
}
buildFeatures {
@ -207,7 +206,6 @@ dependencies {
implementation libs.exoPlayerUi
implementation libs.amplitude
implementation libs.okhttp
implementation libs.shimmerLayout
implementation libs.photoView

View File

@ -1,4 +1,4 @@
version.versionMajor=0
version.versionMinor=23
version.versionPatch=0
version.versionPatch=17
version.useDatedVersionName=false

View File

@ -19,11 +19,11 @@
android:name="com.anytypeio.anytype.app.AndroidApplication"
android:allowBackup="false"
android:fullBackupContent="@xml/my_backup_rules"
android:icon="@mipmap/ic_new_app_launcher"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_new_app_launcher_round"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -24,7 +24,7 @@ class DefaultAppActionManager(val context: Context) : AppActionManager {
val shortcut = ShortcutInfoCompat.Builder(context, ACTION_CREATE_NEW_ID)
.setShortLabel(label)
.setLongLabel(label)
.setIcon(IconCompat.createWithResource(context, R.mipmap.ic_new_app_launcher))
.setIcon(IconCompat.createWithResource(context, R.mipmap.ic_launcher))
.setIntent(
Intent(Intent.ACTION_VIEW, null).apply {
setClass(context, MainActivity::class.java)

View File

@ -1,6 +1,7 @@
package com.anytypeio.anytype.di.common
import android.content.Context
import com.anytypeio.anytype.BuildConfig
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.di.feature.AddFileRelationModule
import com.anytypeio.anytype.di.feature.AddObjectRelationModule
@ -63,12 +64,13 @@ import com.anytypeio.anytype.di.feature.auth.DaggerDeletedAccountComponent
import com.anytypeio.anytype.di.feature.cover.UnsplashModule
import com.anytypeio.anytype.di.feature.home.DaggerHomeScreenComponent
import com.anytypeio.anytype.di.feature.library.DaggerLibraryComponent
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingLoginSetupComponent
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingMnemonicComponent
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingMnemonicLoginComponent
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingSoulCreationAnimComponent
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingSoulCreationComponent
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingComponent
import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingStartComponent
import com.anytypeio.anytype.di.feature.onboarding.login.DaggerOnboardingLoginSetupComponent
import com.anytypeio.anytype.di.feature.onboarding.login.DaggerOnboardingMnemonicLoginComponent
import com.anytypeio.anytype.di.feature.onboarding.signup.DaggerOnboardingMnemonicComponent
import com.anytypeio.anytype.di.feature.onboarding.signup.DaggerOnboardingSoulCreationAnimComponent
import com.anytypeio.anytype.di.feature.onboarding.signup.DaggerOnboardingSoulCreationComponent
import com.anytypeio.anytype.di.feature.onboarding.signup.DaggerOnboardingVoidComponent
import com.anytypeio.anytype.di.feature.relations.DaggerRelationCreateFromLibraryComponent
import com.anytypeio.anytype.di.feature.relations.DaggerRelationEditComponent
@ -101,6 +103,7 @@ import com.anytypeio.anytype.di.main.MainComponent
import com.anytypeio.anytype.ui.relations.RelationEditParameters
import com.anytypeio.anytype.ui.types.edit.TypeEditParameters
import com.anytypeio.anytype.ui.widgets.collection.DaggerCollectionComponent
import timber.log.Timber
class ComponentManager(
private val main: MainComponent,
@ -904,6 +907,12 @@ class ComponentManager(
.create(findComponentDependencies())
}
val onboardingComponent = Component {
DaggerOnboardingComponent
.factory()
.create(findComponentDependencies())
}
val onboardingStartComponent = Component {
DaggerOnboardingStartComponent
.factory()
@ -957,6 +966,16 @@ class ComponentManager(
fun release() {
instance = null
}
fun isInitialized() = instance != null
override fun toString(): String {
return if (BuildConfig.DEBUG) {
instance?.toString().orEmpty()
} else {
super.toString()
}
}
}
class ComponentMap<T>(private val builder: () -> T) {
@ -970,6 +989,16 @@ class ComponentManager(
fun release(id: String) {
map.remove(id)
}
fun isInitialized() = map.isNotEmpty()
override fun toString(): String {
return if (BuildConfig.DEBUG) {
map.toString()
} else {
super.toString()
}
}
}
class DependentComponentMap<T>(private val builder: (Id) -> T) {
@ -983,6 +1012,16 @@ class ComponentManager(
fun release(id: Id) {
map.remove(id)
}
fun isInitialized() = map.isNotEmpty()
override fun toString(): String {
return if (BuildConfig.DEBUG) {
map.toString()
} else {
super.toString()
}
}
}
class ComponentWithParams<out T, in PARAMETER>(private val builder: (PARAMETER) -> T) {
@ -1001,4 +1040,34 @@ class ComponentManager(
private inline fun <reified T : ComponentDependencies> findComponentDependencies(): T {
return provider.dependencies[T::class.java] as T
}
/**
* Can be used for debugging and tracing unreleased components.
*/
fun logUnreleasedComponents() {
if (BuildConfig.DEBUG) {
javaClass.declaredFields.forEach { field ->
val component = field.get(this)
if (component != null) {
when (component) {
is Component<*> -> {
if (component.isInitialized()) {
Timber.d("Unreleased component: $component")
}
}
is DependentComponentMap<*> -> {
if (component.isInitialized()) {
Timber.d("Unreleased component: $component")
}
}
is ComponentMap<*> -> {
if (component.isInitialized()) {
Timber.d("Unreleased component: $component")
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,42 @@
package com.anytypeio.anytype.di.feature.onboarding
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.di.common.ComponentDependencies
import com.anytypeio.anytype.presentation.onboarding.OnboardingViewModel
import com.anytypeio.anytype.ui.onboarding.OnboardingFragment
import dagger.Binds
import dagger.Component
import dagger.Module
@Component(
dependencies = [OnboardingDependencies::class],
modules = [
OnboardingModule::class,
OnboardingModule.Declarations::class
]
)
@PerScreen
interface OnboardingComponent {
@Component.Factory
interface Builder {
fun create(dependencies: OnboardingDependencies): OnboardingComponent
}
fun inject(fragment: OnboardingFragment)
}
@Module
object OnboardingModule {
@Module
interface Declarations {
@Binds
@AuthScreenScope
fun bindViewModelFactory(factory: OnboardingViewModel.Factory): ViewModelProvider.Factory
}
}
interface OnboardingDependencies : ComponentDependencies {
fun analytics(): Analytics
}

View File

@ -1,4 +1,4 @@
package com.anytypeio.anytype.di.feature.onboarding
package com.anytypeio.anytype.di.feature.onboarding.login
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.CrashReporter

View File

@ -1,4 +1,4 @@
package com.anytypeio.anytype.di.feature.onboarding
package com.anytypeio.anytype.di.feature.onboarding.login
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics

View File

@ -1,6 +1,7 @@
package com.anytypeio.anytype.di.feature.onboarding
package com.anytypeio.anytype.di.feature.onboarding.signup
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.di.common.ComponentDependencies
import com.anytypeio.anytype.domain.auth.interactor.GetMnemonic
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
@ -53,6 +54,7 @@ object OnboardingMnemonicModule {
interface OnboardingMnemonicDependencies : ComponentDependencies {
fun authRepository(): AuthRepository
fun analytics(): Analytics
}
@Scope

View File

@ -1,4 +1,4 @@
package com.anytypeio.anytype.di.feature.onboarding
package com.anytypeio.anytype.di.feature.onboarding.signup
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.di.common.ComponentDependencies
@ -9,8 +9,8 @@ import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.onboarding.signup.OnboardingSoulCreationAnimViewModel
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import dagger.Binds
import dagger.Component
import dagger.Module

View File

@ -1,6 +1,7 @@
package com.anytypeio.anytype.di.feature.onboarding
package com.anytypeio.anytype.di.feature.onboarding.signup
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.di.common.ComponentDependencies
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.repo.BlockRepository
@ -60,6 +61,7 @@ interface OnboardingSoulCreationDependencies : ComponentDependencies {
fun blockRepository(): BlockRepository
fun dispatchers(): AppCoroutineDispatchers
fun configStorage(): ConfigStorage
fun analytics(): Analytics
}
@Scope

View File

@ -1,6 +1,8 @@
package com.anytypeio.anytype.di.feature.onboarding.signup
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.CrashReporter
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.di.common.ComponentDependencies
import com.anytypeio.anytype.domain.auth.interactor.CreateAccount
@ -9,6 +11,7 @@ import com.anytypeio.anytype.domain.auth.repo.AuthRepository
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import com.anytypeio.anytype.domain.device.PathProvider
import com.anytypeio.anytype.domain.`object`.SetupMobileUseCaseSkip
import com.anytypeio.anytype.domain.platform.MetricsProvider
@ -93,6 +96,7 @@ object OnboardingVoidModule {
interface OnboardingVoidDependencies : ComponentDependencies {
fun authRepository(): AuthRepository
fun userSettings(): UserSettingsRepository
fun blockRepository(): BlockRepository
fun configStorage(): ConfigStorage
fun workspaceManager(): WorkspaceManager
@ -101,4 +105,6 @@ interface OnboardingVoidDependencies : ComponentDependencies {
fun pathProvider(): PathProvider
fun metricsProvider(): MetricsProvider
fun dispatchers(): AppCoroutineDispatchers
fun analytics(): Analytics
fun crashReporter(): CrashReporter
}

View File

@ -2,21 +2,32 @@ package com.anytypeio.anytype.di.feature.settings
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.device.share.debug.DebugSpaceDeviceFileContentSaver
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.debugging.DebugSpaceContentSaver
import com.anytypeio.anytype.domain.debugging.DebugSpaceShareDownloader
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider
import com.anytypeio.anytype.providers.DefaultUriFileProvider
import com.anytypeio.anytype.ui.settings.MainSettingFragment
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.Subcomponent
@Subcomponent(modules = [MainSettingsModule::class])
@Subcomponent(
modules = [
MainSettingsModule::class,
MainSettingsModule.Bindings::class
]
)
@PerScreen
interface MainSettingsSubComponent {
@ -70,13 +81,30 @@ object MainSettingsModule {
configStorage: ConfigStorage,
urlBuilder: UrlBuilder,
setObjectDetails: SetObjectDetails,
spaceGradientProvider: SpaceGradientProvider
spaceGradientProvider: SpaceGradientProvider,
debugSpaceShareDownloader: DebugSpaceShareDownloader
): MainSettingsViewModel.Factory = MainSettingsViewModel.Factory(
analytics,
storelessSubscriptionContainer,
configStorage,
urlBuilder,
setObjectDetails,
spaceGradientProvider
analytics = analytics,
storelessSubscriptionContainer = storelessSubscriptionContainer,
configStorage = configStorage,
urlBuilder = urlBuilder,
setObjectDetails = setObjectDetails,
spaceGradientProvider = spaceGradientProvider,
debugSpaceShareDownloader = debugSpaceShareDownloader
)
@Module
interface Bindings {
@PerScreen
@Binds
fun bindUriFileProvider(
defaultProvider: DefaultUriFileProvider
): UriFileProvider
@PerScreen
@Binds
fun bindSpaceDebugDeviceSharer(
saver: DebugSpaceDeviceFileContentSaver
): DebugSpaceContentSaver
}
}

View File

@ -1,6 +1,5 @@
package com.anytypeio.anytype.di.feature.settings
import android.content.Context
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.domain.account.DeleteAccount
@ -17,13 +16,8 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider
import com.anytypeio.anytype.providers.DefaultUriFileProvider
import com.anytypeio.anytype.ui.settings.ProfileFragment
import com.anytypeio.anytype.ui_settings.account.ProfileViewModel
import com.anytypeio.anytype.ui_settings.account.repo.DebugSpaceShareDownloader
import com.anytypeio.anytype.ui_settings.account.repo.FileSaver
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.Subcomponent
@ -49,7 +43,6 @@ object ProfileModule {
@PerScreen
fun provideViewModelFactory(
deleteAccount: DeleteAccount,
debugSpaceShareDownloader: DebugSpaceShareDownloader,
analytics: Analytics,
storelessSubscriptionContainer: StorelessSubscriptionContainer,
setObjectDetails: SetObjectDetails,
@ -59,7 +52,6 @@ object ProfileModule {
spaceGradientProvider: SpaceGradientProvider
): ProfileViewModel.Factory = ProfileViewModel.Factory(
deleteAccount = deleteAccount,
debugSpaceShareDownloader = debugSpaceShareDownloader,
analytics = analytics,
storelessSubscriptionContainer = storelessSubscriptionContainer,
setObjectDetails = setObjectDetails,
@ -80,22 +72,6 @@ object ProfileModule {
dispatchers: AppCoroutineDispatchers
): DebugSpace = DebugSpace(repo = repo, dispatchers = dispatchers)
@Provides
@PerScreen
fun provideFileSaver(
uriFileProvider: UriFileProvider,
context: Context,
dispatchers: AppCoroutineDispatchers,
): FileSaver = FileSaver(context, uriFileProvider, dispatchers)
@Provides
@PerScreen
fun providesDebugSyncShareDownloader(
debugSpace: DebugSpace,
fileSaver: FileSaver,
dispatchers: AppCoroutineDispatchers,
): DebugSpaceShareDownloader = DebugSpaceShareDownloader(debugSpace, fileSaver, dispatchers)
@JvmStatic
@PerScreen
@Provides
@ -149,11 +125,6 @@ object ProfileModule {
@Module
interface Bindings {
@PerScreen
@Binds
fun bindUriFileProvider(
defaultProvider: DefaultUriFileProvider
): UriFileProvider
// Add bindings if needed
}
}

View File

@ -6,6 +6,7 @@ import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import com.anytypeio.anytype.app.DefaultAppActionManager
import com.anytypeio.anytype.app.DefaultMetricsProvider
import com.anytypeio.anytype.core_utils.tools.ThreadInfo
import com.anytypeio.anytype.data.auth.config.DefaultFeaturesConfigProvider
import com.anytypeio.anytype.data.auth.repo.AuthCache
import com.anytypeio.anytype.data.auth.repo.AuthCacheDataStore
@ -216,8 +217,15 @@ object DataModule {
service: MiddlewareService,
factory: MiddlewareFactory,
logger: MiddlewareProtobufLogger,
protobufConverter: ProtobufConverterProvider
): Middleware = Middleware(service, factory, logger, protobufConverter)
protobufConverter: ProtobufConverterProvider,
threadInfo: ThreadInfo
): Middleware = Middleware(
service = service,
factory = factory,
logger = logger,
protobufConverter = protobufConverter,
threadInfo = threadInfo
)
@JvmStatic
@Provides

View File

@ -21,12 +21,13 @@ import com.anytypeio.anytype.di.feature.SplashDependencies
import com.anytypeio.anytype.di.feature.auth.DeletedAccountDependencies
import com.anytypeio.anytype.di.feature.home.HomeScreenDependencies
import com.anytypeio.anytype.di.feature.library.LibraryDependencies
import com.anytypeio.anytype.di.feature.onboarding.OnboardingLoginSetupDependencies
import com.anytypeio.anytype.di.feature.onboarding.OnboardingMnemonicDependencies
import com.anytypeio.anytype.di.feature.onboarding.OnboardingMnemonicLoginDependencies
import com.anytypeio.anytype.di.feature.onboarding.OnboardingSoulCreationAnimDependencies
import com.anytypeio.anytype.di.feature.onboarding.OnboardingSoulCreationDependencies
import com.anytypeio.anytype.di.feature.onboarding.OnboardingDependencies
import com.anytypeio.anytype.di.feature.onboarding.OnboardingStartDependencies
import com.anytypeio.anytype.di.feature.onboarding.login.OnboardingLoginSetupDependencies
import com.anytypeio.anytype.di.feature.onboarding.login.OnboardingMnemonicLoginDependencies
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingMnemonicDependencies
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingSoulCreationAnimDependencies
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingSoulCreationDependencies
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingVoidDependencies
import com.anytypeio.anytype.di.feature.relations.RelationCreateFromLibraryDependencies
import com.anytypeio.anytype.di.feature.relations.RelationEditDependencies
@ -84,6 +85,7 @@ interface MainComponent :
MigrationErrorDependencies,
BacklinkOrAddToObjectDependencies,
FilesStorageDependencies,
OnboardingDependencies,
OnboardingStartDependencies,
OnboardingVoidDependencies,
OnboardingMnemonicDependencies,
@ -198,6 +200,11 @@ private abstract class ComponentDependenciesModule private constructor() {
@ComponentDependenciesKey(FilesStorageDependencies::class)
abstract fun provideFilesStorageDependencies(component: MainComponent): ComponentDependencies
@Binds
@IntoMap
@ComponentDependenciesKey(OnboardingDependencies::class)
abstract fun provideOnboardingDependencies(component: MainComponent): ComponentDependencies
@Binds
@IntoMap
@ComponentDependenciesKey(OnboardingStartDependencies::class)

View File

@ -4,12 +4,14 @@ import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import com.anytypeio.anytype.BuildConfig
import com.anytypeio.anytype.app.TogglePrefs
import com.anytypeio.anytype.app.DefaultFeatureToggles
import com.anytypeio.anytype.app.TogglePrefs
import com.anytypeio.anytype.core_utils.tools.AppInfo
import com.anytypeio.anytype.core_utils.tools.DefaultAppInfo
import com.anytypeio.anytype.core_utils.tools.DefaultThreadInfo
import com.anytypeio.anytype.core_utils.tools.DefaultUrlValidator
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
import com.anytypeio.anytype.core_utils.tools.ThreadInfo
import com.anytypeio.anytype.core_utils.tools.UrlValidator
import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.misc.UrlBuilder
@ -44,6 +46,10 @@ object UtilModule {
@Module
interface Bindings {
@Binds
@Singleton
fun bindThreadInfo(info: DefaultThreadInfo): ThreadInfo
@Binds
@Singleton
fun bindUrlValidator(applicator: DefaultUrlValidator): UrlValidator

View File

@ -7,17 +7,18 @@ import androidx.navigation.navOptions
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.presentation.navigation.AppNavigation
import com.anytypeio.anytype.presentation.settings.EditorSettings
import com.anytypeio.anytype.presentation.widgets.collection.Subscription
import com.anytypeio.anytype.ui.auth.Keys
import com.anytypeio.anytype.ui.auth.account.CreateAccountFragment.Companion.ARGS_CODE
import com.anytypeio.anytype.ui.auth.account.DeletedAccountFragment
import com.anytypeio.anytype.ui.editor.EditorFragment
import com.anytypeio.anytype.ui.home.HomeScreenFragment
import com.anytypeio.anytype.ui.search.ObjectSearchFragment
import com.anytypeio.anytype.ui.sets.ObjectSetFragment
import com.anytypeio.anytype.ui.settings.RemoteStorageFragment
import com.anytypeio.anytype.ui.templates.TemplateSelectFragment
import com.anytypeio.anytype.ui.widgets.collection.CollectionFragment
import timber.log.Timber
class Navigator : AppNavigation {
@ -40,10 +41,6 @@ class Navigator : AppNavigation {
navController?.navigate(R.id.action_global_desktopScreen, bundle)
}
override fun startLogin() {
navController?.navigate(R.id.action_splashFragment_to_login_nav)
}
override fun createProfile(invitationCode: String) {
val bundle = bundleOf(ARGS_CODE to invitationCode)
navController?.navigate(R.id.action_invitationFragment_to_createAccountScreen, bundle)
@ -76,17 +73,18 @@ class Navigator : AppNavigation {
override fun workspace() {}
override fun openSettings() {
navController?.navigate(R.id.action_open_settings)
try {
navController?.navigate(R.id.action_open_settings)
} catch (e: Exception) {
Timber.e(e, "Error while opening settings")
}
}
override fun openDocument(id: String, editorSettings: EditorSettings?) {
override fun openDocument(id: String) {
navController?.navigate(
R.id.objectNavigation,
Bundle().apply {
putString(EditorFragment.ID_KEY, id)
editorSettings?.let {
putParcelable(EditorFragment.DEBUG_SETTINGS, it)
}
}
)
}
@ -97,6 +95,9 @@ class Navigator : AppNavigation {
bundleOf(EditorFragment.ID_KEY to id),
navOptions {
launchSingleTop = true
popUpTo(R.id.pageSearchFragment) {
inclusive = true
}
}
)
}
@ -114,6 +115,9 @@ class Navigator : AppNavigation {
bundleOf(ObjectSetFragment.CONTEXT_ID_KEY to id),
navOptions {
launchSingleTop = true
popUpTo(R.id.pageSearchFragment) {
inclusive = true
}
}
)
}
@ -189,8 +193,11 @@ class Navigator : AppNavigation {
navController?.navigate(R.id.action_profileScreen_to_debugSettingsFragment)
}
override fun openPageSearch() {
navController?.navigate(R.id.pageSearchFragment)
override fun openPageSearch(ctx: Id?) {
navController?.navigate(
R.id.pageSearchFragment,
bundleOf(ObjectSearchFragment.CTX_KEY to ctx)
)
}
override fun exitToDesktopAndOpenPage(pageId: String) {
@ -254,6 +261,14 @@ class Navigator : AppNavigation {
navController?.navigate(R.id.migrationNeededScreen)
}
override fun exitFromMigrationScreen() {
navController?.navigate(R.id.onboarding_nav, null, navOptions {
popUpTo(R.id.migrationNeededScreen) {
inclusive = true
}
})
}
override fun openTemplates(
ctx: Id,
type: String,

View File

@ -9,68 +9,80 @@ class NavigationRouter(
) {
fun navigate(command: AppNavigation.Command) {
Timber.d("Navigate to $command")
try {
when (command) {
is AppNavigation.Command.StartSplashFromDesktop -> navigation.startSplashFromDesktop()
is AppNavigation.Command.StartDesktopFromLogin -> navigation.startDesktopFromLogin()
is AppNavigation.Command.StartDesktopFromSignUp -> navigation.startDesktopFromSignUp()
is AppNavigation.Command.StartDesktopFromSplash -> navigation.startDesktopFromSplash()
is AppNavigation.Command.ExitFromMigrationScreen -> navigation.exitFromMigrationScreen()
is AppNavigation.Command.OpenCreateAccount -> navigation.createProfile(command.invitationCode)
is AppNavigation.Command.ChoosePinCodeScreen -> navigation.choosePinCode()
is AppNavigation.Command.EnterKeyChainScreen -> navigation.enterKeychain()
is AppNavigation.Command.SelectAccountScreen -> navigation.chooseAccount()
is AppNavigation.Command.WorkspaceScreen -> navigation.workspace()
is AppNavigation.Command.InvitationCodeScreen -> navigation.enterInvitationCode()
is AppNavigation.Command.AboutAnalyticsScreen -> navigation.aboutAnalyticsScreen()
is AppNavigation.Command.ExitToInvitationCodeScreen -> navigation.exitToInvitationCodeScreen()
is AppNavigation.Command.SetupNewAccountScreen -> navigation.setupNewAccount()
is AppNavigation.Command.SetupSelectedAccountScreen -> navigation.setupSelectedAccount(
command.id
)
when (command) {
is AppNavigation.Command.StartSplashFromDesktop -> navigation.startSplashFromDesktop()
is AppNavigation.Command.StartDesktopFromLogin -> navigation.startDesktopFromLogin()
is AppNavigation.Command.StartDesktopFromSignUp -> navigation.startDesktopFromSignUp()
is AppNavigation.Command.StartDesktopFromSplash -> navigation.startDesktopFromSplash()
is AppNavigation.Command.OpenStartLoginScreen -> navigation.startLogin()
is AppNavigation.Command.OpenCreateAccount -> navigation.createProfile(command.invitationCode)
is AppNavigation.Command.ChoosePinCodeScreen -> navigation.choosePinCode()
is AppNavigation.Command.EnterKeyChainScreen -> navigation.enterKeychain()
is AppNavigation.Command.SelectAccountScreen -> navigation.chooseAccount()
is AppNavigation.Command.WorkspaceScreen -> navigation.workspace()
is AppNavigation.Command.InvitationCodeScreen -> navigation.enterInvitationCode()
is AppNavigation.Command.AboutAnalyticsScreen -> navigation.aboutAnalyticsScreen()
is AppNavigation.Command.ExitToInvitationCodeScreen -> navigation.exitToInvitationCodeScreen()
is AppNavigation.Command.SetupNewAccountScreen -> navigation.setupNewAccount()
is AppNavigation.Command.SetupSelectedAccountScreen -> navigation.setupSelectedAccount(
command.id
)
is AppNavigation.Command.ConfirmPinCodeScreen -> navigation.confirmPinCode(command.code)
is AppNavigation.Command.OpenSettings -> navigation.openSettings()
is AppNavigation.Command.OpenObject -> navigation.openDocument(
command.id,
command.editorSettings
)
is AppNavigation.Command.OpenSetOrCollection -> navigation.openObjectSet(
command.target,
command.isPopUpToDashboard
)
is AppNavigation.Command.LaunchObjectSet -> navigation.launchObjectSet(command.target)
is AppNavigation.Command.LaunchDocument -> navigation.launchDocument(command.id)
is AppNavigation.Command.LaunchObjectFromSplash -> navigation.launchObjectFromSplash(
command.target
)
is AppNavigation.Command.LaunchObjectSetFromSplash -> navigation.launchObjectSetFromSplash(
command.target
)
is AppNavigation.Command.OpenDatabaseViewAddView -> navigation.openDatabaseViewAddView()
is AppNavigation.Command.OpenEditDatabase -> navigation.openEditDatabase()
is AppNavigation.Command.OpenKeychainScreen -> navigation.openKeychainScreen()
is AppNavigation.Command.OpenUserSettingsScreen -> navigation.openUserSettingsScreen()
is AppNavigation.Command.OpenContactsScreen -> navigation.openContacts()
is AppNavigation.Command.OpenSwitchDisplayView -> navigation.openSwitchDisplayView()
is AppNavigation.Command.OpenCustomizeDisplayView -> navigation.openCustomizeDisplayView()
is AppNavigation.Command.Exit -> navigation.exit()
is AppNavigation.Command.ExitToDesktop -> navigation.exitToDesktop()
is AppNavigation.Command.OpenDebugSettingsScreen -> navigation.openDebugSettings()
is AppNavigation.Command.ExitToDesktopAndOpenPage -> navigation.exitToDesktopAndOpenPage(
command.pageId
)
is AppNavigation.Command.OpenPageSearch -> navigation.openPageSearch()
is AppNavigation.Command.OpenUpdateAppScreen -> navigation.openUpdateAppScreen()
is AppNavigation.Command.DeletedAccountScreen -> navigation.deletedAccountScreen(command.deadline)
is AppNavigation.Command.OpenTemplates -> navigation.openTemplates(
ctx = command.ctx,
type = command.type,
templates = command.templates
)
is AppNavigation.Command.OpenLibrary -> navigation.openLibrary()
is AppNavigation.Command.MigrationErrorScreen -> navigation.migrationErrorScreen()
is AppNavigation.Command.OpenRemoteStorageScreen -> navigation.openRemoteStorageScreen(command.subscription)
else -> Timber.d("Nav command ignored: $command")
is AppNavigation.Command.ConfirmPinCodeScreen -> navigation.confirmPinCode(command.code)
is AppNavigation.Command.OpenSettings -> navigation.openSettings()
is AppNavigation.Command.OpenObject -> navigation.openDocument(command.id)
is AppNavigation.Command.OpenSetOrCollection -> navigation.openObjectSet(
command.target,
command.isPopUpToDashboard
)
is AppNavigation.Command.LaunchObjectSet -> navigation.launchObjectSet(command.target)
is AppNavigation.Command.LaunchDocument -> navigation.launchDocument(command.id)
is AppNavigation.Command.LaunchObjectFromSplash -> navigation.launchObjectFromSplash(
command.target
)
is AppNavigation.Command.LaunchObjectSetFromSplash -> navigation.launchObjectSetFromSplash(
command.target
)
is AppNavigation.Command.OpenDatabaseViewAddView -> navigation.openDatabaseViewAddView()
is AppNavigation.Command.OpenEditDatabase -> navigation.openEditDatabase()
is AppNavigation.Command.OpenKeychainScreen -> navigation.openKeychainScreen()
is AppNavigation.Command.OpenUserSettingsScreen -> navigation.openUserSettingsScreen()
is AppNavigation.Command.OpenContactsScreen -> navigation.openContacts()
is AppNavigation.Command.OpenSwitchDisplayView -> navigation.openSwitchDisplayView()
is AppNavigation.Command.OpenCustomizeDisplayView -> navigation.openCustomizeDisplayView()
is AppNavigation.Command.Exit -> navigation.exit()
is AppNavigation.Command.ExitToDesktop -> navigation.exitToDesktop()
is AppNavigation.Command.OpenDebugSettingsScreen -> navigation.openDebugSettings()
is AppNavigation.Command.ExitToDesktopAndOpenPage -> navigation.exitToDesktopAndOpenPage(
command.pageId
)
is AppNavigation.Command.OpenPageSearch -> navigation.openPageSearch(command.ctx)
is AppNavigation.Command.OpenUpdateAppScreen -> navigation.openUpdateAppScreen()
is AppNavigation.Command.DeletedAccountScreen -> navigation.deletedAccountScreen(
command.deadline
)
is AppNavigation.Command.OpenTemplates -> navigation.openTemplates(
ctx = command.ctx,
type = command.type,
templates = command.templates
)
is AppNavigation.Command.OpenLibrary -> navigation.openLibrary()
is AppNavigation.Command.MigrationErrorScreen -> navigation.migrationErrorScreen()
is AppNavigation.Command.OpenRemoteStorageScreen -> navigation.openRemoteStorageScreen(
command.subscription
)
else -> Timber.d("Nav command ignored: $command")
}
} catch (e: Exception) {
Timber.e(e, "Error while navigation")
}
}
}

View File

@ -62,6 +62,7 @@ import com.anytypeio.anytype.core_ui.menu.ObjectTypePopupMenu
import com.anytypeio.anytype.core_ui.reactive.clicks
import com.anytypeio.anytype.core_ui.tools.ClipboardInterceptor
import com.anytypeio.anytype.core_ui.tools.EditorHeaderOverlayDetector
import com.anytypeio.anytype.core_ui.tools.LastItemBottomOffsetDecorator
import com.anytypeio.anytype.core_ui.tools.MarkupColorToolbarFooter
import com.anytypeio.anytype.core_ui.tools.MentionFooterItemDecorator
import com.anytypeio.anytype.core_ui.tools.NoteHeaderItemDecorator
@ -85,6 +86,7 @@ import com.anytypeio.anytype.core_utils.ext.gone
import com.anytypeio.anytype.core_utils.ext.hide
import com.anytypeio.anytype.core_utils.ext.hideSoftInput
import com.anytypeio.anytype.core_utils.ext.invisible
import com.anytypeio.anytype.core_utils.ext.lastDecorator
import com.anytypeio.anytype.core_utils.ext.safeNavigate
import com.anytypeio.anytype.core_utils.ext.screen
import com.anytypeio.anytype.core_utils.ext.show
@ -245,6 +247,12 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
)
}
private val defaultBottomOffsetDecorator by lazy {
LastItemBottomOffsetDecorator(
dimen(R.dimen.dp_48)
)
}
private val footerMentionDecorator by lazy { MentionFooterItemDecorator(screen) }
private val noteHeaderDecorator by lazy {
NoteHeaderItemDecorator(offset = dimen(R.dimen.default_note_title_offset))
@ -503,6 +511,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
itemAnimator = null
adapter = blockAdapter
addOnScrollListener(titleVisibilityDetector)
addItemDecoration(defaultBottomOffsetDecorator)
}
binding.toolbar.apply {
@ -1376,7 +1385,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
binding.styleToolbarMain.setSelectedStyle(this.state)
if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
keyboardDelayJobs += lifecycleScope.launch {
if (binding.recycler.itemDecorationCount == 0) {
if (binding.recycler.lastDecorator() == defaultBottomOffsetDecorator) {
binding.recycler.addItemDecoration(styleToolbarFooter)
}
proceedWithHidingSoftInput()
@ -1441,7 +1450,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
}
if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
keyboardDelayJobs += lifecycleScope.launch {
if (binding.recycler.itemDecorationCount == 0) {
if (binding.recycler.lastDecorator() == defaultBottomOffsetDecorator) {
binding.recycler.addItemDecoration(styleToolbarFooter)
}
proceedWithHidingSoftInput()
@ -1490,11 +1499,6 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
}
} else {
if (binding.slashWidget.isVisible) {
applySlideTransition(
binding.slashWidget,
SLASH_HIDE_ANIM_DURATION,
binding.sheet
)
binding.slashWidget.gone()
}
binding.recycler.removeItemDecoration(slashWidgetFooter)
@ -1538,7 +1542,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
)
if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
keyboardDelayJobs += lifecycleScope.launch {
if (binding.recycler.itemDecorationCount == 0) {
if (binding.recycler.lastDecorator() == defaultBottomOffsetDecorator) {
binding.recycler.addItemDecoration(styleToolbarFooter)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
@ -1770,15 +1774,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
}
private fun enterScrollAndMove() {
if (binding.recycler.itemDecorationCount == 0 || binding.recycler.getItemDecorationAt(0) !is ScrollAndMoveTargetHighlighter) {
// val offset = recycler.computeVerticalScrollOffset()
//
// lifecycleScope.launch {
// recycler.layoutChanges().take(1).collect {
// if (offset < screen.y / 3) recycler.scrollBy(0, screen.y / 3)
// }
// }
if (binding.recycler.lastDecorator() !is ScrollAndMoveTargetHighlighter) {
binding.recycler.addItemDecoration(scrollAndMoveTargetHighlighter)
@ -2100,7 +2096,6 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
companion object {
const val ID_KEY = "id"
const val DEBUG_SETTINGS = "debug_settings"
const val DEFAULT_ANIM_DURATION = 150L
const val DEFAULT_DELAY_BLOCK_ACTION_TOOLBAR = 100L

View File

@ -53,23 +53,26 @@ abstract class SelectCoverGalleryFragment :
)
}
val getContent = registerForActivityResult(GetImageContract()) { uri: Uri? ->
if (uri != null) {
try {
val path = uri.parseImagePath(requireContext())
vm.onImagePicked(ctx, path)
} catch (e: Exception) {
toast("Error while parsing path for cover image")
Timber.d(e, "Error while parsing path for cover image")
}
} else {
toast("Error while upload cover image, URI is null")
Timber.e("Error while upload cover image, URI is null")
}
private val getContent = try { getContentLauncher() } catch (e: Exception) {
null
}
private val permissionReadStorage =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { grantResults ->
private fun getContentLauncher() = registerForActivityResult(GetImageContract()) { uri: Uri? ->
if (uri != null) {
try {
val path = uri.parseImagePath(requireContext())
vm.onImagePicked(ctx, path)
} catch (e: Exception) {
toast("Error while parsing path for cover image")
Timber.d(e, "Error while parsing path for cover image")
}
} else {
toast("Error while upload cover image, URI is null")
Timber.e("Error while upload cover image, URI is null")
}
}
private val permissionReadStorage = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { grantResults ->
val readResult = grantResults[Manifest.permission.READ_EXTERNAL_STORAGE]
if (readResult == true) {
openGallery()
@ -159,7 +162,12 @@ abstract class SelectCoverGalleryFragment :
}
private fun openGallery() {
getContent.launch(SELECT_IMAGE_CODE)
try {
getContent?.launch(SELECT_IMAGE_CODE)
} catch (e: Exception) {
Timber.e(e, "Error while opening gallery")
toast("Error while opening gallery: ${e.message}")
}
}
private fun hasReadStoragePermission(): Boolean = ContextCompat.checkSelfPermission(

View File

@ -21,6 +21,7 @@ import com.anytypeio.anytype.analytics.base.EventsDictionary
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_ui.features.navigation.DefaultObjectViewAdapter
import com.anytypeio.anytype.core_ui.layout.SpacingItemDecoration
import com.anytypeio.anytype.core_utils.ext.arg
import com.anytypeio.anytype.core_utils.ext.drawable
import com.anytypeio.anytype.core_utils.ext.hideSoftInput
@ -216,6 +217,11 @@ class BacklinkOrAddToObjectFragment :
setDrawable(drawable(R.drawable.divider_object_search))
}
)
addItemDecoration(
SpacingItemDecoration(
lastItemSpacingBottom = resources.getDimension(R.dimen.dp_120).toInt()
)
)
}
}

View File

@ -20,6 +20,7 @@ import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.EventsDictionary
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_ui.features.navigation.DefaultObjectViewAdapter
import com.anytypeio.anytype.core_ui.layout.SpacingItemDecoration
import com.anytypeio.anytype.core_utils.ext.arg
import com.anytypeio.anytype.core_utils.ext.argOrNull
import com.anytypeio.anytype.core_utils.ext.drawable
@ -224,6 +225,11 @@ class LinkToObjectFragment : BaseBottomSheetTextInputFragment<FragmentObjectSear
setDrawable(drawable(R.drawable.divider_object_search))
}
)
addItemDecoration(
SpacingItemDecoration(
lastItemSpacingBottom = resources.getDimension(R.dimen.dp_120).toInt()
)
)
}
}

View File

@ -37,7 +37,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.ColorMnemonicPhrase
import com.anytypeio.anytype.core_ui.ColorMnemonicStub
import com.anytypeio.anytype.core_ui.ColorPagerIndicator
import com.anytypeio.anytype.core_ui.ColorPagerIndicatorCurrent
@ -45,6 +44,7 @@ import com.anytypeio.anytype.core_ui.ColorPagerIndicatorText
import com.anytypeio.anytype.core_ui.ColorPlaceholderText
import com.anytypeio.anytype.core_ui.ColorTextInput
import com.anytypeio.anytype.core_ui.ColorTextInputCursor
import com.anytypeio.anytype.core_ui.MnemonicPhrasePaletteColors
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.core_ui.views.HeadlineOnBoardingDescription
import com.anytypeio.anytype.core_ui.views.PreviewTitle1Regular
@ -110,20 +110,29 @@ fun PagerIndicator(
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun MnemonicPhraseWidget(
modifier: Modifier = Modifier, mnemonic: String
modifier: Modifier = Modifier,
mnemonic: String,
mnemonicColorPalette: List<Color>
) {
val words = mnemonic.split(" ")
FlowRow(
modifier = modifier, maxItemsInEachRow = 4, horizontalArrangement = Arrangement.Center
) {
words.forEach { word ->
Text(
modifier = Modifier.padding(horizontal = 3.dp),
text = word.replaceFirst(word.first(), word.first().uppercaseChar()),
style = PreviewTitle1Regular.copy(
color = ColorMnemonicPhrase
if (words.isNotEmpty()) {
FlowRow(
modifier = modifier, maxItemsInEachRow = 4, horizontalArrangement = Arrangement.Center
) {
words.forEachIndexed { idx, word ->
val color = if (idx <= mnemonicColorPalette.lastIndex) {
mnemonicColorPalette[idx]
} else {
MnemonicPhrasePaletteColors.random()
}
Text(
modifier = Modifier.padding(horizontal = 6.dp, vertical = 3.dp),
text = word.lowercase(),
style = PreviewTitle1Regular.copy(
color = color
)
)
)
}
}
}
}
@ -185,7 +194,10 @@ fun MnemonicStubPreview() {
@Composable
fun MnemonicPhraseWidgetPreview() {
val mnemonic = "kenobi hello there general grievous you are bold like toe pineapple wave"
MnemonicPhraseWidget(mnemonic = mnemonic)
MnemonicPhraseWidget(
mnemonic = mnemonic,
mnemonicColorPalette = emptyList()
)
}
@Composable

View File

@ -3,7 +3,6 @@ package com.anytypeio.anytype.ui.onboarding
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.net.Uri
import android.os.Build
@ -11,12 +10,12 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import androidx.activity.compose.BackHandler
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.compose.animation.AnimatedContentScope.SlideDirection.Companion.Left
import androidx.compose.animation.AnimatedContentScope.SlideDirection.Companion.Right
import androidx.compose.animation.ExperimentalAnimationApi
@ -25,9 +24,12 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
@ -42,18 +44,26 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.navigation.NavHostController
import androidx.navigation.fragment.findNavController
import androidx.navigation.navOptions
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.BuildConfig
import com.anytypeio.anytype.core_ui.MNEMONIC_WORD_COUNT
import com.anytypeio.anytype.core_ui.MnemonicPhrasePaletteColors
import com.anytypeio.anytype.core_ui.views.BaseAlertDialog
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.insets.RootViewDeferringInsetsCallback
import com.anytypeio.anytype.di.common.ComponentManager
@ -61,6 +71,7 @@ import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.ext.daggerViewModel
import com.anytypeio.anytype.presentation.onboarding.OnboardingStartViewModel
import com.anytypeio.anytype.presentation.onboarding.OnboardingStartViewModel.SideEffect
import com.anytypeio.anytype.presentation.onboarding.OnboardingViewModel
import com.anytypeio.anytype.presentation.onboarding.login.OnboardingLoginSetupViewModel
import com.anytypeio.anytype.presentation.onboarding.login.OnboardingMnemonicLoginViewModel
import com.anytypeio.anytype.presentation.onboarding.signup.OnboardingSoulCreationViewModel
@ -85,11 +96,35 @@ import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
import com.google.android.exoplayer2.upstream.RawResourceDataSource
import com.google.android.exoplayer2.util.Util
import com.google.zxing.integration.android.IntentIntegrator
import javax.inject.Inject
import kotlinx.coroutines.delay
import timber.log.Timber
class OnboardingFragment : Fragment() {
@OptIn(ExperimentalAnimationApi::class)
@Inject
lateinit var factory: OnboardingViewModel.Factory
private val onBoardingViewModel by viewModels<OnboardingViewModel> { factory }
private val mnemonicColorPalette by lazy {
buildList {
repeat(MNEMONIC_WORD_COUNT) {
add(MnemonicPhrasePaletteColors.random())
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
injectDependencies()
}
override fun onDestroy() {
super.onDestroy()
releaseDependencies()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -98,24 +133,50 @@ class OnboardingFragment : Fragment() {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme {
val navController = rememberAnimatedNavController()
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
) {
val currentPage = remember { mutableStateOf(OnboardingPage.AUTH) }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
BackgroundCircle()
}
Onboarding(currentPage, navController)
PagerIndicator(
pageCount = OnboardingPage.values().filter { it.visible }.size,
page = currentPage,
onBackClick = { navController.popBackStack() }
)
OnboardingScreen()
}
}
}
@Composable
@OptIn(ExperimentalAnimationApi::class)
private fun OnboardingScreen() {
MaterialTheme {
val navController = rememberAnimatedNavController()
val defaultBackCallback: BackButtonCallback = { navController.popBackStack() }
val signUpBackButtonCallback = remember {
mutableStateOf(defaultBackCallback)
}
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
) {
val currentPage = remember { mutableStateOf(OnboardingPage.AUTH) }
BackgroundCircle()
Onboarding(
currentPage = currentPage,
navController = navController,
backButtonCallback = signUpBackButtonCallback
)
PagerIndicator(
pageCount = OnboardingPage.values().filter { it.visible }.size,
page = currentPage,
onBackClick = {
signUpBackButtonCallback.value?.invoke()
}
)
}
LaunchedEffect(Unit) {
onBoardingViewModel.toasts.collect {
toast(it)
}
}
DisposableEffect(
viewLifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.DESTROYED)
) {
onDispose {
signUpBackButtonCallback.value = null
}
}
}
@ -126,6 +187,14 @@ class OnboardingFragment : Fragment() {
onApplyWindowRootInsets(view)
}
override fun onDestroyView() {
super.onDestroyView()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
// Disabling workaround to prevent background circle with video shrinking.
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
}
}
private fun onApplyWindowRootInsets(view: View) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val deferringInsetsListener = RootViewDeferringInsetsCallback(
@ -135,12 +204,19 @@ class OnboardingFragment : Fragment() {
ViewCompat.setWindowInsetsAnimationCallback(view, deferringInsetsListener)
ViewCompat.setOnApplyWindowInsetsListener(view, deferringInsetsListener)
} else {
// Enabling workaround to prevent background circle with video shrinking.
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
}
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun Onboarding(currentPage: MutableState<OnboardingPage>, navController: NavHostController) {
private fun Onboarding(
currentPage: MutableState<OnboardingPage>,
backButtonCallback: MutableState<BackButtonCallback>,
navController: NavHostController
) {
AnimatedNavHost(navController, startDestination = OnboardingNavigation.auth) {
composable(
route = OnboardingNavigation.auth,
@ -188,25 +264,25 @@ class OnboardingFragment : Fragment() {
}
) {
currentPage.value = OnboardingPage.VOID
val component = componentManager().onboardingNewVoidComponent.ReleaseOn(
viewLifecycleOwner = viewLifecycleOwner,
state = Lifecycle.State.DESTROYED
)
val component = componentManager().onboardingNewVoidComponent
val vm = daggerViewModel { component.get().getViewModel() }
VoidScreenWrapper(
contentPaddingTop = ContentPaddingTop(),
onNextClicked = vm::onNextClicked,
screenState = vm.state.collectAsState().value
)
BackHandler {
vm.onSystemBackPressed()
}
backButtonCallback.value = vm::onBackButtonPressed
BackHandler { vm.onSystemBackPressed() }
LaunchedEffect(Unit) {
vm.navigation.collect { navigation ->
when(navigation) {
when (navigation) {
OnboardingVoidViewModel.Navigation.GoBack -> {
navController.popBackStack()
}
OnboardingVoidViewModel.Navigation.NavigateToMnemonic -> {
navController.navigate(OnboardingNavigation.mnemonic)
}
@ -216,6 +292,9 @@ class OnboardingFragment : Fragment() {
LaunchedEffect(Unit) {
vm.toasts.collect { toast(it) }
}
DisposableEffect(Unit) {
onDispose { component.release() }
}
}
composable(
route = OnboardingNavigation.mnemonic,
@ -224,6 +303,7 @@ class OnboardingFragment : Fragment() {
OnboardingNavigation.void -> {
slideIntoContainer(Left, tween(ANIMATION_LENGTH_SLIDE))
}
else -> {
slideIntoContainer(Right, tween(ANIMATION_LENGTH_SLIDE))
}
@ -234,6 +314,7 @@ class OnboardingFragment : Fragment() {
OnboardingNavigation.void -> {
slideOutOfContainer(Right, tween(ANIMATION_LENGTH_SLIDE))
}
else -> {
slideOutOfContainer(Left, tween(ANIMATION_LENGTH_SLIDE))
}
@ -241,7 +322,12 @@ class OnboardingFragment : Fragment() {
}
) {
currentPage.value = OnboardingPage.MNEMONIC
Mnemonic(navController, ContentPaddingTop())
backButtonCallback.value = { navController.popBackStack() }
Mnemonic(
navController = navController,
contentPaddingTop = ContentPaddingTop(),
mnemonicColorPalette = mnemonicColorPalette
)
}
composable(
route = OnboardingNavigation.createSoul,
@ -251,6 +337,7 @@ class OnboardingFragment : Fragment() {
OnboardingNavigation.mnemonic -> {
slideOutOfContainer(Right, tween(ANIMATION_LENGTH_SLIDE))
}
else -> {
fadeOut(tween(ANIMATION_LENGTH_FADE))
}
@ -258,6 +345,7 @@ class OnboardingFragment : Fragment() {
}
) {
currentPage.value = OnboardingPage.SOUL_CREATION
backButtonCallback.value = { navController.popBackStack() }
CreateSoul(
navController = navController,
contentPaddingTop = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
@ -273,9 +361,10 @@ class OnboardingFragment : Fragment() {
}
) {
currentPage.value = OnboardingPage.SOUL_CREATION_ANIM
backButtonCallback.value = null
CreateSoulAnimation(ContentPaddingTop())
BackHandler {
Timber.d("OnBackHandler")
// Intercepting back-press event but doing nothing.
}
}
composable(
@ -296,13 +385,12 @@ class OnboardingFragment : Fragment() {
@Composable
private fun ContentPaddingTop() = LocalConfiguration.current.screenHeightDp * 2 / 6
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun Recovery(navController: NavHostController) {
val component = componentManager().onboardingMnemonicLoginComponent.ReleaseOn(
viewLifecycleOwner = viewLifecycleOwner,
state = Lifecycle.State.DESTROYED
)
val component = componentManager().onboardingMnemonicLoginComponent
val vm = daggerViewModel { component.get().getViewModel() }
val isQrWarningDialogVisible = remember { mutableStateOf(false) }
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
@ -317,46 +405,57 @@ class OnboardingFragment : Fragment() {
vm = vm,
onBackClicked = vm::onBackButtonPressed,
onScanQrClick = {
AlertDialog.Builder(requireContext())
.setMessage(R.string.alert_qr_camera)
.setPositiveButton(R.string.alert_qr_camera_ok) { dialog, _ ->
proceedWithQrCodeActivity(launcher, dialog)
}
.setCancelable(true)
.show()
}
isQrWarningDialogVisible.value = true
vm.onScanQrCodeClicked()
},
)
LaunchedEffect(Unit) {
vm.sideEffects.collect { effect ->
when(effect) {
when (effect) {
is OnboardingMnemonicLoginViewModel.SideEffect.Exit -> {
navController.popBackStack()
val lastDestination = navController.backQueue.lastOrNull()
// TODO Temporary workaround to prevent inconsistent state in navigation
if (lastDestination?.destination?.route == OnboardingNavigation.recovery) {
navController.popBackStack()
}
}
is OnboardingMnemonicLoginViewModel.SideEffect.ProceedWithLogin -> {
navController.navigate(OnboardingNavigation.enterTheVoid)
}
is OnboardingMnemonicLoginViewModel.SideEffect.Error -> {
toast(effect.msg)
}
}
}
}
if (isQrWarningDialogVisible.value) {
BaseAlertDialog(
dialogText = stringResource(id = R.string.alert_qr_camera),
buttonText = stringResource(id = R.string.alert_qr_camera_ok),
onButtonClick = {
isQrWarningDialogVisible.value = false
proceedWithQrCodeActivity(launcher)
},
onDismissRequest = { isQrWarningDialogVisible.value = false }
)
}
DisposableEffect(Unit) {
onDispose { component.release() }
}
}
private fun proceedWithQrCodeActivity(
launcher: ManagedActivityResultLauncher<Intent, ActivityResult>,
dialog: DialogInterface
) {
private fun proceedWithQrCodeActivity(launcher: ManagedActivityResultLauncher<Intent, ActivityResult>) {
try {
launcher.launch(
IntentIntegrator
.forSupportFragment(this)
.setBeepEnabled(false)
.createScanIntent()
).also {
dialog.dismiss()
}
)
} catch (e: Exception) {
toast("Error while scanning QR code")
Timber.e(e, "Error while scanning QR code")
}
}
@ -365,10 +464,7 @@ class OnboardingFragment : Fragment() {
private fun enterTheVoid(
navController: NavHostController
) {
val component = componentManager().onboardingLoginSetupComponent.ReleaseOn(
viewLifecycleOwner = viewLifecycleOwner,
state = Lifecycle.State.DESTROYED
)
val component = componentManager().onboardingLoginSetupComponent
val vm = daggerViewModel { component.get().getViewModel() }
EnteringTheVoidScreen(
error = vm.error.collectAsState().value,
@ -381,52 +477,70 @@ class OnboardingFragment : Fragment() {
OnboardingLoginSetupViewModel.Navigation.Exit -> {
navController.popBackStack()
}
OnboardingLoginSetupViewModel.Navigation.NavigateToHomeScreen -> {
findNavController().navigate(R.id.action_openHome)
}
OnboardingLoginSetupViewModel.Navigation.NavigateToMigrationErrorScreen -> {
// TODO
findNavController().navigate(R.id.migrationNeededScreen, null, navOptions {
popUpTo(R.id.onboarding_nav) {
inclusive = false
}
})
}
}
}
}
LaunchedEffect(Unit) {
vm.toasts.collect { toast(it) }
}
DisposableEffect(Unit) {
onDispose { component.release() }
}
}
@Composable
private fun CreateSoulAnimation(contentPaddingTop: Int) {
val component = componentManager().onboardingSoulCreationAnimComponent.ReleaseOn(
viewLifecycleOwner = viewLifecycleOwner,
state = Lifecycle.State.DESTROYED
)
val component = componentManager().onboardingSoulCreationAnimComponent
CreateSoulAnimWrapper(
contentPaddingTop = contentPaddingTop,
viewModel = daggerViewModel { component.get().getViewModel() }
) {
findNavController().navigate(R.id.action_openHome)
viewModel = daggerViewModel { component.get().getViewModel() },
onAnimationComplete = {
findNavController().navigate(R.id.action_openHome)
}
)
DisposableEffect(Unit) {
onDispose { component.release() }
}
}
@Composable
private fun CreateSoul(navController: NavHostController, contentPaddingTop: Int) {
val component = componentManager().onboardingSoulCreationComponent.ReleaseOn(
viewLifecycleOwner = viewLifecycleOwner,
state = Lifecycle.State.DESTROYED
)
val component = componentManager().onboardingSoulCreationComponent
val vm = daggerViewModel { component.get().getViewModel() }
val focusManager = LocalFocusManager.current
val keyboardInsets = WindowInsets.ime
val density = LocalDensity.current
CreateSoulWrapper(vm, contentPaddingTop)
val navigationCommands = vm.navigationFlow.collectAsState(
initial = OnboardingSoulCreationViewModel.Navigation.Idle
)
LaunchedEffect(key1 = navigationCommands.value) {
when (navigationCommands.value) {
is OnboardingSoulCreationViewModel.Navigation.OpenSoulCreationAnim -> {
navController.navigate(
route = OnboardingNavigation.createSoulAnim
)
LaunchedEffect(Unit) {
vm.navigationFlow.collect { command ->
when (command) {
is OnboardingSoulCreationViewModel.Navigation.OpenSoulCreationAnim -> {
if (keyboardInsets.getBottom(density) > 0) {
focusManager.clearFocus(force = true)
delay(KEYBOARD_HIDE_DELAY)
}
navController.navigate(
route = OnboardingNavigation.createSoulAnim
)
vm.sendAnalyticsOnboardingScreen()
}
}
else -> {}
}
}
LaunchedEffect(Unit) {
@ -434,22 +548,33 @@ class OnboardingFragment : Fragment() {
toast(it)
}
}
DisposableEffect(Unit) {
onDispose { component.release() }
}
}
@Composable
private fun Mnemonic(navController: NavHostController, contentPaddingTop: Int) {
val component = componentManager().onboardingMnemonicComponent.ReleaseOn(
viewLifecycleOwner = viewLifecycleOwner,
state = Lifecycle.State.DESTROYED
)
private fun Mnemonic(
navController: NavHostController,
contentPaddingTop: Int,
mnemonicColorPalette: List<Color>
) {
val component = componentManager().onboardingMnemonicComponent
val vm = daggerViewModel { component.get().getViewModel() }
MnemonicPhraseScreenWrapper(
contentPaddingTop = contentPaddingTop,
viewModel = daggerViewModel { component.get().getViewModel() },
viewModel = vm,
openSoulCreation = {
navController.navigate(OnboardingNavigation.createSoul)
vm.sendAnalyticsOnboardingScreen()
},
copyMnemonicToClipboard = ::copyMnemonicToClipboard
copyMnemonicToClipboard = ::copyMnemonicToClipboard,
vm = vm,
mnemonicColorPalette = mnemonicColorPalette
)
DisposableEffect(Unit) {
onDispose { component.release() }
}
}
private fun copyMnemonicToClipboard(mnemonicPhrase: String) {
@ -467,10 +592,7 @@ class OnboardingFragment : Fragment() {
@Composable
private fun Auth(navController: NavHostController) {
val component = componentManager().onboardingStartComponent.ReleaseOn(
viewLifecycleOwner = viewLifecycleOwner,
state = Lifecycle.State.DESTROYED
)
val component = componentManager().onboardingStartComponent
val vm = daggerViewModel { component.get().getViewModel() }
AuthScreenWrapper(vm = vm)
LaunchedEffect(Unit) {
@ -507,6 +629,7 @@ class OnboardingFragment : Fragment() {
when (navigation) {
is OnboardingStartViewModel.AuthNavigation.ProceedWithSignUp -> {
navController.navigate(OnboardingNavigation.void)
vm.sendAnalyticsOnboardingScreen()
}
is OnboardingStartViewModel.AuthNavigation.ProceedWithSignIn -> {
@ -515,6 +638,9 @@ class OnboardingFragment : Fragment() {
}
}
}
DisposableEffect(Unit) {
onDispose { component.release() }
}
}
@Composable
@ -569,25 +695,17 @@ class OnboardingFragment : Fragment() {
return player
}
@Composable
fun <T> ComponentManager.Component<T>.ReleaseOn(
viewLifecycleOwner: LifecycleOwner,
state: Lifecycle.State
): ComponentManager.Component<T> {
val that = this
DisposableEffect(
key1 = viewLifecycleOwner.lifecycle.currentState.isAtLeast(state),
effect = {
onDispose {
if (viewLifecycleOwner.lifecycle.currentState == state) {
that.release()
}
}
}
)
return that
fun injectDependencies() {
componentManager().onboardingComponent.get().inject(this)
}
fun releaseDependencies() {
componentManager().onboardingComponent.release()
}
}
private const val ANIMATION_LENGTH_SLIDE = 300
private const val ANIMATION_LENGTH_FADE = 700
private const val ANIMATION_LENGTH_FADE = 700
private const val KEYBOARD_HIDE_DELAY = 300L
typealias BackButtonCallback = (() -> Unit)?

View File

@ -4,8 +4,10 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.text.ClickableText
@ -22,6 +24,7 @@ import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.ColorButtonInversion
import com.anytypeio.anytype.core_ui.OnBoardingTextPrimaryColor
import com.anytypeio.anytype.core_ui.OnBoardingTextSecondaryColor
import com.anytypeio.anytype.core_ui.views.ButtonSize
@ -68,6 +71,7 @@ fun AuthScreen(
Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center) {
Title(modifier = Modifier)
Description()
Spacer(modifier = Modifier.height(72.dp))
}
Column(
modifier = Modifier.fillMaxSize(),
@ -142,7 +146,8 @@ fun SignButtons(
modifier = Modifier
.weight(1f)
.fillMaxWidth()
.padding(start = 6.dp, end = 32.dp)
.padding(start = 6.dp, end = 32.dp),
textColor = ColorButtonInversion
)
}
}

View File

@ -1,6 +1,7 @@
package com.anytypeio.anytype.ui.onboarding.screens.signin
import androidx.activity.compose.BackHandler
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@ -8,17 +9,35 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.ColorButtonRegular
import com.anytypeio.anytype.core_ui.OnBoardingTextPrimaryColor
import com.anytypeio.anytype.core_ui.views.ButtonSize
import com.anytypeio.anytype.core_ui.views.Title1
import com.anytypeio.anytype.core_ui.views.animations.DotsLoadingIndicator
import com.anytypeio.anytype.core_ui.views.animations.FadeAnimationSpecs
@Preview
@Composable
fun EnteringTheVoidScreenPreview() {
EnteringTheVoidScreen(
error = "",
contentPaddingTop = 10,
onSystemBackPressed = {}
)
}
@Composable
fun EnteringTheVoidScreen(
@ -26,6 +45,8 @@ fun EnteringTheVoidScreen(
contentPaddingTop: Int,
onSystemBackPressed: () -> Unit
) {
val loadingAlpha by animateFloatAsState(targetValue = 1f)
Column(
modifier = Modifier
.fillMaxSize()
@ -51,6 +72,18 @@ fun EnteringTheVoidScreen(
fontSize = 10.sp
)
)
} else {
DotsLoadingIndicator(
animating = true,
modifier = Modifier
.padding(top = 20.dp)
.graphicsLayer { alpha = loadingAlpha }
.align(Alignment.CenterHorizontally)
,
animationSpecs = FadeAnimationSpecs(itemCount = 3),
size = ButtonSize.XSmall,
color = ColorButtonRegular
)
}
}
BackHandler {

View File

@ -23,6 +23,7 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.ColorButtonRegular
import com.anytypeio.anytype.core_ui.OnBoardingTextPrimaryColor
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.core_ui.views.ButtonSize
@ -147,7 +148,8 @@ fun RecoveryScreen(
size = ButtonSize.Large,
modifier = Modifier
.fillMaxWidth()
.padding(start = 18.dp, end = 18.dp, bottom = 24.dp)
.padding(start = 18.dp, end = 18.dp, bottom = 24.dp),
textColor = ColorButtonRegular
)
}
}

View File

@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
@ -32,6 +33,8 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.graphics.toColorInt
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@ -63,44 +66,41 @@ private fun CreateSoulAnimScreen(
onAnimationComplete: () -> Unit,
contentPaddingTop: Int
) {
state.fold(
onSuccess = {
val gradient = Brush.radialGradient(
listOf(
Color(it.icon.from.toColorInt()),
Color(it.icon.to.toColorInt())
)
)
val isBoxVisible = remember { mutableStateOf(false) }
val targetElementsAlpha by animateFloatAsState(
targetValue = if (isBoxVisible.value) 1f else 0f,
animationSpec = tween(durationMillis = ANIMATION_DURATION)
)
Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Top) {
Spacer(modifier = Modifier.height(contentPaddingTop.dp))
CreateSoulAnimationTitle(targetElementsAlpha)
CreateSoulAnimation(
isBoxVisible,
targetElementsAlpha,
gradient,
it.name
)
}
LaunchedEffect(Unit) {
delay(ANIMATION_START_DELAY)
isBoxVisible.value = true
}
LaunchedEffect(Unit) {
delay(ANIMATION_COMPLETE_DELAY)
onAnimationComplete.invoke()
}
}
val isBoxVisible = remember { mutableStateOf(false) }
val targetElementsAlpha by animateFloatAsState(
targetValue = if (isBoxVisible.value) 1f else 0f,
animationSpec = tween(durationMillis = ANIMATION_DURATION)
)
Box(modifier = Modifier.fillMaxSize()) {
state.fold(
onSuccess = {
val gradient = Brush.radialGradient(
listOf(
Color(it.icon.from.toColorInt()),
Color(it.icon.to.toColorInt())
)
)
Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Top) {
Spacer(modifier = Modifier.height(contentPaddingTop.dp))
CreateSoulAnimationTitle(targetElementsAlpha)
CreateSoulAnimation(
isBoxVisible,
targetElementsAlpha,
gradient,
it.name
)
}
LaunchedEffect(Unit) {
delay(ANIMATION_START_DELAY)
isBoxVisible.value = true
}
LaunchedEffect(Unit) {
delay(ANIMATION_COMPLETE_DELAY)
onAnimationComplete.invoke()
}
}
)
}
}
@Composable
@ -186,6 +186,9 @@ private fun CreateSoulAnimation(
.offset(x = -circleOffset.value)
.padding(top = 88.dp)
.align(Alignment.Center)
.widthIn(max = 128.dp),
overflow = TextOverflow.Ellipsis,
maxLines = 1
)
}
}

View File

@ -30,6 +30,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Name
import com.anytypeio.anytype.core_ui.OnBoardingTextPrimaryColor
import com.anytypeio.anytype.core_ui.OnBoardingTextSecondaryColor
import com.anytypeio.anytype.core_ui.views.ButtonSize
@ -43,15 +44,16 @@ import com.anytypeio.anytype.ui.onboarding.OnboardingInput
@Composable
fun CreateSoulWrapper(viewModel: OnboardingSoulCreationViewModel, contentPaddingTop: Int) {
CreateSoulScreen(contentPaddingTop) {
viewModel.setAccountAndSpaceName(it)
}
CreateSoulScreen(
contentPaddingTop = contentPaddingTop,
onGoToTheAppClicked = viewModel::onGoToTheAppClicked
)
}
@Composable
private fun CreateSoulScreen(
contentPaddingTop: Int,
onCreateSoulClicked: (String) -> Unit
onGoToTheAppClicked: (Name) -> Unit
) {
val text = remember { mutableStateOf("") }
val isKeyboardVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 0
@ -80,11 +82,11 @@ private fun CreateSoulScreen(
item {
CreateSoulInput(
text = text,
onKeyboardActionDoneClicked = { onCreateSoulClicked(text.value) }
onKeyboardActionDoneClicked = { onGoToTheAppClicked(text.value) }
)
}
item {
Spacer(modifier = Modifier.height(9.dp))
Spacer(modifier = Modifier.height(16.dp))
}
item {
CreateSoulDescription()
@ -103,7 +105,7 @@ private fun CreateSoulScreen(
Modifier.padding(bottom = 13.dp)
)
,
onCreateSoulEntered = onCreateSoulClicked,
onGoToTheAppClicked = onGoToTheAppClicked,
text = text
)
}
@ -152,7 +154,7 @@ fun CreateSoulInput(
.focusRequester(focusRequester)
,
text = text,
placeholder = stringResource(id = R.string.onboarding_soul_creation_placeholder),
placeholder = stringResource(id = R.string.name),
keyboardActions = KeyboardActions(
onDone = {
val input = text.value
@ -181,6 +183,7 @@ fun CreateSoulDescription() {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 48.dp)
.wrapContentHeight(),
contentAlignment = Alignment.Center
) {
@ -197,13 +200,13 @@ fun CreateSoulDescription() {
@Composable
fun CreateSoulNextButton(
modifier: Modifier,
onCreateSoulEntered: (String) -> Unit,
onGoToTheAppClicked: (Name) -> Unit,
text: MutableState<String>
) {
OnBoardingButtonPrimary(
text = stringResource(id = R.string.go_to_the_app),
onClick = {
onCreateSoulEntered.invoke(text.value)
onGoToTheAppClicked(text.value)
},
size = ButtonSize.Large,
enabled = text.value.trim().isNotEmpty(),

View File

@ -19,12 +19,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.blur
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.ColorBackgroundField
import com.anytypeio.anytype.core_ui.ColorButtonRegular
import com.anytypeio.anytype.core_ui.OnBoardingTextPrimaryColor
import com.anytypeio.anytype.core_ui.OnBoardingTextSecondaryColor
import com.anytypeio.anytype.core_ui.extensions.conditional
@ -42,7 +44,9 @@ fun MnemonicPhraseScreenWrapper(
viewModel: OnboardingMnemonicViewModel,
openSoulCreation: () -> Unit,
copyMnemonicToClipboard: (String) -> Unit,
contentPaddingTop: Int
contentPaddingTop: Int,
vm: OnboardingMnemonicViewModel,
mnemonicColorPalette: List<Color>
) {
val state = viewModel.state.collectAsStateWithLifecycle().value
MnemonicPhraseScreen(
@ -50,7 +54,9 @@ fun MnemonicPhraseScreenWrapper(
reviewMnemonic = { viewModel.openMnemonic() },
openSoulCreation = openSoulCreation,
copyMnemonicToClipboard = copyMnemonicToClipboard,
contentPaddingTop = contentPaddingTop
contentPaddingTop = contentPaddingTop,
vm = vm,
mnemonicColorPalette = mnemonicColorPalette
)
}
@ -60,7 +66,9 @@ fun MnemonicPhraseScreen(
reviewMnemonic: () -> Unit,
openSoulCreation: () -> Unit,
copyMnemonicToClipboard: (String) -> Unit,
contentPaddingTop: Int
contentPaddingTop: Int,
vm: OnboardingMnemonicViewModel,
mnemonicColorPalette: List<Color>
) {
Box(modifier = Modifier.fillMaxSize()) {
Column(
@ -71,14 +79,19 @@ fun MnemonicPhraseScreen(
) {
Spacer(modifier = Modifier.height(contentPaddingTop.dp))
MnemonicTitle()
MnemonicPhrase(state, copyMnemonicToClipboard)
MnemonicPhrase(
state = state,
copyMnemonicToClipboard = copyMnemonicToClipboard,
mnemonicColorPalette = mnemonicColorPalette
)
MnemonicDescription()
}
MnemonicButtons(
modifier = Modifier.align(Alignment.BottomCenter),
openMnemonic = reviewMnemonic,
openSoulCreation = openSoulCreation,
state = state
state = state,
vm = vm
)
}
}
@ -88,7 +101,8 @@ fun MnemonicButtons(
modifier: Modifier = Modifier,
openMnemonic: () -> Unit,
openSoulCreation: () -> Unit,
state: OnboardingMnemonicViewModel.State
state: OnboardingMnemonicViewModel.State,
vm: OnboardingMnemonicViewModel
) {
Column(modifier.wrapContentHeight()) {
when (state) {
@ -125,7 +139,10 @@ fun MnemonicButtons(
),
onClick = {
openSoulCreation.invoke()
}, size = ButtonSize.Large
vm.onCheckLaterClicked()
},
size = ButtonSize.Large,
textColor = ColorButtonRegular
)
}
}
@ -154,7 +171,8 @@ fun MnemonicTitle() {
@Composable
fun MnemonicPhrase(
state: OnboardingMnemonicViewModel.State,
copyMnemonicToClipboard: (String) -> Unit
copyMnemonicToClipboard: (String) -> Unit,
mnemonicColorPalette: List<Color>
) {
when (state) {
is OnboardingMnemonicViewModel.State.Idle -> {}
@ -187,7 +205,8 @@ fun MnemonicPhrase(
end = 16.dp,
bottom = 16.dp
),
mnemonic = state.mnemonicPhrase
mnemonic = state.mnemonicPhrase,
mnemonicColorPalette = mnemonicColorPalette
)
}
}
@ -199,7 +218,9 @@ fun MnemonicPhrase(
.padding(bottom = 12.dp),
onClick = {
copyMnemonicToClipboard.invoke(state.mnemonicPhrase)
}, size = ButtonSize.SmallSecondary
},
size = ButtonSize.SmallSecondary,
textColor = ColorButtonRegular
)
}
}

View File

@ -1,4 +1,5 @@
package com.anytypeio.anytype.ui.onboarding.screens.signup
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@ -7,30 +8,32 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.ColorButtonRegular
import com.anytypeio.anytype.core_ui.OnBoardingTextPrimaryColor
import com.anytypeio.anytype.core_ui.OnBoardingTextSecondaryColor
import com.anytypeio.anytype.core_ui.views.ButtonSize
import com.anytypeio.anytype.core_ui.views.HeadlineOnBoardingDescription
import com.anytypeio.anytype.core_ui.views.OnBoardingButtonPrimary
import com.anytypeio.anytype.core_ui.views.Title1
import com.anytypeio.anytype.presentation.common.ScreenState
import com.anytypeio.anytype.core_ui.views.animations.DotsLoadingIndicator
import com.anytypeio.anytype.core_ui.views.animations.FadeAnimationSpecs
import com.anytypeio.anytype.presentation.onboarding.signup.OnboardingVoidViewModel
@Composable
fun VoidScreenWrapper(
screenState: ScreenState,
screenState: OnboardingVoidViewModel.ScreenState,
contentPaddingTop: Int,
onNextClicked: () -> Unit
) {
@ -43,7 +46,7 @@ fun VoidScreenWrapper(
@Composable
fun VoidScreen(
screenState: ScreenState,
screenState: OnboardingVoidViewModel.ScreenState,
onNextClicked: () -> Unit,
contentPaddingTop: Int
) {
@ -64,20 +67,24 @@ fun VoidScreen(
.padding(start = 16.dp, end = 16.dp, bottom = 24.dp)
.align(Alignment.BottomCenter)
) {
val isLoading = screenState is OnboardingVoidViewModel.ScreenState.Loading
OnBoardingButtonPrimary(
modifier = Modifier.fillMaxWidth(),
text = stringResource(id = R.string.next),
onClick = { onNextClicked() },
text = if (!isLoading) stringResource(id = R.string.next) else "",
onClick = { if (!isLoading) onNextClicked() },
size = ButtonSize.Large
)
if (screenState is ScreenState.Loading) {
CircularProgressIndicator(
if (isLoading) {
val loadingAlpha by animateFloatAsState(targetValue = 1f)
DotsLoadingIndicator(
animating = true,
modifier = Modifier
.align(Alignment.CenterEnd)
.padding(end = 20.dp)
.size(18.dp),
color = colorResource(R.color.shape_secondary),
strokeWidth = 1.5.dp
.graphicsLayer { alpha = loadingAlpha }
.align(Alignment.Center)
,
animationSpecs = FadeAnimationSpecs(itemCount = 3),
size = ButtonSize.XSmall,
color = ColorButtonRegular
)
}
}

View File

@ -17,10 +17,12 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.anytypeio.anytype.BuildConfig
import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.EventsDictionary
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_ui.extensions.drawable
import com.anytypeio.anytype.core_ui.features.navigation.DefaultObjectViewAdapter
import com.anytypeio.anytype.core_ui.features.search.ObjectSearchDividerItemDecoration
import com.anytypeio.anytype.core_ui.layout.SpacingItemDecoration
import com.anytypeio.anytype.core_utils.ext.argOrNull
import com.anytypeio.anytype.core_utils.ext.imm
import com.anytypeio.anytype.core_utils.ext.invisible
import com.anytypeio.anytype.core_utils.ext.syncFocusWithImeVisibility
@ -44,6 +46,8 @@ class ObjectSearchFragment :
@Inject
lateinit var factory: ObjectSearchViewModelFactory
private val ctx: Id? get() = argOrNull(CTX_KEY)
private lateinit var clearSearchText: View
private lateinit var filterInputField: EditText
@ -51,7 +55,9 @@ class ObjectSearchFragment :
private val searchAdapter by lazy {
DefaultObjectViewAdapter(
onDefaultObjectClicked = vm::onObjectClicked
onDefaultObjectClicked = { obj ->
vm.onObjectClicked(view = obj, ctx = ctx)
}
)
}
@ -191,7 +197,7 @@ class ObjectSearchFragment :
)
addItemDecoration(
SpacingItemDecoration(
lastItemSpacingBottom = resources.getDimension(com.anytypeio.anytype.core_ui.R.dimen.dp_48).toInt()
lastItemSpacingBottom = resources.getDimension(R.dimen.dp_120).toInt()
)
)
}
@ -215,5 +221,6 @@ class ObjectSearchFragment :
companion object {
const val EMPTY_FILTER_TEXT = ""
const val CTX_KEY = "arg.search.ctx"
}
}

View File

@ -1063,7 +1063,11 @@ open class ObjectSetFragment :
if (childFragmentManager.backStackEntryCount > 0) {
childFragmentManager.popBackStack()
} else {
vm.onSystemBackPressed()
if (vm.isCustomizeViewPanelVisible.value) {
vm.onHideViewerCustomizeSwiped()
} else {
vm.onSystemBackPressed()
}
}
}
}

View File

@ -86,6 +86,7 @@ class AboutAppFragment : BaseBottomSheetComposeFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
expand()
skipCollapsed()
subscribe(vm.navigation) {
when (it) {
is AboutAppViewModel.Navigation.OpenExternalLink -> {

View File

@ -5,6 +5,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.compose.material.MaterialTheme
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.os.bundleOf
@ -16,6 +17,7 @@ import androidx.lifecycle.repeatOnLifecycle
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.common.ComposeDialogView
import com.anytypeio.anytype.core_utils.ext.setupBottomSheetBehavior
import com.anytypeio.anytype.core_utils.ext.shareFile
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
@ -23,12 +25,15 @@ import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel.Command
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel.Event
import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider
import com.anytypeio.anytype.ui.editor.modals.IconPickerFragmentBase.Companion.ARG_CONTEXT_ID_KEY
import com.anytypeio.anytype.ui.sets.ARG_SHOW_REMOVE_BUTTON
import com.anytypeio.anytype.ui.settings.system.SettingsActivity
import com.anytypeio.anytype.ui_settings.main.MainSettingScreen
import kotlinx.coroutines.launch
import java.io.File
import javax.inject.Inject
import kotlinx.coroutines.launch
import timber.log.Timber
class MainSettingFragment : BaseBottomSheetComposeFragment() {
@ -38,6 +43,9 @@ class MainSettingFragment : BaseBottomSheetComposeFragment() {
@Inject
lateinit var featureToggles: FeatureToggles
@Inject
lateinit var uriFileProvider: UriFileProvider
private val vm by viewModels<MainSettingsViewModel> { factory }
private val onProfileClicked = {
@ -131,16 +139,32 @@ class MainSettingFragment : BaseBottomSheetComposeFragment() {
safeNavigate(
R.id.actionOpenImagePickerScreen, bundleOf(
ARG_CONTEXT_ID_KEY to command.id,
ARG_SHOW_REMOVE_BUTTON to true
ARG_SHOW_REMOVE_BUTTON to command.showRemoveButton
)
)
}
Command.OpenFilesStorageScreen -> {
is Command.OpenFilesStorageScreen -> {
safeNavigate(R.id.actionOpenFilesStorageScreen)
}
is Command.Toast -> {
toast(msg = command.msg)
toast(
msg = command.msg,
duration = if (command.isLongDuration)
Toast.LENGTH_LONG
else
Toast.LENGTH_SHORT
)
}
is Command.ShareSpaceDebug -> {
try {
shareFile(
uriFileProvider.getUriForFile(File(command.path))
)
} catch (e: Exception) {
Timber.e(e, "Error while sharing space debug").also {
toast("Error while sharing space debug. Please try again later.")
}
}
}
}
}

View File

@ -76,10 +76,7 @@ class ProfileFragment : BaseBottomSheetComposeFragment() {
onKeychainPhraseClicked = onKeychainPhraseClicked,
onDeleteAccountClicked = { throttle { proceedWithAccountDeletion() } },
onLogoutClicked = onLogoutClicked,
onSpaceDebugClicked = { throttle { vm.onSpaceDebugClicked() } },
isLogoutInProgress = vm.isLoggingOut.collectAsState().value,
isDebugSpaceReportInProgress = vm.isDebugSpaceReportInProgress.collectAsState().value,
isShowDebug = true,
onNameChange = { vm.onNameChange(it) },
onProfileIconClick = { proceedWithIconClick() },
account = vm.profileData.collectAsStateWithLifecycle().value,

View File

@ -54,7 +54,7 @@ class MigrationErrorFragment : BaseComposeFragment() {
browseUrl(command)
}
is MigrationErrorViewModel.Command.Exit -> {
navigation().startLogin()
navigation().exitFromMigrationScreen()
}
}
}

View File

@ -19,6 +19,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_ui.features.navigation.DefaultObjectViewAdapter
import com.anytypeio.anytype.core_ui.layout.SpacingItemDecoration
import com.anytypeio.anytype.core_utils.ext.arg
import com.anytypeio.anytype.core_utils.ext.argOrNull
import com.anytypeio.anytype.core_utils.ext.drawable
@ -205,6 +206,11 @@ class SelectWidgetSourceFragment : BaseBottomSheetTextInputFragment<FragmentObje
setDrawable(drawable(R.drawable.divider_object_search))
}
)
addItemDecoration(
SpacingItemDecoration(
lastItemSpacingBottom = resources.getDimension(R.dimen.dp_120).toInt()
)
)
}
}

View File

@ -461,7 +461,7 @@ fun CollectionItem(
.padding(0.dp, 0.dp, 60.dp, 0.dp)
) {
val name = view.obj.name.ifBlank { stringResource(R.string.untitled) }
val name = view.obj.name.trim().ifBlank { stringResource(R.string.untitled) }
Text(
text = name,

View File

@ -31,6 +31,7 @@ import com.anytypeio.anytype.presentation.objects.ObjectIcon
@Composable
fun TreeWidgetObjectIcon(
modifier: Modifier = Modifier,
icon: ObjectIcon,
paddingStart: Dp,
paddingEnd: Dp
@ -38,7 +39,7 @@ fun TreeWidgetObjectIcon(
when (icon) {
is ObjectIcon.Profile.Avatar -> {
Box(
modifier = Modifier
modifier = modifier
.height(18.dp)
.width(18.dp)
.padding(start = paddingStart, end = paddingEnd)
@ -61,13 +62,13 @@ fun TreeWidgetObjectIcon(
is ObjectIcon.Profile.Image -> {
UriImage(
uri = icon.hash,
modifier = Modifier.padding(start = paddingStart, end = paddingEnd)
modifier = modifier.padding(start = paddingStart, end = paddingEnd)
)
}
is ObjectIcon.Basic.Emoji -> {
UriImage(
uri = Emojifier.safeUri(icon.unicode),
modifier = Modifier.padding(start = paddingStart, end = paddingEnd)
modifier = modifier.padding(start = paddingStart, end = paddingEnd)
)
}
is ObjectIcon.Basic.Image -> {
@ -89,7 +90,7 @@ fun TreeWidgetObjectIcon(
else
painterResource(id = R.drawable.ic_dashboard_task_checkbox_not_checked),
contentDescription = "Task icon",
modifier = Modifier.padding(start = paddingStart, end = paddingEnd)
modifier = modifier.padding(start = paddingStart, end = paddingEnd)
)
}
else -> {

View File

@ -14,6 +14,7 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.colorResource
@ -135,7 +136,7 @@ fun CompactListWidgetList(
onWidgetElementClicked: (ObjectWrapper.Basic) -> Unit
) {
elements.forEachIndexed { idx, element ->
Column() {
Column {
Row(
modifier = Modifier
.padding(vertical = 10.dp, horizontal = 16.dp)
@ -149,11 +150,12 @@ fun CompactListWidgetList(
TreeWidgetObjectIcon(
icon = element.objectIcon,
paddingStart = 8.dp,
paddingEnd = 4.dp
paddingEnd = 4.dp,
modifier = Modifier.align(Alignment.CenterVertically)
)
Text(
text = element.obj.getWidgetObjectName() ?: stringResource(id = R.string.untitled),
modifier = Modifier.padding(start = 8.dp),
modifier = Modifier.padding(start = 8.dp).fillMaxWidth(),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = PreviewTitle2Medium,

View File

@ -200,12 +200,13 @@ private fun TreeWidgetTreeItems(
TreeWidgetObjectIcon(
icon = element.objectIcon,
paddingStart = 8.dp,
paddingEnd = 4.dp
paddingEnd = 4.dp,
modifier = Modifier.align(Alignment.CenterVertically)
)
}
Text(
text = element.obj.getWidgetObjectName() ?: stringResource(id = R.string.untitled),
modifier = Modifier.padding(start = 8.dp),
modifier = Modifier.padding(start = 8.dp).fillMaxWidth(),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = PreviewTitle2Medium,

View File

@ -1,34 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0"/>
<item
android:color="#00000000"
android:offset="1.0"/>
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1"/>
</vector>

View File

@ -4,167 +4,11 @@
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<group android:scaleX="1.01"
android:scaleY="1.01"
android:translateX="-0.54"
android:translateY="-0.54">
<path android:fillColor="#000000"
android:pathData="M0,0h108v108h-108z"/>
</group>
</vector>

View File

@ -2,33 +2,34 @@
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
android:viewportWidth="110"
android:viewportHeight="110">
<group android:scaleX="1.01"
android:scaleY="1.01"
android:translateX="-0.55"
android:translateY="-0.55">
<group>
<clip-path
android:pathData="M0,0h110v110h-110z"/>
<path
android:pathData="M0,0h110v110h-110z"
android:fillColor="#000000"/>
<path
android:pathData="M62.5,45.43V38H41.5V45.5L62.5,45.43ZM62.5,45.5H70.5V72H62.5V45.5ZM48.5,72.5C54.58,72.5 59.5,67.58 59.5,61.5C59.5,55.42 54.58,50.5 48.5,50.5C42.42,50.5 37.5,55.42 37.5,61.5C37.5,67.58 42.42,72.5 48.5,72.5Z"
android:fillType="evenOdd">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
<gradient
android:startX="37.5"
android:startY="38"
android:endX="53.81"
android:endY="78.7"
android:type="linear">
<item android:offset="0.5" android:color="#FFFFFFFF"/>
<item android:offset="0.66" android:color="#FFFFEDBE"/>
<item android:offset="0.84" android:color="#FFFFBCC3"/>
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</path>
</group>
</group>
</vector>

View File

@ -1,11 +1,19 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="140dp"
android:height="30dp"
android:viewportWidth="140"
android:viewportHeight="30">
android:width="121dp"
android:height="28dp"
android:viewportWidth="121"
android:viewportHeight="28">
<path
android:pathData="M19.369,4.757H14.168V7.71C13.128,5.873 10.94,4.325 8.178,4.325C5.882,4.325 3.838,5.189 2.367,6.809C0.897,8.43 0,10.842 0,13.975V14.264C0,17.396 0.897,19.809 2.331,21.43C3.802,23.122 5.847,23.986 8.178,23.986C11.083,23.986 13.2,22.546 14.168,20.709V23.588H19.369V4.757ZM9.469,19.917C6.6,19.917 5.022,18.008 5.022,14.299V14.011C5.022,10.158 6.887,8.322 9.505,8.322C12.231,8.322 14.097,10.23 14.097,14.011V14.299C14.097,18.08 12.339,19.917 9.469,19.917ZM24.411,4.757H29.612V7.746C30.581,5.873 32.554,4.325 35.71,4.325C37.575,4.325 39.118,4.901 40.265,6.053C41.377,7.241 42.023,9.114 42.023,11.599V23.59H36.822V12.355C36.822,9.762 35.818,8.574 33.522,8.574C31.262,8.574 29.612,9.942 29.612,12.715V23.59H24.411V4.757ZM51.248,21.681L43.787,4.756H49.382L53.938,16.064L58.134,4.756H63.12L52.969,30H47.948L51.248,21.681ZM71.872,23.95C67.891,23.95 65.81,21.934 65.81,18.008V0H70.976V4.757H75.616V8.466H70.976V17.54C70.976,19.017 71.657,19.773 73.056,19.773C74.414,19.773 75.055,19.445 75.616,19.137V23.129C74.634,23.6 73.618,23.95 71.872,23.95ZM85.379,21.681L77.919,4.756H83.514L88.07,16.064L92.266,4.756H97.252L87.101,30H82.079L85.379,21.681ZM99.59,4.757H104.791V7.71C105.831,5.873 108.019,4.325 110.781,4.325C113.077,4.325 115.121,5.189 116.592,6.809C118.062,8.43 118.959,10.842 118.959,13.975V14.264C118.959,17.396 118.062,19.809 116.628,21.43C115.157,23.122 113.112,23.986 110.781,23.986C107.876,23.986 105.759,22.546 104.791,20.709V30H99.59V4.757ZM109.171,19.917C112.041,19.917 113.619,18.008 113.619,14.299V14.011C113.619,10.158 111.754,8.322 109.135,8.322C106.409,8.322 104.544,10.23 104.544,14.011V14.299C104.544,18.08 106.302,19.917 109.171,19.917ZM130.997,23.982C128.164,23.982 125.725,23.118 124.003,21.425C122.246,19.805 121.206,17.428 121.206,14.403V14.115C121.206,11.127 122.246,8.714 124.003,6.986C125.725,5.293 128.128,4.321 130.854,4.321C133.293,4.321 135.553,5.041 137.238,6.59C138.924,8.138 140,10.551 140,13.899V15.34H126.478C126.622,18.508 128.272,20.273 131.141,20.273C133.58,20.273 134.728,19.229 135.05,17.644H139.964C139.354,21.713 136.162,23.982 130.997,23.982ZM134.943,12.134C134.764,9.254 133.329,7.922 130.854,7.922C128.523,7.922 126.945,9.434 126.55,12.134H134.943Z"
android:fillColor="#2AA7EE"
android:fillAlpha="0.6"
android:pathData="M16.03,20.99V4.07H12.09V6.88H11.86C11.65,6.47 11.36,6.03 10.99,5.56C10.61,5.09 10.1,4.69 9.46,4.36C8.82,4.02 8,3.85 7.01,3.85C5.7,3.85 4.51,4.18 3.46,4.86C2.4,5.52 1.56,6.5 0.94,7.81C0.31,9.1 0,10.69 0,12.58C0,14.44 0.31,16.03 0.92,17.33C1.53,18.64 2.36,19.63 3.42,20.32C4.47,21 5.66,21.34 7,21.34C7.97,21.34 8.78,21.18 9.42,20.86C10.06,20.53 10.57,20.14 10.96,19.69C11.35,19.22 11.65,18.78 11.86,18.37H12.02V20.99H16.03ZM12.1,12.56C12.1,13.66 11.95,14.62 11.64,15.44C11.32,16.27 10.87,16.91 10.28,17.38C9.68,17.83 8.96,18.06 8.11,18.06C7.23,18.06 6.49,17.83 5.89,17.35C5.29,16.88 4.84,16.22 4.54,15.4C4.23,14.57 4.08,13.62 4.08,12.56C4.08,11.5 4.23,10.57 4.53,9.75C4.83,8.93 5.28,8.29 5.88,7.83C6.48,7.36 7.22,7.13 8.11,7.13C8.97,7.13 9.69,7.36 10.29,7.81C10.89,8.26 11.34,8.89 11.65,9.7C11.95,10.51 12.1,11.46 12.1,12.56Z"
android:fillColor="#DBDAD4"/>
<path
android:pathData="M64.59,4.07V7.16H61.24V15.94C61.24,16.47 61.32,16.89 61.48,17.17C61.65,17.45 61.87,17.65 62.14,17.75C62.42,17.85 62.72,17.9 63.05,17.9C63.3,17.9 63.53,17.89 63.73,17.85C63.95,17.81 64.11,17.78 64.22,17.75L64.89,20.88C64.68,20.95 64.38,21.03 63.98,21.12C63.59,21.21 63.11,21.26 62.54,21.27C61.54,21.3 60.64,21.15 59.83,20.82C59.03,20.48 58.39,19.96 57.92,19.25C57.46,18.54 57.23,17.66 57.24,16.6V7.16V4.07V0H61.24V4.07H64.59ZM23.82,21.04V11.1C23.82,10.28 23.97,9.58 24.27,9.01C24.58,8.43 25.01,7.99 25.54,7.69C26.09,7.38 26.71,7.23 27.41,7.23C28.44,7.23 29.25,7.55 29.83,8.19C30.42,8.83 30.72,9.72 30.72,10.85V21.04H34.72V10.23C34.72,8.88 34.49,7.73 34,6.77C33.52,5.82 32.84,5.1 31.97,4.6C31.1,4.1 30.08,3.85 28.9,3.85C27.64,3.85 26.57,4.13 25.71,4.69C24.86,5.25 24.23,6 23.84,6.95H23.64V4.07H19.82V21.04H23.82ZM70.58,27.23C71.3,25.29 72.01,23.34 72.74,21.4L66.58,4.07H70.83L74.74,16.89H74.92L78.84,4.07H83.09L74.8,27.23H70.58ZM85.86,27.41V4.07H89.8V6.87H90.03C90.24,6.46 90.53,6.02 90.9,5.56C91.28,5.09 91.79,4.69 92.43,4.35C93.07,4.02 93.89,3.85 94.88,3.85C96.19,3.85 97.37,4.18 98.43,4.85C99.49,5.51 100.33,6.5 100.95,7.8C101.57,9.1 101.89,10.69 101.89,12.58C101.89,14.44 101.58,16.03 100.97,17.33C100.36,18.63 99.53,19.63 98.47,20.31C97.42,21 96.23,21.34 94.89,21.34C93.92,21.34 93.11,21.18 92.47,20.85C91.83,20.53 91.31,20.14 90.92,19.68C90.54,19.22 90.24,18.78 90.03,18.37H89.86V27.41H85.86ZM89.79,12.55C89.79,13.65 89.94,14.61 90.25,15.44C90.57,16.26 91.02,16.91 91.61,17.37C92.21,17.83 92.93,18.06 93.78,18.06C94.66,18.06 95.4,17.82 96,17.35C96.59,16.87 97.04,16.22 97.35,15.4C97.65,14.56 97.81,13.62 97.81,12.55C97.81,11.5 97.66,10.57 97.36,9.75C97.05,8.93 96.6,8.29 96.01,7.82C95.41,7.36 94.67,7.13 93.78,7.13C92.92,7.13 92.2,7.35 91.6,7.8C91,8.25 90.55,8.88 90.24,9.69C89.94,10.5 89.79,11.46 89.79,12.55ZM108.42,20.31C109.66,21.02 111.13,21.37 112.83,21.37C114.15,21.37 115.32,21.18 116.34,20.78C117.37,20.37 118.21,19.8 118.86,19.08C119.53,18.34 119.98,17.48 120.21,16.49L116.48,16.07C116.3,16.55 116.04,16.95 115.69,17.27C115.35,17.6 114.94,17.84 114.46,18.01C113.99,18.18 113.47,18.26 112.88,18.26C112.01,18.26 111.26,18.07 110.61,17.7C109.96,17.33 109.45,16.79 109.09,16.09C108.75,15.41 108.56,14.59 108.55,13.65H120.38V12.42C120.38,10.93 120.17,9.65 119.76,8.58C119.35,7.49 118.78,6.6 118.06,5.9C117.34,5.2 116.52,4.69 115.58,4.35C114.65,4.02 113.67,3.85 112.62,3.85C111,3.85 109.59,4.22 108.39,4.96C107.18,5.7 106.25,6.73 105.58,8.06C104.92,9.38 104.58,10.91 104.58,12.65C104.58,14.43 104.92,15.97 105.58,17.28C106.24,18.59 107.19,19.6 108.42,20.31ZM108.56,10.95C108.6,10.29 108.77,9.67 109.07,9.11C109.42,8.47 109.9,7.95 110.52,7.56C111.14,7.16 111.86,6.96 112.67,6.96C113.44,6.96 114.11,7.14 114.69,7.48C115.27,7.83 115.72,8.3 116.04,8.91C116.37,9.5 116.54,10.19 116.54,10.95H108.56Z"
android:fillColor="#DBDAD4"
android:fillType="evenOdd"/>
<path
android:pathData="M66.58,4.07L72.74,21.4C72.01,23.34 71.3,25.29 70.58,27.23H74.8L83.09,4.07H78.84L74.92,16.89H74.74L70.83,4.07H66.58Z"
android:fillColor="#DBDAD4"/>
<path
android:pathData="M36.98,4.07L43.14,21.4C42.41,23.34 41.69,25.28 40.97,27.23H45.2L53.49,4.07H49.24L45.31,16.89H45.14L41.22,4.07H36.98Z"
android:fillColor="#DBDAD4"/>
</vector>

View File

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background_primary">
android:background="@android:color/black">
<ImageView
android:id="@+id/logo"
@ -19,7 +19,8 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="16dp"
android:textColor="@color/palette_system_sky"
android:textColor="#DBDAD4"
android:textSize="10sp"
tools:text="0.0.10-alpha" />
<TextView

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_new_app_launcher_background"/>
<foreground android:drawable="@mipmap/ic_new_app_launcher_foreground"/>
</adaptive-icon>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_new_app_launcher_background"/>
<foreground android:drawable="@mipmap/ic_new_app_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -337,9 +337,6 @@
android:id="@+id/action_openHome"
app:destination="@id/homeScreen"
app:enterAnim="@anim/fade_in"
app:exitAnim="@anim/fade_out"
app:popEnterAnim="@anim/fade_in"
app:popExitAnim="@anim/fade_out"
app:popUpTo="@id/authStartScreen"
app:popUpToInclusive="true" />
@ -535,7 +532,7 @@
<dialog
android:id="@+id/relationCreationFragment"
android:name="com.anytypeio.anytype.ui.relations.RelationCreateFromLibraryFragment"
android:label="RelationCreationFragment"></dialog>
android:label="RelationCreationFragment"/>
<dialog
android:id="@+id/typeEditingFragment"
@ -666,15 +663,6 @@
android:id="@+id/migrationNeededScreen"
android:name="com.anytypeio.anytype.ui.update.MigrationErrorFragment"
android:label="Migration-needed screen">
<action
android:id="@+id/action_splashFragment_to_login_nav"
app:destination="@id/login_nav"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:popUpTo="@+id/splashScreen"
app:popUpToInclusive="true" />
</fragment>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_new_app_launcher_background">#FFFFFF</color>
</resources>

View File

@ -212,7 +212,7 @@ Do the computation of an expensive paragraph of text on a background thread:
<string name="btn_upload_storage">Upload from External storage</string>
<string name="btn_upload">Upload</string>
<string name="alert_qr_camera">Open "Profile" and then "Recovery phrase" on Anytype Desktop.</string>
<string name="alert_qr_camera">Go to Settings Recovery Phrase on Desktop in order to find the QR code for login.</string>
<string name="alert_qr_camera_ok">Ok</string>
<string name="choose_option">Choose option</string>
<string name="find_a_relation">Find a relation</string>
@ -391,4 +391,7 @@ Do the computation of an expensive paragraph of text on a background thread:
<string name="mail_more_space_body">Hi, Anytype team. I am reaching out to request an increase in my file storage capacity as I have run out of storage. My current limits is %1$s. My account id is %2$s. Cheers, %3$s</string>
<string name="your_recovery_phrase_can_t_be_empty">Your recovery phrase can\'t be empty</string>
<string name="go_to_the_app">Go to the app</string>
<string name="exiting_please_wait">Exiting... please wait</string>
<string name="loading_please_wait">Loading... please wait</string>
</resources>

View File

@ -43,7 +43,6 @@ plugins {
alias(libs.plugins.wire) apply false
alias(libs.plugins.dokka) apply false
alias(libs.plugins.firebaseDistribution) apply false
alias(libs.plugins.crashlytics) apply false
alias(libs.plugins.navigation) apply false
alias(libs.plugins.gms) apply false
}

View File

@ -5,6 +5,8 @@ typealias Key = String
typealias Url = String
typealias Hash = String
typealias Struct = Map<Id, Any?>
typealias Name = String
typealias Filepath = String
typealias Document = List<Block>

View File

@ -323,7 +323,8 @@ data class Block(
val id: Id = "",
val relationKey: String,
val type: Type,
val includeTime: Boolean = false
val includeTime: Boolean = false,
val customOrder: List<Id> = emptyList()
) {
enum class Type(val formattedName: String) {
ASC("ascending"),

View File

@ -23,7 +23,7 @@ sealed class ObjectWrapper {
val name: String? by default
val iconEmoji: String? by default
val iconImage: String? by default
val iconImage: String? = getValue(Relations.ICON_IMAGE)
val iconOption: Double? by default
val coverId: String? by default
@ -135,7 +135,7 @@ sealed class ObjectWrapper {
val description: String? by default
val source: String? by default
val iconEmoji: String? by default
val iconImage: String? by default
val iconImage: String? = getValue(Relations.ICON_IMAGE)
val picture: String? by default
val isArchived: Boolean? by default
val isDeleted: Boolean? by default

View File

@ -11,7 +11,7 @@ android {
}
composeOptions {
kotlinCompilerExtensionVersion libs.versions.androidxComposeVersion1.get()
kotlinCompilerExtensionVersion libs.versions.composeKotlinCompilerVersion.get()
}
buildFeatures {

View File

@ -17,6 +17,8 @@ val ColorButtonSecondaryPressed = Color(0xFF1F1E1C)
val ColorButtonSecondaryBorder = Color(0xFF525148)
val ColorButtonSecondaryBorderPressed = Color(0xFF525148)
val ColorButtonSecondaryText = Color(0xFF7B7A72)
val ColorButtonRegular = Color(0xFFCBC9BD)
val ColorButtonInversion= Color(0xFFFFFFFF)
val ColorPagerIndicator = Color(0xFF3A3935)
val ColorPagerIndicatorCurrent = Color(0xFFF3F2EC)
@ -30,4 +32,17 @@ val ColorBackgroundField = Color(0x26DAD7CA)
val ColorMnemonicStub = Color(0xFF252525)
val ColorPlaceholderText = Color(0x4DFFFFFF)
val ColorSoulConnectorLine = Color(0x30DBDAD4)
val ColorSpaceBackground = Color(0xFFDBDAD4)
val ColorSpaceBackground = Color(0xFFDBDAD4)
val MnemonicPhrasePaletteColors = listOf(
Color(0xFFF1A91A),
Color(0xFFF55522),
Color(0xFF5871FF),
Color(0xFF18A3F1),
Color(0xFFDD2EA1),
Color(0xFFB354D7),
Color(0xFF5DD400),
)
const val MNEMONIC_WORD_COUNT = 12

View File

@ -87,7 +87,7 @@ class BlockViewDiffUtil(
if (newBlock is Focusable && oldBlock is Focusable) {
if (newBlock.isFocused != oldBlock.isFocused) {
changes.add(FOCUS_CHANGED)
Timber.d("Focus changed!")
Timber.d("Focus changed to [${newBlock.isFocused}] for block ${newBlock.id}")
} else
Timber.d("Focus hasn't changed")
}

View File

@ -6,13 +6,10 @@ import android.text.style.LeadingMarginSpan
import android.widget.FrameLayout
import android.widget.TextView
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.RecyclerView
import com.anytypeio.anytype.core_ui.BuildConfig
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.common.SearchHighlightSpan
import com.anytypeio.anytype.core_ui.common.SearchTargetHighlightSpan
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkBinding
import com.anytypeio.anytype.core_ui.extensions.setBlockBackgroundColor
import com.anytypeio.anytype.core_ui.features.editor.BlockViewDiffUtil
import com.anytypeio.anytype.core_ui.features.editor.BlockViewHolder
import com.anytypeio.anytype.core_ui.features.editor.EditorTouchProcessor
@ -22,7 +19,6 @@ import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_utils.ext.gone
import com.anytypeio.anytype.core_utils.ext.removeSpans
import com.anytypeio.anytype.core_utils.ext.visible
import com.anytypeio.anytype.core_models.ThemeColor
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Searchable.Field.Companion.DEFAULT_SEARCH_FIELD_KEY
@ -41,7 +37,7 @@ class LinkToObject(
private val untitled = itemView.resources.getString(R.string.untitled)
val objectIcon = binding.objectIconWidget
private val objectIconContainer = binding.iconObjectContainer
private val title = binding.text
val title = binding.text
private val description = binding.tvDescription
private val objectType = binding.tvObjectType
@ -63,8 +59,8 @@ class LinkToObject(
applyText(item)
applyDescription(item)
applyObjectType(item)
applySearchHighlight(item)
applyImageOrEmoji(item)
applySearchHighlight(item)
itemView.setOnClickListener { clicked(ListenerType.LinkToObject(item.id)) }
}
@ -89,7 +85,7 @@ class LinkToObject(
)
}
title.visible()
title.text = sb
title.setText(sb, TextView.BufferType.EDITABLE)
}
else -> {
val sb = SpannableString(name)
@ -101,7 +97,7 @@ class LinkToObject(
)
}
title.visible()
title.text = sb
title.setText(sb, TextView.BufferType.EDITABLE)
}
}
}
@ -181,9 +177,6 @@ class LinkToObject(
if (payload.isSelectionChanged) {
applySelectedState(item)
}
if (payload.isSearchHighlightChanged) {
applySearchHighlight(item)
}
if (payload.isObjectTitleChanged) {
applyText(item)
}
@ -196,6 +189,9 @@ class LinkToObject(
if (payload.isObjectTypeChanged) {
applyObjectType(item)
}
if (payload.isSearchHighlightChanged) {
applySearchHighlight(item)
}
}
}

View File

@ -35,7 +35,7 @@ class LinkToObjectArchive(
private val root = binding.root
private val untitled = itemView.resources.getString(R.string.untitled)
private val title = binding.pageTitle
val title = binding.pageTitle
override val editorTouchProcessor = EditorTouchProcessor(
fallback = { e -> itemView.onTouchEvent(e) }

View File

@ -77,12 +77,12 @@ abstract class LinkToObjectCard(
applyBackground(item.background)
applySearchHighlight(item)
applyImageOrEmoji(item)
applyObjectType(item)
applySearchHighlight(item)
itemView.setOnClickListener { clicked(ListenerType.LinkToObject(item.id)) }
}
@ -97,12 +97,12 @@ abstract class LinkToObjectCard(
name.isBlank() -> {
titleView.visible()
val sb = SpannableString(untitled)
titleView.setText(sb, TextView.BufferType.SPANNABLE)
titleView.setText(sb, TextView.BufferType.EDITABLE)
}
else -> {
titleView.visible()
val sb = SpannableString(name)
titleView.setText(sb, TextView.BufferType.SPANNABLE)
titleView.setText(sb, TextView.BufferType.EDITABLE)
}
}
}

View File

@ -24,4 +24,20 @@ open class MentionFooterItemDecorator(private val screen: Point) : RecyclerView.
class MarkupColorToolbarFooter(screen: Point) : MentionFooterItemDecorator(screen)
class SlashWidgetFooterItemDecorator(screen: Point) : MentionFooterItemDecorator(screen)
class StyleToolbarItemDecorator(screen: Point) : MentionFooterItemDecorator(screen)
class StyleToolbarItemDecorator(screen: Point) : MentionFooterItemDecorator(screen)
class LastItemBottomOffsetDecorator(private val offset: Int) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val adapter = parent.adapter ?: return
val pos = parent.getChildAdapterPosition(view)
if (pos == adapter.itemCount - 1) {
outRect.bottom = offset
}
}
}

View File

@ -33,8 +33,8 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.views.animations.DotsLoadingIndicator
import com.anytypeio.anytype.core_ui.views.animations.FadeAnimationSpecs
import com.anytypeio.anytype.core_ui.views.animations.LoadingIndicator
class ButtonPrimaryXSmall @JvmOverloads constructor(
context: Context,
@ -154,6 +154,45 @@ fun ButtonPrimary(
}
}
@Composable
fun ButtonPrimaryDarkTheme(
text: String = "",
onClick: () -> Unit,
enabled: Boolean = true,
modifier: Modifier = Modifier,
size: ButtonSize
) {
val interactionSource = remember { MutableInteractionSource() }
val backgroundColor = Color(0xFFF3F2EC)
CompositionLocalProvider(LocalRippleTheme provides NoRippleTheme) {
Button(
onClick = onClick,
interactionSource = interactionSource,
enabled = enabled,
shape = RoundedCornerShape(size.cornerSize),
colors = ButtonDefaults.buttonColors(
backgroundColor = backgroundColor,
contentColor = Color(0xFF1F1E1D),
disabledBackgroundColor = Color(0xFF1F1E1D),
disabledContentColor = Color(0xFF1F1E1D)
),
modifier = modifier
.defaultMinSize(minWidth = 1.dp, minHeight = 1.dp),
elevation = ButtonDefaults.elevation(
defaultElevation = 0.dp,
pressedElevation = 0.dp
),
contentPadding = size.contentPadding
) {
Text(
text = text,
style = size.textStyle
)
}
}
}
@Composable
fun ButtonPrimaryLoading(
text: String = "",
@ -202,7 +241,7 @@ fun ButtonPrimaryLoading(
style = size.textStyle
)
}
LoadingIndicator(
DotsLoadingIndicator(
animating = loading,
modifier = Modifier.graphicsLayer { alpha = loadingAlpha },
animationSpecs = FadeAnimationSpecs(itemCount = loadingItemsCount),
@ -309,7 +348,7 @@ fun ButtonSecondaryLoading(
modifier = Modifier.graphicsLayer { alpha = contentAlpha }
)
}
LoadingIndicator(
DotsLoadingIndicator(
animating = loading,
modifier = Modifier.graphicsLayer { alpha = loadingAlpha },
animationSpecs = FadeAnimationSpecs(itemCount = loadingItemsCount),
@ -407,7 +446,7 @@ fun ButtonWarningLoading(
style = size.textStyle
)
}
LoadingIndicator(
DotsLoadingIndicator(
animating = loading,
modifier = Modifier.graphicsLayer { alpha = loadingAlpha },
animationSpecs = FadeAnimationSpecs(itemCount = loadingItemsCount),
@ -487,6 +526,19 @@ fun MyPrimaryButton() {
)
}
@Composable
@Preview
fun MyPrimaryButtonDark() {
ButtonPrimaryDarkTheme(
onClick = {},
size = ButtonSize.Large,
text = "Login",
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp)
)
}
@Composable
@Preview
fun MySecondaryButton() {

View File

@ -0,0 +1,75 @@
package com.anytypeio.anytype.core_ui.views
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
@ExperimentalMaterial3Api
@Composable
fun BaseAlertDialog(
dialogText: String,
buttonText: String,
onButtonClick: () -> Unit,
onDismissRequest: () -> Unit
): Unit {
val modifier = Modifier
.shadow(
elevation = 40.dp, spotColor = Color(0x40000000), ambientColor = Color(0x40000000)
)
.wrapContentWidth()
.wrapContentHeight()
.background(
color = Color(0xFF1F1E1D), shape = RoundedCornerShape(size = 8.dp)
)
.padding(start = 32.dp, top = 32.dp, end = 32.dp, bottom = 32.dp)
AlertDialog(onDismissRequest = onDismissRequest) {
Surface(
modifier = modifier,
shape = MaterialTheme.shapes.large,
tonalElevation = AlertDialogDefaults.TonalElevation,
color = Color.Transparent
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = dialogText,
style = UXBody,
textAlign = TextAlign.Center,
color = Color(0xFFFFFFFF),
modifier = Modifier.padding(horizontal = 10.dp)
)
Spacer(modifier = Modifier.height(18.dp))
ButtonPrimaryDarkTheme(
text = buttonText,
onClick = onButtonClick,
size = ButtonSize.Large,
modifier = Modifier
.wrapContentHeight()
.fillMaxWidth()
.align(Alignment.CenterHorizontally)
)
}
}
}
}

View File

@ -14,7 +14,6 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_ui.ColorButtonPrimaryActive
import com.anytypeio.anytype.core_ui.ColorButtonPrimaryInactive
@ -26,7 +25,6 @@ import com.anytypeio.anytype.core_ui.ColorButtonSecondaryBorder
import com.anytypeio.anytype.core_ui.ColorButtonSecondaryBorderPressed
import com.anytypeio.anytype.core_ui.ColorButtonSecondaryPressed
import com.anytypeio.anytype.core_ui.ColorButtonSecondaryText
import com.anytypeio.anytype.core_ui.R
@Composable
fun OnBoardingButtonPrimary(
@ -78,7 +76,8 @@ fun OnBoardingButtonSecondary(
onClick: () -> Unit,
enabled: Boolean = true,
modifier: Modifier = Modifier,
size: ButtonSize
size: ButtonSize,
textColor: Color = ColorButtonSecondaryText
) {
val interactionSource = remember { MutableInteractionSource() }
val isPressed = interactionSource.collectIsPressedAsState()
@ -114,7 +113,7 @@ fun OnBoardingButtonSecondary(
Text(
text = text,
style = size.textStyle.copy(
color = ColorButtonSecondaryText
color = textColor
)
)
}

View File

@ -40,7 +40,7 @@ fun rememberLoadingIndicatorState(
}
@Composable
fun LoadingIndicator(
fun DotsLoadingIndicator(
animating: Boolean,
modifier: Modifier = Modifier,
color: Color = Color.White,

View File

@ -4,6 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
xmlns:tools="http://schemas.android.com/tools">
<androidx.recyclerview.widget.RecyclerView

View File

@ -991,8 +991,8 @@ class BlockAdapterTest {
check(firstHolder is Paragraph)
check(secondHolder is Paragraph)
assertTrue { firstHolder.content.isSelected }
assertFalse { secondHolder.content.isSelected }
assertTrue { firstHolder.selectionView.isSelected }
assertFalse { secondHolder.selectionView.isSelected }
}
@Test
@ -1369,12 +1369,12 @@ class BlockAdapterTest {
check(h3SelectedHolder is HeaderThree)
check(h3NotSelectedHolder is HeaderThree)
assertTrue { h1SelectedHolder.content.isSelected }
assertFalse { h1NotSelectedHolder.content.isSelected }
assertTrue { h2SelectedHolder.content.isSelected }
assertFalse { h2NotSelectedHolder.content.isSelected }
assertTrue { h3SelectedHolder.content.isSelected }
assertFalse { h3NotSelectedHolder.content.isSelected }
assertTrue { h1SelectedHolder.selectionView.isSelected }
assertFalse { h1NotSelectedHolder.selectionView.isSelected }
assertTrue { h2SelectedHolder.selectionView.isSelected }
assertFalse { h2NotSelectedHolder.selectionView.isSelected }
assertTrue { h3SelectedHolder.selectionView.isSelected }
assertFalse { h3NotSelectedHolder.selectionView.isSelected }
}
@Test

View File

@ -0,0 +1,197 @@
package com.anytypeio.anytype.core_ui.features.editor.holders.other
import android.content.Context
import android.os.Build
import android.view.LayoutInflater
import android.widget.TextView
import androidx.fragment.app.testing.FragmentScenario
import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.test.core.app.ApplicationProvider
import com.anytypeio.anytype.core_models.ThemeColor
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.common.SearchHighlightSpan
import com.anytypeio.anytype.core_ui.common.SearchTargetHighlightSpan
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkArchiveBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkCardMediumIconBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkCardMediumIconCoverBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkCardSmallIconBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkCardSmallIconCoverBinding
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.test_utils.MockDataFactory
import com.anytypeio.anytype.test_utils.TestFragment
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.core.IsEqual.equalTo
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(
manifest = Config.NONE,
sdk = [Build.VERSION_CODES.P],
instrumentedPackages = [
"androidx.loader.content"
]
)
class LinkToObjectTest {
private val context: Context = ApplicationProvider.getApplicationContext()
lateinit var scenario: FragmentScenario<TestFragment>
private lateinit var linkToObject: LinkToObject
private lateinit var linkToObjectArchived: LinkToObjectArchive
private lateinit var linkToObjectCardSmallIcon: LinkToObjectCardSmallIcon
private lateinit var linkToObjectCardSmallIconCover: LinkToObjectCardSmallIconCover
private lateinit var linkToObjectCardMediumIcon: LinkToObjectCardMediumIcon
private lateinit var linkToObjectCardMediumIconCover: LinkToObjectCardMediumIconCover
private lateinit var item: BlockView.LinkToObject.Default.Text
private lateinit var itemArchived: BlockView.LinkToObject.Archived
private lateinit var itemCardSmallIcon: BlockView.LinkToObject.Default.Card.SmallIcon
private lateinit var itemCardSmallIconCover: BlockView.LinkToObject.Default.Card.SmallIconCover
private lateinit var itemCardMediumIcon: BlockView.LinkToObject.Default.Card.MediumIcon
private lateinit var itemCardMediumIconCover: BlockView.LinkToObject.Default.Card.MediumIconCover
@Before
fun setup() {
context.setTheme(R.style.Theme_MaterialComponents)
scenario = launchFragmentInContainer()
val layoutInflater = LayoutInflater.from(context)
linkToObject = LinkToObject(ItemBlockObjectLinkBinding.inflate(layoutInflater))
linkToObjectArchived = LinkToObjectArchive(ItemBlockObjectLinkArchiveBinding.inflate(layoutInflater))
linkToObjectCardSmallIcon = LinkToObjectCardSmallIcon(ItemBlockObjectLinkCardSmallIconBinding.inflate(layoutInflater))
linkToObjectCardSmallIconCover = LinkToObjectCardSmallIconCover(
ItemBlockObjectLinkCardSmallIconCoverBinding.inflate(layoutInflater))
linkToObjectCardMediumIcon = LinkToObjectCardMediumIcon(
ItemBlockObjectLinkCardMediumIconBinding.inflate(layoutInflater))
linkToObjectCardMediumIconCover = LinkToObjectCardMediumIconCover(
ItemBlockObjectLinkCardMediumIconCoverBinding.inflate(layoutInflater))
val id = MockDataFactory.randomUuid()
val searchField = BlockView.Searchable.Field(
highlights = listOf(IntRange(0, 4), IntRange(6, 10)),
target = IntRange(0, 4)
)
item = BlockView.LinkToObject.Default.Text(
id = id,
indent = MockDataFactory.randomInt(),
isSelected = false,
icon = ObjectIcon.None,
searchFields = listOf(searchField),
text = "Text1 Text2"
)
itemArchived = BlockView.LinkToObject.Archived(
id = id,
indent = MockDataFactory.randomInt(),
isSelected = false,
searchFields = listOf(searchField),
text = "Text1 Text2"
)
itemCardSmallIcon = BlockView.LinkToObject.Default.Card.SmallIcon(
id = id,
indent = MockDataFactory.randomInt(),
isSelected = false,
icon = ObjectIcon.None,
searchFields = listOf(searchField),
text = "Text1 Text2",
background = ThemeColor.BLUE,
isPreviousBlockMedia = false
)
itemCardSmallIconCover = BlockView.LinkToObject.Default.Card.SmallIconCover(
id = id,
indent = MockDataFactory.randomInt(),
isSelected = false,
icon = ObjectIcon.None,
searchFields = listOf(searchField),
text = "Text1 Text2",
background = ThemeColor.BLUE,
isPreviousBlockMedia = false,
cover = null
)
itemCardMediumIcon = BlockView.LinkToObject.Default.Card.MediumIcon(
id = id,
indent = MockDataFactory.randomInt(),
isSelected = false,
icon = ObjectIcon.None,
searchFields = listOf(searchField),
text = "Text1 Text2",
background = ThemeColor.BLUE,
isPreviousBlockMedia = false
)
itemCardMediumIconCover = BlockView.LinkToObject.Default.Card.MediumIconCover(
id = id,
indent = MockDataFactory.randomInt(),
isSelected = false,
icon = ObjectIcon.None,
searchFields = listOf(searchField),
text = "Text1 Text2",
background = ThemeColor.BLUE,
isPreviousBlockMedia = false,
cover = null
)
}
@Test
fun `test linkToObject block`() {
linkToObject.bind(item, {})
assertSearchSpans(linkToObject.title)
}
@Test
fun `test linkToObjectArchive block`() {
linkToObjectArchived.bind(itemArchived, {})
assertSearchSpans(linkToObjectArchived.title)
}
@Test
fun `test linkToObjectCard block`() {
linkToObjectCardSmallIcon.bind(itemCardSmallIcon, {})
assertSearchSpans(linkToObjectCardSmallIcon.titleView)
}
@Test
fun `test linkToObjectCardCover block`() {
linkToObjectCardSmallIconCover.bind(itemCardSmallIconCover, {})
assertSearchSpans(linkToObjectCardSmallIconCover.titleView)
}
@Test
fun `test linkToObjectCardMediumIcon block`() {
linkToObjectCardMediumIcon.bind(itemCardMediumIcon, {})
assertSearchSpans(linkToObjectCardMediumIcon.titleView)
}
@Test
fun `test linkToObjectCardMediumIconCover block`() {
linkToObjectCardMediumIconCover.bind(itemCardMediumIconCover, {})
assertSearchSpans(linkToObjectCardMediumIconCover.titleView)
}
private fun assertSearchSpans(textView: TextView) {
// verify
val spans = textView.editableText.getSpans(0, item.text!!.length, SearchHighlightSpan::class.java)
assertThat(spans.size, equalTo(2))
assertThat(textView.editableText.getSpanStart(spans[0]), equalTo(0))
assertThat(textView.editableText.getSpanEnd(spans[0]), equalTo(4))
assertThat(textView.editableText.getSpanStart(spans[1]), equalTo(6))
assertThat(textView.editableText.getSpanEnd(spans[1]), equalTo(10))
val targetSpans = textView.editableText.getSpans(0, textView.text.length, SearchTargetHighlightSpan::class.java)
assertThat(targetSpans.size, equalTo(1))
assertThat(textView.editableText.getSpanStart(targetSpans[0]), equalTo(0))
assertThat(textView.editableText.getSpanEnd(targetSpans[0]), equalTo(4))
}
}

View File

@ -228,8 +228,7 @@ class EditorDecorationContainerTest {
)
assertEquals(
expected = context.resources.getDimension(R.dimen.default_highlight_content_margin_top)
.toInt(),
expected = 0,
actual = rectWithVerticalQuoteLine.marginBottom
)
@ -293,8 +292,7 @@ class EditorDecorationContainerTest {
)
assertEquals(
expected = context.resources.getDimension(R.dimen.default_highlight_content_margin_top)
.toInt(),
expected = 0,
actual = rectWithVerticalQuoteLine.marginBottom
)
@ -355,8 +353,7 @@ class EditorDecorationContainerTest {
)
assertEquals(
expected = context.resources.getDimension(R.dimen.default_highlight_content_margin_top)
.toInt(),
expected = 0,
actual = rectWithVerticalQuoteLine.marginBottom
)
@ -919,14 +916,12 @@ class EditorDecorationContainerTest {
)
assertEquals(
expected = context.resources.getDimension(R.dimen.default_header_two_extra_space_top)
.toInt(),
expected = 0,
actual = view2.marginTop
)
assertEquals(
expected = context.resources.getDimension(R.dimen.default_header_two_extra_space_bottom)
.toInt(),
expected = 0,
actual = view2.marginBottom
)
}
@ -1008,14 +1003,12 @@ class EditorDecorationContainerTest {
)
assertEquals(
expected = context.resources.getDimension(R.dimen.default_header_three_extra_space_top)
.toInt(),
expected = 0,
actual = view2.marginTop
)
assertEquals(
expected = context.resources.getDimension(R.dimen.default_header_three_extra_space_bottom)
.toInt(),
expected = 0,
actual = view2.marginBottom
)
}
@ -1097,14 +1090,12 @@ class EditorDecorationContainerTest {
)
assertEquals(
expected = context.resources.getDimension(R.dimen.default_header_one_extra_space_top)
.toInt(),
expected = 0,
actual = view2.marginTop
)
assertEquals(
expected = context.resources.getDimension(R.dimen.default_header_one_extra_space_bottom)
.toInt(),
expected = 0,
actual = view2.marginBottom
)
}

View File

@ -48,9 +48,9 @@ import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_MEDIA_CODE
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import timber.log.Timber
import java.text.SimpleDateFormat
import java.util.*
import timber.log.Timber
fun Context.dimen(res: Int): Float {
return resources

View File

@ -64,8 +64,22 @@ inline fun <T> List<T>.replace(replacement: (T) -> T, target: (T) -> Boolean): L
return map { if (target(it)) replacement(it) else it }
}
fun Context.toast(msg: CharSequence) = Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
fun Fragment.toast(msg: CharSequence) = requireActivity().toast(msg)
fun Context.toast(
msg: CharSequence,
duration: Int = Toast.LENGTH_SHORT
) = Toast.makeText(
this,
msg,
duration
).show()
fun Fragment.toast(
msg: CharSequence,
duration: Int = Toast.LENGTH_SHORT
) = requireActivity().toast(
msg = msg,
duration = duration
)
fun Fragment.toast(@StringRes msgId: Int) = requireActivity().toast(requireActivity().getString(msgId))
@ -114,4 +128,12 @@ fun <T> MutableList<T>.moveAfterIndexInLine(
} else {
addAll(size, split.first)
}
}
fun <T> MutableList<T>.moveOnTop(
predicate: (T) -> Boolean
) {
val (first, last) = partition(predicate)
clear()
addAll(first + last)
}

View File

@ -130,4 +130,23 @@ fun EditText.showKeyboard() {
}
}
}
}
inline fun <reified T>RecyclerView.isLastOfType() : Boolean {
val count = itemDecorationCount
return if (count > 0) {
val lastIndex = count - 1
val lastDecorator = getItemDecorationAt(lastIndex)
lastDecorator is T
} else {
false
}
}
fun RecyclerView.lastDecorator() : RecyclerView.ItemDecoration? {
val count = itemDecorationCount
return if (count > 0)
getItemDecorationAt(count - 1)
else
null
}

View File

@ -0,0 +1,14 @@
package com.anytypeio.anytype.core_utils.tools
import android.os.Looper
import javax.inject.Inject
interface ThreadInfo {
fun isOnMainThread(): Boolean
}
class DefaultThreadInfo @Inject constructor() : ThreadInfo {
override fun isOnMainThread(): Boolean {
return Looper.myLooper() == Looper.getMainLooper()
}
}

Some files were not shown because too many files have changed in this diff Show More