Skip to content

Commit 5d7008e

Browse files
LucasXu0appflowy
andauthored
feat: Customize the storage folder path (#1538)
* feat: support customize folder path * feat: add l10n and optimize the logic * chore: code refactor * feat: add file read/write permission for macOS * fix: add toast for restoring path * feat: fetch apps and show them * feat: fetch apps and show them * feat: implement select document logic * feat: l10n and add select item callback * feat: add space between tile * chore: move file exporter to settings * chore: update UI * feat: support customizing folder when launching the app * feat: auto register after customizing folder * feat: l10n * feat: l10n * chore: reinitialize flowy sdk when calling init_sdk * chore: remove flowysdk const keyword to make sure it can be rebuild * chore: clear kv values when user logout * chore: replace current workspace id key in kv.db * feat: add config.name as a part of seesion_cache_key * feat: support open folder when launching * chore: fix some bugs * chore: dart fix & flutter analyze * chore: wrap 'sign up with ramdom user' as interface * feat: dismiss settings view after changing the folder * fix: read kv value after initializaing with new path * chore: remove user_id prefix from current workspace key * fix: move open latest view action to bloc * test: add test utils for integration tests * chore: move integration_test to its parent directory * test: add integration_test ci * test: switch to B from A, then switch to A again * chore: fix warings and format code and fix tests * chore: remove comment out codes * chore: rename some properties name and optimize the logic * chore: abstract logic of settings file exporter widget to cubit * chore: abstract location customizer view from file system view * chore: abstract settings page index to enum type * chore: remove the redundant underscore * test: fix integration test error * chore: enable integration test for windows and ubuntu * feat: abstract file picker as service and mock it under integration test * chore: fix bloc test Co-authored-by: nathan <[email protected]>
1 parent 079fab6 commit 5d7008e

File tree

59 files changed

+1837
-302
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1837
-302
lines changed

.github/workflows/appflowy_editor_test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
- uses: codecov/codecov-action@v3
4545
with:
4646
name: appflowy_editor
47+
flags: appflowy editor
4748
env_vars: ${{ matrix.os }}
4849
fail_ci_if_error: true
4950
verbose: true
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
name: integration test
2+
3+
on:
4+
push:
5+
branches:
6+
- "main"
7+
- "release/*"
8+
paths:
9+
- "frontend/app_flowy/**"
10+
11+
pull_request:
12+
branches:
13+
- "main"
14+
- "release/*"
15+
paths:
16+
- "frontend/app_flowy/**"
17+
18+
env:
19+
CARGO_TERM_COLOR: always
20+
21+
jobs:
22+
tests:
23+
strategy:
24+
matrix:
25+
os: [macos-latest]
26+
27+
runs-on: ${{ matrix.os }}
28+
29+
steps:
30+
- uses: actions/checkout@v2
31+
- uses: actions-rs/toolchain@v1
32+
with:
33+
toolchain: "stable-2022-04-07"
34+
35+
- uses: subosito/flutter-action@v2
36+
with:
37+
channel: "stable"
38+
flutter-version: "3.0.5"
39+
cache: true
40+
41+
- name: Cache Cargo
42+
uses: actions/cache@v2
43+
with:
44+
path: |
45+
~/.cargo
46+
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
47+
48+
- name: Cache Rust
49+
id: cache-rust-target
50+
uses: actions/cache@v2
51+
with:
52+
path: |
53+
frontend/rust-lib/target
54+
shared-lib/target
55+
key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
56+
57+
- name: Setup Environment
58+
run: |
59+
if [ "$RUNNER_OS" == "Linux" ]; then
60+
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
61+
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
62+
sudo apt-get update
63+
sudo apt-get install -y dart curl build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
64+
sudo apt-get install keybinder-3.0
65+
elif [ "$RUNNER_OS" == "Windows" ]; then
66+
vcpkg integrate install
67+
cargo install --force duckscript_cli
68+
elif [ "$RUNNER_OS" == "macOS" ]; then
69+
echo 'do nothing'
70+
fi
71+
shell: bash
72+
73+
- if: steps.cache-cargo.outputs.cache-hit != 'true'
74+
name: Rust Deps
75+
working-directory: frontend
76+
run: |
77+
cargo install cargo-make
78+
cargo make appflowy-deps-tools
79+
80+
- name: Build Test lib
81+
working-directory: frontend
82+
run: |
83+
if [ "$RUNNER_OS" == "Linux" ]; then
84+
cargo make --profile production-linux-x86_64 appflowy
85+
elif [ "$RUNNER_OS" == "macOS" ]; then
86+
cargo make --profile production-mac-x86_64 appflowy
87+
elif [ "$RUNNER_OS" == "Windows" ]; then
88+
cargo make --profile production-windows-x86 appflowy
89+
fi
90+
91+
- name: Config Flutter
92+
run: |
93+
if [ "$RUNNER_OS" == "Linux" ]; then
94+
flutter config --enable-linux-desktop
95+
elif [ "$RUNNER_OS" == "macOS" ]; then
96+
flutter config --enable-macos-desktop
97+
elif [ "$RUNNER_OS" == "Windows" ]; then
98+
flutter config --enable-windows-desktop
99+
fi
100+
shell: bash
101+
102+
- name: Flutter Code Generation
103+
working-directory: frontend/app_flowy
104+
run: |
105+
flutter packages pub get
106+
flutter packages pub run easy_localization:generate -f keys -o locale_keys.g.dart -S assets/translations -s en.json
107+
flutter packages pub run build_runner build --delete-conflicting-outputs
108+
109+
- name: Run AppFlowy tests
110+
working-directory: frontend/app_flowy
111+
run: |
112+
if [ "$RUNNER_OS" == "Linux" ]; then
113+
flutter test integration_test -d Linux --coverage
114+
elif [ "$RUNNER_OS" == "macOS" ]; then
115+
flutter test integration_test -d macOS --coverage
116+
elif [ "$RUNNER_OS" == "Windows" ]; then
117+
flutter test integration_test -d Windows --coverage
118+
fi
119+
shell: bash
120+
121+
- uses: codecov/codecov-action@v3
122+
with:
123+
name: appflowy
124+
flags: appflowy
125+
env_vars: ${{ matrix.os }}
126+
fail_ci_if_error: true
127+
verbose: true
128+

frontend/app_flowy/assets/translations/en.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@
155155
"appearance": "Appearance",
156156
"language": "Language",
157157
"user": "User",
158+
"files": "Files",
158159
"open": "Open Settings"
159160
},
160161
"appearance": {
@@ -164,6 +165,26 @@
164165
"dark": "Dark Mode",
165166
"system": "Adapt to System"
166167
}
168+
},
169+
"files": {
170+
"defaultLocation": "Default location for new notes",
171+
"doubleTapToCopy": "Double tap to copy the path",
172+
"restoreLocation": "Restore to default location",
173+
"customizeLocation": "Customize location",
174+
"restartApp": "Please restart app for the changes to take effect.",
175+
"exportDatabase": "Export databae",
176+
"selectFiles": "Select the files that need to be export",
177+
"createNewFolder": "Create a new folder",
178+
"createNewFolderDesc": "Create a new folder ...",
179+
"open": "Open",
180+
"openFolder": "Open folder",
181+
"openFolderDesc": "Open folder ...",
182+
"folderHintText": "folder name",
183+
"location": "Location",
184+
"locationDesc": "Pick a name for your location",
185+
"browser": "Browser",
186+
"create": "create",
187+
"locationCannotBeEmpty": "Location cannot be empty"
167188
}
168189
},
169190
"grid": {
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import 'package:app_flowy/user/presentation/folder/folder_widget.dart';
2+
import 'package:flowy_infra_ui/style_widget/text_field.dart';
3+
import 'package:flutter_test/flutter_test.dart';
4+
import 'package:integration_test/integration_test.dart';
5+
6+
import 'util/mock/mock_file_picker.dart';
7+
import 'util/util.dart';
8+
9+
void main() {
10+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
11+
12+
group('customize the folder path', () {
13+
const location = 'appflowy';
14+
15+
setUp(() async {
16+
await TestFolder.cleanTestLocation(location);
17+
await TestFolder.setTestLocation(location);
18+
});
19+
20+
tearDown(() async {
21+
await TestFolder.cleanTestLocation(location);
22+
});
23+
24+
tearDownAll(() async {
25+
await TestFolder.cleanTestLocation(null);
26+
});
27+
28+
testWidgets(
29+
'customize folder name and path when launching app in first time',
30+
(tester) async {
31+
const folderName = 'appflowy';
32+
await TestFolder.cleanTestLocation(folderName);
33+
34+
await tester.initializeAppFlowy();
35+
36+
// Click create button
37+
await tester.tapCreateButton();
38+
39+
// Set directory
40+
final cfw = find.byType(CreateFolderWidget);
41+
expect(cfw, findsOneWidget);
42+
final state = tester.state(cfw) as CreateFolderWidgetState;
43+
final dir = await TestFolder.testLocation(null);
44+
state.directory = dir.path;
45+
46+
// input folder name
47+
final ftf = find.byType(FlowyTextField);
48+
expect(ftf, findsOneWidget);
49+
await tester.enterText(ftf, 'appflowy');
50+
51+
// Click create button again
52+
await tester.tapCreateButton();
53+
54+
await tester.expectToSeeWelcomePage();
55+
56+
await TestFolder.cleanTestLocation(folderName);
57+
});
58+
59+
testWidgets('open a new folder when launching app in first time',
60+
(tester) async {
61+
const folderName = 'appflowy';
62+
await TestFolder.cleanTestLocation(folderName);
63+
await TestFolder.setTestLocation(folderName);
64+
65+
await tester.initializeAppFlowy();
66+
67+
// tap open button
68+
await mockGetDirectoryPath(folderName);
69+
await tester.tapOpenFolderButton();
70+
71+
await tester.wait(1000);
72+
await tester.expectToSeeWelcomePage();
73+
74+
await TestFolder.cleanTestLocation(folderName);
75+
});
76+
77+
testWidgets('switch to B from A, then switch to A again', (tester) async {
78+
const String userA = 'userA';
79+
const String userB = 'userB';
80+
81+
await TestFolder.cleanTestLocation(userA);
82+
await TestFolder.setTestLocation(userA);
83+
84+
await tester.initializeAppFlowy();
85+
86+
await tester.tapGoButton();
87+
await tester.expectToSeeWelcomePage();
88+
89+
// swith to user B
90+
{
91+
await tester.openSettings();
92+
await tester.openSettingsPage(SettingsPage.user);
93+
await tester.enterUserName(userA);
94+
95+
await tester.openSettingsPage(SettingsPage.files);
96+
await tester.pumpAndSettle();
97+
98+
// mock the file_picker result
99+
await mockGetDirectoryPath(userB);
100+
await tester.tapCustomLocationButton();
101+
await tester.pumpAndSettle();
102+
await tester.expectToSeeWelcomePage();
103+
}
104+
105+
// switch to the userA
106+
{
107+
await tester.openSettings();
108+
await tester.openSettingsPage(SettingsPage.user);
109+
await tester.enterUserName(userB);
110+
111+
await tester.openSettingsPage(SettingsPage.files);
112+
await tester.pumpAndSettle();
113+
114+
// mock the file_picker result
115+
await mockGetDirectoryPath(userA);
116+
await tester.tapCustomLocationButton();
117+
118+
await tester.pumpAndSettle();
119+
await tester.expectToSeeWelcomePage();
120+
expect(find.textContaining(userA), findsOneWidget);
121+
}
122+
123+
// swith to the userB again
124+
{
125+
await tester.openSettings();
126+
await tester.openSettingsPage(SettingsPage.files);
127+
await tester.pumpAndSettle();
128+
129+
// mock the file_picker result
130+
await mockGetDirectoryPath(userB);
131+
await tester.tapCustomLocationButton();
132+
133+
await tester.pumpAndSettle();
134+
await tester.expectToSeeWelcomePage();
135+
expect(find.textContaining(userB), findsOneWidget);
136+
}
137+
138+
await TestFolder.cleanTestLocation(userA);
139+
await TestFolder.cleanTestLocation(userB);
140+
});
141+
142+
testWidgets('reset to default location', (tester) async {
143+
await tester.initializeAppFlowy();
144+
145+
await tester.tapGoButton();
146+
147+
// home and readme document
148+
await tester.expectToSeeWelcomePage();
149+
150+
// open settings and restore the location
151+
await tester.openSettings();
152+
await tester.openSettingsPage(SettingsPage.files);
153+
await tester.restoreLocation();
154+
155+
expect(
156+
await TestFolder.defaultDevelopmentLocation(),
157+
await TestFolder.currentLocation(),
158+
);
159+
});
160+
});
161+
}

0 commit comments

Comments
 (0)