Compare commits

..

111 Commits
auth ... master

Author SHA1 Message Date
2880dd7a03 tabSize: 4 2023-12-09 11:29:00 +01:00
10c0387eb0 updates 2023-12-08 22:42:48 +01:00
aee6ecfbfe upgrade daisyui 2023-12-08 22:06:41 +01:00
e2472c522e update DaisyUI 2023-07-26 17:55:20 +02:00
9933f730ad fix active color 2023-06-14 20:39:36 +02:00
a4c25b8342 update to DaisyUI 3.1.0 2023-06-14 00:06:27 +02:00
4641c09b5d disable GH and MS auth - not working native 2023-06-08 23:16:46 +02:00
8b4744446f fixes 2023-06-08 01:54:12 +02:00
cf287f31ab fixes 2023-06-08 22:22:29 +02:00
3e6fb75575 Merge branch 'master' of ssh://git.crapts.org:10022/marco/contexted-v3 2023-06-08 00:46:35 +02:00
2e5b5cf74d improve haptics 2023-06-08 00:46:17 +02:00
dab21cdc44 small ui fix 2023-06-08 00:37:09 +02:00
8828c98795 android icon logos 2023-06-07 20:01:38 +02:00
ddd6a33d56 fix auth modal on mobile 2023-06-06 23:45:50 +02:00
339e74e53e ios icons and splash 2023-06-06 23:39:13 +02:00
5b267356db add logos and splashscreen plugin 2023-06-06 23:11:58 +02:00
2c830c61ea small ui fixes 2023-06-06 22:27:00 +02:00
a617758459 add iOS 2023-06-06 21:38:32 +02:00
83d63038c5 remove statusbar and keyboard plugin errors 2023-06-06 20:41:31 +02:00
a10bfcf42d remove firebaseapp global 2023-06-06 20:40:01 +02:00
6ab24d6236 bug fixes 2023-06-06 20:29:42 +02:00
dc8dcb1867 complete capacitor firebase integration 2023-06-06 20:19:50 +02:00
53b364e798 configure Firebase Android 2023-06-06 19:47:48 +02:00
882c55373b capacitor firebase auth 2023-06-06 19:33:43 +02:00
cd807d7ff0 small ui fixes 2023-06-03 23:04:57 +02:00
33f23f838d run prettier 2023-06-02 00:04:18 +02:00
c984cc7a7b fix keyboard and haptics 2023-06-01 23:17:41 +02:00
a66b838f46 fix statusbar 2023-06-01 22:08:55 +02:00
8248c4ad49 fix table layout 2023-06-01 22:02:54 +02:00
27ba4c6b3d fix iphone padding 2023-06-01 21:32:25 +02:00
Marco Crapts
8fe704744f fix iphone padding 2023-06-01 21:31:18 +02:00
Marco Crapts
2f4c4aa7fd update ios project 2023-06-01 17:41:41 +02:00
19e99fcc28 daisyUI 3.0 2023-06-01 13:42:35 +02:00
1e742826cf add Android app link 2023-06-01 00:21:14 +02:00
af054ac61c fix list view 2023-05-31 21:26:59 +02:00
c57d0be562 redirect signin 2023-05-31 21:14:59 +02:00
de12d22434 haptics 2023-05-31 20:14:46 +02:00
9cd6382bfc native dialog on android & ios 2023-05-31 00:29:10 +02:00
ec802259a8 create ios and android targets 2023-05-30 23:06:11 +02:00
4faaf8c613 Merge branch 'master' into capacitor 2023-05-30 22:54:27 +02:00
f634e8cd76 persistent local cache 2023-05-30 22:54:14 +02:00
ab54088ffb update .env 2023-05-30 00:56:29 +02:00
45fd39022d add Capacitor 2023-05-30 00:45:45 +02:00
bd9b926550 public Firebase keys 2023-05-30 00:19:34 +02:00
70b3319778 small update 2023-05-29 22:10:27 +02:00
0fac36b5b7 ui improvements 2023-05-29 01:54:59 +02:00
33b38774df delete account 2023-05-29 01:48:46 +02:00
21222abad4 delete account ui functionality 2023-05-28 23:29:42 +02:00
06bfd4cd9e delete account ui functionality 2023-05-28 23:21:59 +02:00
b3f740770a slight refactoring 2023-05-28 22:23:41 +02:00
b4eab2d8e8 enable/disable e2e encryption 2023-05-28 21:45:47 +02:00
77f5bafa2f settings modal 2023-05-28 18:35:27 +02:00
87729c9c00 refactor to UI components 2023-05-26 01:44:20 +02:00
9ca0bba526 refactor to UI components 2023-05-26 00:50:19 +02:00
b89816ecd5 fix tailwind css 2023-05-26 19:29:52 +02:00
dd811c3f66 more ui components 2023-05-26 19:21:27 +02:00
2088b12242 more ui components 2023-05-26 17:32:43 +02:00
c3bd807bff refactor to ui components 2023-05-26 16:43:12 +02:00
a642fcd1fb collapse sidebar on resize 2023-05-25 22:56:56 +02:00
e384495e73 delete notes & sync to firebase 2023-05-25 22:29:40 +02:00
2b7ba1faf7 passphrase validation 2023-05-25 20:29:24 +02:00
3db4fe87a6 fix overlay 2023-05-25 17:10:57 +02:00
bf35285272 fix layout 2023-05-25 16:41:05 +02:00
6b7debc811 fix layout 2023-05-25 10:50:40 +02:00
84376263c8 fix click outside 2023-05-25 00:11:18 +02:00
d62444f65e store settings in localstorage 2023-05-24 23:30:28 +02:00
d6480a7a37 improve sidebar mobile 2023-05-23 23:01:02 +02:00
eb02ab7648 click outside sidebar collapse on mobile 2023-05-23 22:49:36 +02:00
ef1830aaf1 less recent items 2023-05-23 22:13:25 +02:00
511b53f073 tabs 2023-05-23 22:12:23 +02:00
f0d302a46a update search results 2023-05-23 21:53:55 +02:00
65694e530c update search results 2023-05-23 21:16:48 +02:00
b9f283a180 refactor colors 2023-05-23 13:29:00 +02:00
bd1eadb7cb better animated sidebar 2023-05-23 13:18:43 +02:00
aa77296d00 passphrase prompt 2023-05-23 00:44:51 +02:00
029893830f improve loading 2023-05-22 20:56:54 +02:00
7ca2b25e8f decrypt notes 2023-05-22 20:48:53 +02:00
55a281581e cleanup css 2023-05-22 10:58:29 +02:00
1b46687b6a fix stretch + default notes 2023-05-22 10:02:52 +02:00
833d1b34b7 try catch json parse 2023-05-22 09:08:06 +02:00
15786aafa1 add word count to note list 2023-05-22 00:27:06 +02:00
d28d57388b update signout dialog 2023-05-22 00:23:25 +02:00
c2cbe513d1 sync with local storage 2023-05-22 00:19:46 +02:00
6dd8c2d524 default sidebar collapse mobile 2023-05-21 13:56:42 +02:00
7eeedd0eeb fix jumpy safari 2023-05-21 13:28:02 +02:00
b7c38a8b2a fix note title too wide 2023-05-21 12:00:20 +02:00
84725cd1c2 single firebaseui import 2023-05-21 11:28:19 +02:00
e456ed2f5c rename close to cancel 2023-05-21 01:52:08 +02:00
1160165cf5 add modal titles 2023-05-20 15:41:37 +02:00
6c4bd5b0ae signout icon 2023-05-20 15:33:51 +02:00
7a4748c2a8 dropdown menu 2023-05-20 15:26:15 +02:00
745cf7b648 skeleton loader 2023-05-20 15:00:55 +02:00
b71bffc064 skeleton loader 2023-05-20 14:54:41 +02:00
7c40017b05 show auth modal in case of redirect 2023-05-20 12:38:55 +02:00
b4c9076986 fix auth 2023-05-20 12:14:48 +02:00
95359c792a auth loader 2023-05-20 07:13:06 +02:00
893b24354a working auth 2023-05-20 06:53:47 +02:00
684a1d76bf add auth modal 2023-05-20 04:05:36 +02:00
2990e5ec89 create link without autocomplete 2023-05-20 02:35:11 +02:00
2fc81d5da6 add firebase 2023-05-19 23:38:43 +02:00
0f2b17dab9 mindmap 2023-05-19 22:07:58 +02:00
99290b807a listview 2023-05-19 19:12:55 +02:00
83c58d5aca update project config to match official create vue recommendations 2023-05-19 14:16:44 +02:00
e491f52e26 update list view 2023-05-19 11:00:18 +02:00
95648988ef listview component 2023-05-17 07:43:53 +02:00
0c4c7782e0 improve scrolling / layout 2023-05-17 07:24:26 +02:00
e11438c952 flex-grow instead of flex-1 2023-05-17 05:27:59 +02:00
41c0ed5811 add favicons 2023-05-17 04:20:08 +02:00
8774d74163 create note on click if does not exist 2023-05-17 03:47:48 +02:00
add3e944f6 improved autocomplete 2023-05-17 03:42:10 +02:00
a189dac30f rename types to types.d.ts & fix Contexted logo 2023-05-17 15:27:12 +02:00
99 changed files with 3083 additions and 2618 deletions

View File

@@ -1,7 +1,7 @@
{ {
"$schema": "https://json.schemastore.org/prettierrc", "$schema": "https://json.schemastore.org/prettierrc",
"semi": false, "semi": false,
"tabWidth": 2, "tabWidth": 4,
"singleQuote": true, "singleQuote": true,
"printWidth": 100, "printWidth": 100,
"trailingComma": "none", "trailingComma": "none",

View File

@@ -1,2 +1,11 @@
pretty: pretty:
npx prettier --write "./src/**/*.(ts|vue)" npx prettier --write "./src/**/*.(ts|vue)"
check:
npm run type-check && npm run lint
local: check
npm run local
deploy: check
npm run deploy

View File

@@ -9,9 +9,11 @@ android {
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies { dependencies {
implementation project(':capacitor-firebase-authentication')
implementation project(':capacitor-dialog') implementation project(':capacitor-dialog')
implementation project(':capacitor-haptics') implementation project(':capacitor-haptics')
implementation project(':capacitor-keyboard') implementation project(':capacitor-keyboard')
implementation project(':capacitor-splash-screen')
implementation project(':capacitor-status-bar') implementation project(':capacitor-status-bar')
} }

View File

@@ -0,0 +1,40 @@
{
"project_info": {
"project_number": "1048273547256",
"firebase_url": "https://contexted-f8b4e.firebaseio.com",
"project_id": "contexted-f8b4e",
"storage_bucket": "contexted-f8b4e.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:1048273547256:android:878e44df2e5f0b200f700d",
"android_client_info": {
"package_name": "com.contexted.app"
}
},
"oauth_client": [
{
"client_id": "1048273547256-mq8b1irdiovpblrdcuf8bb30is29rfm9.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyCJ-A3ziDw_2qMWnP2uXFDoY2O8DrUlXp8"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "1048273547256-mq8b1irdiovpblrdcuf8bb30is29rfm9.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

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

View File

@@ -2,6 +2,9 @@
include ':capacitor-android' include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
include ':capacitor-firebase-authentication'
project(':capacitor-firebase-authentication').projectDir = new File('../node_modules/@capacitor-firebase/authentication/android')
include ':capacitor-dialog' include ':capacitor-dialog'
project(':capacitor-dialog').projectDir = new File('../node_modules/@capacitor/dialog/android') project(':capacitor-dialog').projectDir = new File('../node_modules/@capacitor/dialog/android')
@@ -11,5 +14,8 @@ project(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/
include ':capacitor-keyboard' include ':capacitor-keyboard'
project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android') project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android')
include ':capacitor-splash-screen'
project(':capacitor-splash-screen').projectDir = new File('../node_modules/@capacitor/splash-screen/android')
include ':capacitor-status-bar' include ':capacitor-status-bar'
project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android') project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')

View File

@@ -13,4 +13,5 @@ ext {
androidxJunitVersion = '1.1.5' androidxJunitVersion = '1.1.5'
androidxEspressoCoreVersion = '3.5.1' androidxEspressoCoreVersion = '3.5.1'
cordovaAndroidVersion = '10.1.1' cordovaAndroidVersion = '10.1.1'
rgcfaIncludeGoogle = true
} }

View File

@@ -8,8 +8,15 @@ const config: CapacitorConfig = {
androidScheme: 'https' androidScheme: 'https'
}, },
plugins: { plugins: {
SplashScreen: {
backgroundColor: '#1E4BC4'
},
Keyboard: { Keyboard: {
resize: 'native' resize: 'native'
},
FirebaseAuthentication: {
skipNativeAuth: false,
providers: ['google.com', 'microsoft.com', 'github.com']
} }
} }
} }

View File

@@ -15,6 +15,7 @@
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; }; 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; }; 50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; }; A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
A9A186E62A2FB826009CBA16 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = A9A186E52A2FB826009CBA16 /* GoogleService-Info.plist */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
@@ -27,6 +28,7 @@
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; }; 50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
A9A186E52A2FB826009CBA16 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; }; AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; }; AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; }; FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
@@ -74,6 +76,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
50379B222058CBB4000EE86E /* capacitor.config.json */, 50379B222058CBB4000EE86E /* capacitor.config.json */,
A9A186E52A2FB826009CBA16 /* GoogleService-Info.plist */,
504EC3071FED79650016851F /* AppDelegate.swift */, 504EC3071FED79650016851F /* AppDelegate.swift */,
504EC30B1FED79650016851F /* Main.storyboard */, 504EC30B1FED79650016851F /* Main.storyboard */,
504EC30E1FED79650016851F /* Assets.xcassets */, 504EC30E1FED79650016851F /* Assets.xcassets */,
@@ -161,6 +164,7 @@
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */, 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,
504EC30D1FED79650016851F /* Main.storyboard in Resources */, 504EC30D1FED79650016851F /* Main.storyboard in Resources */,
2FAD9763203C412B000D30F8 /* config.xml in Resources */, 2FAD9763203C412B000D30F8 /* config.xml in Resources */,
A9A186E62A2FB826009CBA16 /* GoogleService-Info.plist in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -347,6 +351,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 5XQS3G6YV7;
INFOPLIST_FILE = App/Info.plist; INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -367,6 +372,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 5XQS3G6YV7;
INFOPLIST_FILE = App/Info.plist; INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

View File

@@ -1,7 +1,7 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "AppIcon-512@2x.png", "filename" : "AppIcon-1024@2x.png",
"idiom" : "universal", "idiom" : "universal",
"platform" : "ios", "platform" : "ios",
"size" : "1024x1024" "size" : "1024x1024"

View File

@@ -1,23 +1,23 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "splash-landscape@1x.png",
"idiom" : "universal", "idiom" : "universal",
"filename" : "splash-2732x2732-2.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"filename" : "splash-landscape@2x.png",
"idiom" : "universal", "idiom" : "universal",
"filename" : "splash-2732x2732-1.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"filename" : "splash-landscape@3x.png",
"idiom" : "universal", "idiom" : "universal",
"filename" : "splash-2732x2732.png",
"scale" : "3x" "scale" : "3x"
} }
], ],
"info" : { "info" : {
"version" : 1, "author" : "xcode",
"author" : "xcode" "version" : 1
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CLIENT_ID</key>
<string>1048273547256-fes4qqj6po81ki21doggnst5ptjsa940.apps.googleusercontent.com</string>
<key>REVERSED_CLIENT_ID</key>
<string>com.googleusercontent.apps.1048273547256-fes4qqj6po81ki21doggnst5ptjsa940</string>
<key>ANDROID_CLIENT_ID</key>
<string>1048273547256-qq9j4hcvni377p8cva7v10u9us1pjrc9.apps.googleusercontent.com</string>
<key>API_KEY</key>
<string>AIzaSyCkN_Fm0wGcDZJJ6ltz5NHDvs9zPgKiSTQ</string>
<key>GCM_SENDER_ID</key>
<string>1048273547256</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>com.contexted.app</string>
<key>PROJECT_ID</key>
<string>contexted-f8b4e</string>
<key>STORAGE_BUCKET</key>
<string>contexted-f8b4e.appspot.com</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:1048273547256:ios:e57d78bd483100900f700d</string>
<key>DATABASE_URL</key>
<string>https://contexted-f8b4e.firebaseio.com</string>
</dict>
</plist>

View File

@@ -18,6 +18,19 @@
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string> <string>$(MARKETING_VERSION)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>Firebase</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.1048273547256-fes4qqj6po81ki21doggnst5ptjsa940</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>

View File

@@ -11,17 +11,29 @@ install! 'cocoapods', :disable_input_output_paths => true
def capacitor_pods def capacitor_pods
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios' pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios' pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorFirebaseAuthentication', :path => '../../node_modules/@capacitor-firebase/authentication'
pod 'CapacitorDialog', :path => '../../node_modules/@capacitor/dialog' pod 'CapacitorDialog', :path => '../../node_modules/@capacitor/dialog'
pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics' pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard' pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar' pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
end end
target 'App' do target 'App' do
capacitor_pods capacitor_pods
# Add your Pods here # Add your Pods here
pod 'CapacitorFirebaseAuthentication/Google', :path => '../../node_modules/@capacitor-firebase/authentication'
end end
post_install do |installer| post_install do |installer|
assertDeploymentTarget(installer) assertDeploymentTarget(installer)
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
target.build_configurations.each do |config|
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end
end
end
end end

View File

@@ -1,24 +1,96 @@
PODS: PODS:
- AppAuth (1.6.2):
- AppAuth/Core (= 1.6.2)
- AppAuth/ExternalUserAgent (= 1.6.2)
- AppAuth/Core (1.6.2)
- AppAuth/ExternalUserAgent (1.6.2):
- AppAuth/Core
- Capacitor (5.0.4): - Capacitor (5.0.4):
- CapacitorCordova - CapacitorCordova
- CapacitorCordova (5.0.4) - CapacitorCordova (5.0.4)
- CapacitorDialog (5.0.2): - CapacitorDialog (5.0.2):
- Capacitor - Capacitor
- CapacitorFirebaseAuthentication (5.0.0):
- Capacitor
- CapacitorFirebaseAuthentication/Lite (= 5.0.0)
- FirebaseAuth (= 10.8.0)
- CapacitorFirebaseAuthentication/Google (5.0.0):
- Capacitor
- FirebaseAuth (= 10.8.0)
- GoogleSignIn (= 7.0.0)
- CapacitorFirebaseAuthentication/Lite (5.0.0):
- Capacitor
- FirebaseAuth (= 10.8.0)
- CapacitorHaptics (5.0.2): - CapacitorHaptics (5.0.2):
- Capacitor - Capacitor
- CapacitorKeyboard (5.0.2): - CapacitorKeyboard (5.0.2):
- Capacitor - Capacitor
- CapacitorSplashScreen (5.0.2):
- Capacitor
- CapacitorStatusBar (5.0.2): - CapacitorStatusBar (5.0.2):
- Capacitor - Capacitor
- FirebaseAppCheckInterop (10.10.0)
- FirebaseAuth (10.8.0):
- FirebaseAppCheckInterop (~> 10.0)
- FirebaseCore (~> 10.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
- GoogleUtilities/Environment (~> 7.8)
- GTMSessionFetcher/Core (< 4.0, >= 2.1)
- FirebaseCore (10.10.0):
- FirebaseCoreInternal (~> 10.0)
- GoogleUtilities/Environment (~> 7.8)
- GoogleUtilities/Logger (~> 7.8)
- FirebaseCoreInternal (10.10.0):
- "GoogleUtilities/NSData+zlib (~> 7.8)"
- GoogleSignIn (7.0.0):
- AppAuth (~> 1.5)
- GTMAppAuth (< 3.0, >= 1.3)
- GTMSessionFetcher/Core (< 4.0, >= 1.1)
- GoogleUtilities/AppDelegateSwizzler (7.11.1):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Environment (7.11.1):
- PromisesObjC (< 3.0, >= 1.2)
- GoogleUtilities/Logger (7.11.1):
- GoogleUtilities/Environment
- GoogleUtilities/Network (7.11.1):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (7.11.1)"
- GoogleUtilities/Reachability (7.11.1):
- GoogleUtilities/Logger
- GTMAppAuth (2.0.0):
- AppAuth/Core (~> 1.6)
- GTMSessionFetcher/Core (< 4.0, >= 1.5)
- GTMSessionFetcher/Core (3.1.1)
- PromisesObjC (2.2.0)
DEPENDENCIES: DEPENDENCIES:
- "Capacitor (from `../../node_modules/@capacitor/ios`)" - "Capacitor (from `../../node_modules/@capacitor/ios`)"
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)" - "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
- "CapacitorDialog (from `../../node_modules/@capacitor/dialog`)" - "CapacitorDialog (from `../../node_modules/@capacitor/dialog`)"
- "CapacitorFirebaseAuthentication (from `../../node_modules/@capacitor-firebase/authentication`)"
- "CapacitorFirebaseAuthentication/Google (from `../../node_modules/@capacitor-firebase/authentication`)"
- "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)" - "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)"
- "CapacitorKeyboard (from `../../node_modules/@capacitor/keyboard`)" - "CapacitorKeyboard (from `../../node_modules/@capacitor/keyboard`)"
- "CapacitorSplashScreen (from `../../node_modules/@capacitor/splash-screen`)"
- "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)" - "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)"
SPEC REPOS:
trunk:
- AppAuth
- FirebaseAppCheckInterop
- FirebaseAuth
- FirebaseCore
- FirebaseCoreInternal
- GoogleSignIn
- GoogleUtilities
- GTMAppAuth
- GTMSessionFetcher
- PromisesObjC
EXTERNAL SOURCES: EXTERNAL SOURCES:
Capacitor: Capacitor:
:path: "../../node_modules/@capacitor/ios" :path: "../../node_modules/@capacitor/ios"
@@ -26,21 +98,37 @@ EXTERNAL SOURCES:
:path: "../../node_modules/@capacitor/ios" :path: "../../node_modules/@capacitor/ios"
CapacitorDialog: CapacitorDialog:
:path: "../../node_modules/@capacitor/dialog" :path: "../../node_modules/@capacitor/dialog"
CapacitorFirebaseAuthentication:
:path: "../../node_modules/@capacitor-firebase/authentication"
CapacitorHaptics: CapacitorHaptics:
:path: "../../node_modules/@capacitor/haptics" :path: "../../node_modules/@capacitor/haptics"
CapacitorKeyboard: CapacitorKeyboard:
:path: "../../node_modules/@capacitor/keyboard" :path: "../../node_modules/@capacitor/keyboard"
CapacitorSplashScreen:
:path: "../../node_modules/@capacitor/splash-screen"
CapacitorStatusBar: CapacitorStatusBar:
:path: "../../node_modules/@capacitor/status-bar" :path: "../../node_modules/@capacitor/status-bar"
SPEC CHECKSUMS: SPEC CHECKSUMS:
AppAuth: 3bb1d1cd9340bd09f5ed189fb00b1cc28e1e8570
Capacitor: d3d4463573438b9fa65326d1f3549da6f4c21634 Capacitor: d3d4463573438b9fa65326d1f3549da6f4c21634
CapacitorCordova: b1fe6bf1f36974a8e4a9044b342d22d49c0996d6 CapacitorCordova: b1fe6bf1f36974a8e4a9044b342d22d49c0996d6
CapacitorDialog: 01c49f7f4b37e7ad59e38fd317a6e5f006f23cdc CapacitorDialog: 01c49f7f4b37e7ad59e38fd317a6e5f006f23cdc
CapacitorFirebaseAuthentication: f2e3c2a7488b87078025855588670840f93a721e
CapacitorHaptics: 864585542a435bd41eaabf7f30d9ff5ec03024d3 CapacitorHaptics: 864585542a435bd41eaabf7f30d9ff5ec03024d3
CapacitorKeyboard: e628d4e66d621c69e449945ebabded17c5b9c2e8 CapacitorKeyboard: e628d4e66d621c69e449945ebabded17c5b9c2e8
CapacitorSplashScreen: bd2a056394ba0b8807e7bb3e746424f67c426e03
CapacitorStatusBar: 48f2899f6846cc7d8431b251ebfc58e1c10e3d58 CapacitorStatusBar: 48f2899f6846cc7d8431b251ebfc58e1c10e3d58
FirebaseAppCheckInterop: 7d3521f56872cf74a01792c0a095a30e054ff6ae
FirebaseAuth: 28e6fff787467cd15ab51c8c7aa904003b2f57aa
FirebaseCore: d027ff503d37edb78db98429b11f580a24a7df2a
FirebaseCoreInternal: 971029061d326000d65bfdc21f5502c75c8b0893
GoogleSignIn: b232380cf495a429b8095d3178a8d5855b42e842
GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749
GTMAppAuth: 99fb010047ba3973b7026e45393f51f27ab965ae
GTMSessionFetcher: e8647203b65cee28c5f73d0f473d096653945e72
PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef
PODFILE CHECKSUM: b469cdc64593e190968b9aa15066224f10938107 PODFILE CHECKSUM: 9a19ff50409d024bca91266c62454036ebb27258
COCOAPODS: 1.12.1 COCOAPODS: 1.12.1

337
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,18 +9,20 @@
"preview": "vite preview", "preview": "vite preview",
"build-only": "vite build", "build-only": "vite build",
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false", "type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", "lint": "eslint ./src --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier --write src/", "format": "prettier --write src/",
"local": "npm run build && firebase serve --only hosting", "local": "npm run build && firebase serve --only hosting",
"deploy": "npm run build && firebase deploy --only hosting:contexted-v3" "deploy": "npm run build && firebase deploy --only hosting:contexted-v3"
}, },
"dependencies": { "dependencies": {
"@capacitor-firebase/authentication": "^5.0.0",
"@capacitor/android": "^5.0.4", "@capacitor/android": "^5.0.4",
"@capacitor/core": "^5.0.4", "@capacitor/core": "^5.0.4",
"@capacitor/dialog": "^5.0.2", "@capacitor/dialog": "^5.0.2",
"@capacitor/haptics": "^5.0.2", "@capacitor/haptics": "^5.0.2",
"@capacitor/ios": "^5.0.4", "@capacitor/ios": "^5.0.4",
"@capacitor/keyboard": "^5.0.2", "@capacitor/keyboard": "^5.0.2",
"@capacitor/splash-screen": "^5.0.2",
"@capacitor/status-bar": "^5.0.2", "@capacitor/status-bar": "^5.0.2",
"@ckeditor/ckeditor5-autoformat": "^37.1.0", "@ckeditor/ckeditor5-autoformat": "^37.1.0",
"@ckeditor/ckeditor5-basic-styles": "^37.1.0", "@ckeditor/ckeditor5-basic-styles": "^37.1.0",
@@ -40,7 +42,6 @@
"@vueuse/core": "^10.1.2", "@vueuse/core": "^10.1.2",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"cytoscape": "^3.25.0", "cytoscape": "^3.25.0",
"daisyui": "^3.0.0",
"date-fns": "^2.29.3", "date-fns": "^2.29.3",
"dompurify": "^3.0.2", "dompurify": "^3.0.2",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
@@ -52,8 +53,7 @@
"marked": "^4.3.0", "marked": "^4.3.0",
"shortid": "^2.2.16", "shortid": "^2.2.16",
"turndown": "^7.1.2", "turndown": "^7.1.2",
"vue": "^3.3.4", "vue": "^3.3.4"
"vue-virtual-scroller": "^2.0.0-beta.8"
}, },
"devDependencies": { "devDependencies": {
"@capacitor/cli": "^5.0.4", "@capacitor/cli": "^5.0.4",
@@ -72,6 +72,7 @@
"@vue/eslint-config-typescript": "^11.0.3", "@vue/eslint-config-typescript": "^11.0.3",
"@vue/tsconfig": "^0.4.0", "@vue/tsconfig": "^0.4.0",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"daisyui": "^4.4.19",
"eslint": "^8.40.0", "eslint": "^8.40.0",
"eslint-plugin-vue": "^9.13.0", "eslint-plugin-vue": "^9.13.0",
"firebase-tools": "^12.1.0", "firebase-tools": "^12.1.0",

View File

@@ -11,12 +11,13 @@ import {
import { initializeSettings } from '@/composables/useSettings' import { initializeSettings } from '@/composables/useSettings'
import { windowIsMobile } from '@/utils/helpers' import { windowIsMobile } from '@/utils/helpers'
import SideBar from '@/components/SideBar.vue' import SideBar from '@/components/SideBar.vue'
import firebase from 'firebase/compat/app'
import * as firebaseui from 'firebaseui'
import { useWindowSize } from '@vueuse/core' import { useWindowSize } from '@vueuse/core'
import { SplashScreen } from '@capacitor/splash-screen'
initializeSettings() initializeSettings()
onMounted(() => SplashScreen.hide())
const sideBarCollapsed = ref<boolean>(windowIsMobile()) const sideBarCollapsed = ref<boolean>(windowIsMobile())
const { width } = useWindowSize() const { width } = useWindowSize()
@@ -26,9 +27,9 @@ watch(width, () => (sideBarCollapsed.value = windowIsMobile()))
// const ListView = defineAsyncComponent(() => import('@/components/ViewModes/ListView.vue')) // const ListView = defineAsyncComponent(() => import('@/components/ViewModes/ListView.vue'))
// const Mindmap = defineAsyncComponent(() => import('@/components/ViewModes/Mindmap.vue')) // const Mindmap = defineAsyncComponent(() => import('@/components/ViewModes/Mindmap.vue'))
const firebaseAuthUI = // const firebaseAuthUI =
firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(firebase.auth()) // firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(firebase.auth())
provide('firebaseAuthUI', firebaseAuthUI) // provide('firebaseAuthUI', firebaseAuthUI)
watch( watch(
[activeNotesSource, encryptionKey], [activeNotesSource, encryptionKey],
@@ -91,10 +92,10 @@ const topBarHeightWithSafeArea = computed(() => `calc(${topBarHeight}px + var(--
/> />
</Transition> </Transition>
<main <main
class="transition[margin-left] z-10 mx-auto flex h-full w-full max-w-app flex-col overflow-y-auto border-x-[1px] bg-white pb-[var(--safe-area-bottom)] duration-200 ease-out" class="transition[margin-left] z-10 mx-auto flex h-full w-full max-w-app flex-col overflow-y-auto bg-white pb-[var(--safe-area-bottom)] duration-200 ease-out sm:border-x-[1px]"
:class="sideBarCollapsed ? 'ml-0' : 'sm:ml-sidebar'" :class="sideBarCollapsed ? 'ml-0' : 'sm:ml-sidebar'"
> >
<div class="flex h-full w-full px-10 py-6 max-sm:px-4 max-sm:py-3"> <div class="flex w-full flex-grow px-10 py-6 max-sm:px-4 max-sm:py-3">
<template v-if="!loading"> <template v-if="!loading">
<Note <Note
v-if="activeViewMode.name === 'Note' && activeNote" v-if="activeViewMode.name === 'Note' && activeNote"
@@ -110,12 +111,12 @@ const topBarHeightWithSafeArea = computed(() => `calc(${topBarHeight}px + var(--
</div> </div>
</main> </main>
</div> </div>
<UIModal :open="passphraseRequired" :persistent="true"> <UIModal :open="passphraseRequired" persistent>
<template #title>Enter your passphrase</template> <template #title>Enter your passphrase</template>
<template #default="{ close }"> <template #default="{ close }">
<div> <div>
Your notes are encrypted. Please enter your encryption key passphrase to decrypt your cloud Your notes are encrypted. Please enter your encryption key passphrase to decrypt
notes. your cloud notes.
</div> </div>
<form @submit.prevent="submitPassphrase(close)"> <form @submit.prevent="submitPassphrase(close)">
<UIInputText <UIInputText

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@@ -124,7 +124,9 @@ export default defineComponent({
const [major] = CKEDITOR_VERSION.split('.').map(Number) const [major] = CKEDITOR_VERSION.split('.').map(Number)
if (major < 37) { if (major < 37) {
console.warn('The <CKEditor> component requires using CKEditor 5 in version 37 or higher.') console.warn(
'The <CKEditor> component requires using CKEditor 5 in version 37 or higher.'
)
} }
} else { } else {
console.warn('Cannot find the "CKEDITOR_VERSION" in the "window" scope.') console.warn('Cannot find the "CKEDITOR_VERSION" in the "window" scope.')

View File

@@ -116,10 +116,16 @@ export default class ContextedLinkEditing extends Plugin {
} }
model.enqueueChange((writer) => { model.enqueueChange((writer) => {
const rangesToFormat = format.map((array) => const rangesToFormat = format.map((array) =>
model.createRange(range.start.getShiftedBy(array[0]), range.start.getShiftedBy(array[1])) model.createRange(
range.start.getShiftedBy(array[0]),
range.start.getShiftedBy(array[1])
)
) )
const validRanges = editor.model.schema.getValidRanges(rangesToFormat, 'contextedLink') const validRanges = editor.model.schema.getValidRanges(
rangesToFormat,
'contextedLink'
)
for (const range of validRanges) { for (const range of validRanges) {
for (const item of range.getItems()) { for (const item of range.getItems()) {
if ((item as any).data) { if ((item as any).data) {
@@ -145,8 +151,8 @@ export default class ContextedLinkEditing extends Plugin {
} }
const keyCodes = [...keyCodesConfirm, ...keyCodesCycle] const keyCodes = [...keyCodesConfirm, ...keyCodesCycle]
const selection = editor.model.document.selection const selection = editor.model.document.selection
const selectionInContextedLink = ['contextedLink', 'autocomplete'].some((attribute) => const selectionInContextedLink = ['contextedLink', 'autocomplete'].some(
selection.hasAttribute(attribute) (attribute) => selection.hasAttribute(attribute)
) )
if (selectionInContextedLink && keyCodes.includes(keyCode)) { if (selectionInContextedLink && keyCodes.includes(keyCode)) {
if (selection.hasAttribute('contextedLink')) { if (selection.hasAttribute('contextedLink')) {
@@ -216,7 +222,10 @@ function fireAutocompleteEvent(editor: any, show: boolean, autocompleteNode?: an
event = { event = {
position: getNodePosition( position: getNodePosition(
editor, editor,
editor.model.createPositionFromPath(autocompleteNode.root, autocompleteNode.getPath()) editor.model.createPositionFromPath(
autocompleteNode.root,
autocompleteNode.getPath()
)
), ),
autocompleteText: autocompleteNode.data, autocompleteText: autocompleteNode.data,
domElement, domElement,

View File

@@ -2,23 +2,17 @@
import firebase from 'firebase/compat/app' import firebase from 'firebase/compat/app'
import 'firebase/compat/auth' import 'firebase/compat/auth'
import 'firebaseui/dist/firebaseui.css' import 'firebaseui/dist/firebaseui.css'
import { import * as firebaseui from 'firebaseui'
getAuth, import { FirebaseAuthentication } from '@capacitor-firebase/authentication'
signInWithRedirect, import { getAuth, GoogleAuthProvider, signInWithCredential } from 'firebase/auth'
GoogleAuthProvider,
GithubAuthProvider,
OAuthProvider
} from 'firebase/auth'
// const props = defineProps<{
// authenticating?: boolean
// }>()
const emit = defineEmits<{ const emit = defineEmits<{
signedIn: [authResult: any] signedIn: [authResult: any]
}>() }>()
const ui: any = inject('firebaseAuthUI') // const ui: any = inject('firebaseAuthUI')
const auth = getAuth()
const firebaseAuthUI = firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(auth)
const uiConfig = { const uiConfig = {
signInOptions: [ signInOptions: [
@@ -48,50 +42,93 @@ const uiConfig = {
} }
// onMounted(() => ui.start('#auth', uiConfig)) // onMounted(() => ui.start('#auth', uiConfig))
type AuthProvider = 'google' | 'github' | 'microsoft' interface Provider {
const providers: { [key in AuthProvider]: any | (() => any) } = { name: 'google' | 'microsoft' | 'github'
google: new GoogleAuthProvider(), icon: string
github: () => { signin: () => Promise<void>
const provider = new GithubAuthProvider() // (options?: SignInOptions) => Promise<SignInResult>
provider.addScope('user:email')
return provider
},
microsoft: new OAuthProvider('microsoft.com')
} }
const signIn = (providerName: AuthProvider) => { const providers: Provider[] = [
const auth = getAuth() {
const provider = name: 'google',
typeof providers[providerName] === 'function' icon: 'fa-brands fa-google',
? providers[providerName]() signin: async () => {
: providers[providerName] const result = await FirebaseAuthentication.signInWithGoogle({
signInWithRedirect(auth, provider) mode: 'redirect'
})
const credential = GoogleAuthProvider.credential(result.credential?.idToken)
await signInWithCredential(auth, credential)
}
}
// {
// name: 'microsoft',
// icon: 'fa-brands fa-microsoft',
// signin: async () => {
// const result = await FirebaseAuthentication.signInWithMicrosoft({
// mode: 'redirect'
// })
// const provider = new OAuthProvider('microsoft.com')
// const credential = provider.credential({
// idToken: result.credential?.idToken,
// rawNonce: result.credential?.nonce
// })
// await signInWithCredential(auth, credential)
// }
// },
// {
// name: 'github',
// icon: 'fa-brands fa-github',
// signin: async () => {
// const result = await FirebaseAuthentication.signInWithGithub({
// mode: 'redirect'
// })
// const provider = new OAuthProvider('github.com')
// const credential = provider.credential({
// idToken: result.credential?.idToken,
// rawNonce: result.credential?.nonce
// })
// await signInWithCredential(auth, credential)
// }
// }
]
// type Provider = (typeof providers)[number]
const signInWithProvider = async (provider: Provider) => {
provider.signin()
} }
const authenticatingWithEmail = ref(false) const signingInWithEmail = ref(false)
const signInWithEmail = () => { const signInWithEmail = () => {
authenticatingWithEmail.value = true firebaseAuthUI.start('#auth', uiConfig)
ui.start('#auth', uiConfig) signingInWithEmail.value = true
} }
</script> </script>
<template> <template>
<div class="flex flex-col items-center" v-if="!authenticatingWithEmail"> <div class="space-y-2">
<UIButton class="mx-auto w-[200px] bg-red-500 text-center" @click="signIn('google')"> <template v-if="!signingInWithEmail">
Sign in with Google <UIButton
class="mx-auto !block w-[225px] max-sm:w-full"
size="sm"
@click="signInWithProvider(provider)"
v-for="provider in providers"
:key="provider.name"
>
<i class="fa-fw mr-2" :class="provider.icon"></i>
Sign in with {{ provider.name }}
</UIButton> </UIButton>
<UIButton class="mx-auto w-[200px] bg-blue-300 text-center" @click="signIn('github')"> <UIButton
Sign in with Github class="mx-auto !block w-[225px] max-sm:w-full"
size="sm"
@click="signInWithEmail"
>
<i class="fa-fw fa-regular fa-envelope mr-2"></i>
Sign in with email
</UIButton> </UIButton>
<UIButton class="mx-auto w-[200px] bg-blue-300 text-center" @click="signIn('microsoft')"> </template>
Sign in with Microsoft
</UIButton>
<UIButton class="mx-auto w-[200px] bg-blue-300 text-center" @click="signInWithEmail">
Sign in with e-mail
</UIButton>
</div>
<div id="auth"></div> <div id="auth"></div>
<!-- <progress <!-- <progress
v-show="props.authenticating" v-show="props.authenticating"
class="dui-progress dui-progress-primary w-full" class="dui-progress dui-progress-primary w-full"
></progress> --> ></progress> -->
</div>
</template> </template>

View File

@@ -45,7 +45,7 @@ const handleKeypress = (event: { [key: string]: number }) => {
defineExpose({ handleKeypress }) defineExpose({ handleKeypress })
</script> </script>
<template> <template>
<UIMenu class="border-[1px] p-2 text-[0.875rem] text-black shadow-md" :compact="true"> <UIMenu class="border-[1px] p-2 text-[0.875rem] text-black shadow-md" compact>
<UIMenuItem :active="!activeResult" @click="emit('createLink', props.autocompleteText)"> <UIMenuItem :active="!activeResult" @click="emit('createLink', props.autocompleteText)">
<span class="flex-grow">{{ props.autocompleteText }}</span> <span class="flex-grow">{{ props.autocompleteText }}</span>
<i class="fas fa-plus-circle ml-auto text-white" /> <i class="fas fa-plus-circle ml-auto text-white" />

View File

@@ -15,8 +15,7 @@ import ContextedPlugin from '@/ckeditor/ContextedPlugin'
import { mdToHtml, htmlToMd } from '@/utils/markdown' import { mdToHtml, htmlToMd } from '@/utils/markdown'
import { getNoteByTitle, setActiveNote, addNote } from '@/composables/useNotes' import { getNoteByTitle, setActiveNote, addNote } from '@/composables/useNotes'
import Autocomplete from '@/components/Note/Autocomplete.vue' import Autocomplete from '@/components/Note/Autocomplete.vue'
import { Haptics, ImpactStyle } from '@capacitor/haptics' import { vibrate } from '@/composables/useHaptics'
const props = defineProps<{ note: Note }>() const props = defineProps<{ note: Note }>()
const emit = defineEmits<{ const emit = defineEmits<{
@@ -68,7 +67,7 @@ const handleClick = async ({ data }: { data: any }) => {
let note: BaseNote | Note | undefined = getNoteByTitle(noteTitle) let note: BaseNote | Note | undefined = getNoteByTitle(noteTitle)
if (!note) note = addNote(noteTitle, '') if (!note) note = addNote(noteTitle, '')
setActiveNote(note.id) setActiveNote(note.id)
await Haptics.impact({ style: ImpactStyle.Light }) await vibrate()
} }
const autocompleteRef = ref<InstanceType<typeof Autocomplete> | null>(null) const autocompleteRef = ref<InstanceType<typeof Autocomplete> | null>(null)

View File

@@ -6,7 +6,7 @@ const props = defineProps<{
</script> </script>
<template> <template>
<UIMenu class="mt-3 rounded-xl border-[1px] px-3 py-3" v-if="props.references.length > 0"> <UIMenu class="mt-3 rounded-xl border-[1px] px-3 py-3" v-if="props.references.length > 0">
<UIMenuItem :title="true"> <UIMenuItem title>
<span>References</span> <span>References</span>
<UIBadge variant="outline" class="ml-2">{{ props.references.length }}</UIBadge> <UIBadge variant="outline" class="ml-2">{{ props.references.length }}</UIBadge>
</UIMenuItem> </UIMenuItem>

View File

@@ -2,6 +2,7 @@
import { Capacitor } from '@capacitor/core' import { Capacitor } from '@capacitor/core'
import { Dialog } from '@capacitor/dialog' import { Dialog } from '@capacitor/dialog'
import type { ConfirmOptions } from '@capacitor/dialog' import type { ConfirmOptions } from '@capacitor/dialog'
import { vibrate } from '@/composables/useHaptics'
const props = defineProps<{ const props = defineProps<{
note: Note note: Note
@@ -37,7 +38,7 @@ const confirmModals: ModalOptions[] = [
] ]
const emit = defineEmits<{ const emit = defineEmits<{
execute: [actionType: ActionKey, close?: () => void] execute: [actionType: ActionKey, close?: () => Promise<void>]
}>() }>()
const openModal = async (open: () => void, modal: ModalOptions) => { const openModal = async (open: () => void, modal: ModalOptions) => {
@@ -57,7 +58,12 @@ const openModal = async (open: () => void, modal: ModalOptions) => {
<UIButtonGroup class="flex items-center" v-if="!props.note.isRoot"> <UIButtonGroup class="flex items-center" v-if="!props.note.isRoot">
<UIModal v-for="confirmModal in confirmModals" :key="confirmModal.key"> <UIModal v-for="confirmModal in confirmModals" :key="confirmModal.key">
<template #activator="{ open }"> <template #activator="{ open }">
<UIButton size="sm" @click="openModal(open, confirmModal)" :join="true"> <UIButton
size="sm"
@click="openModal(open, confirmModal)"
@mousedown="vibrate"
join
>
<i :class="confirmModal.icon" /> <i :class="confirmModal.icon" />
</UIButton> </UIButton>
</template> </template>
@@ -68,7 +74,11 @@ const openModal = async (open: () => void, modal: ModalOptions) => {
<template #default>{{ confirmModal.confirmOptions.message }}</template> <template #default>{{ confirmModal.confirmOptions.message }}</template>
<template #actions="{ close }"> <template #actions="{ close }">
<UIButton size="sm" @click="close">Cancel</UIButton> <UIButton size="sm" @click="close">Cancel</UIButton>
<UIButton size="sm" color="primary" @click="emit('execute', confirmModal.key, close)"> <UIButton
size="sm"
color="primary"
@click="emit('execute', confirmModal.key, close)"
>
{{ confirmModal.confirmOptions.okButtonTitle }} {{ confirmModal.confirmOptions.okButtonTitle }}
</UIButton> </UIButton>
</template> </template>

View File

@@ -42,7 +42,8 @@ const handleKeydown = (event: KeyboardEvent) => {
if (index < 0) index = results.value.length - 1 if (index < 0) index = results.value.length - 1
activeResult.value = results.value[index] activeResult.value = results.value[index]
const element = resultsRefs.value[index].$el const element = resultsRefs.value[index].$el
if (['ArrowUp', 'ArrowDown', 'Tab'].includes(code)) element.scrollIntoView({ block: 'nearest' }) if (['ArrowUp', 'ArrowDown', 'Tab'].includes(code))
element.scrollIntoView({ block: 'nearest' })
} else if (code === 'Enter' && activeResult.value) { } else if (code === 'Enter' && activeResult.value) {
goToNote(activeResult.value) goToNote(activeResult.value)
} else if (code === 'Escape' && queryElem.value) { } else if (code === 'Escape' && queryElem.value) {
@@ -66,7 +67,7 @@ const resultsRefs = ref<InstanceType<typeof SearchResult>[]>([])
@keydown="handleKeydown" @keydown="handleKeydown"
/> />
<div class="z-1000 absolute left-0 right-0 top-[100%]" v-if="active"> <div class="z-1000 absolute left-0 right-0 top-[100%]" v-if="active">
<UIMenu :compact="true" class="mt-1 w-full rounded-md bg-base-100 p-2 text-black shadow"> <UIMenu compact class="mt-1 w-full rounded-md bg-base-100 p-2 text-black shadow">
<div class="max-h-[320px] w-full overflow-y-auto"> <div class="max-h-[320px] w-full overflow-y-auto">
<template v-if="results.length > 0"> <template v-if="results.length > 0">
<SearchResult <SearchResult

View File

@@ -8,7 +8,11 @@ const props = withDefaults(
</script> </script>
<template> <template>
<div class="flex w-full animate-pulse flex-col"> <div class="flex w-full animate-pulse flex-col">
<div class="mt-1 h-[1.35rem] w-full rounded bg-secondary" v-for="i in props.n" :key="i"></div> <div
class="mt-1 h-[1.35rem] w-full rounded bg-secondary"
v-for="i in props.n"
:key="i"
></div>
</div> </div>
</template> </template>
<style scoped> <style scoped>

View File

@@ -23,9 +23,8 @@ const searchActive = ref<boolean>(false)
// const authUI: any = inject('firebaseAuthUI') // const authUI: any = inject('firebaseAuthUI')
// const authPending = ref<boolean>(authUI.isPendingRedirect()) // const authPending = ref<boolean>(authUI.isPendingRedirect())
// const authPending = ref(false)
const handleSignIn = async (close: () => Promise<boolean>) => { const handleSignIn = async (close: () => Promise<void>) => {
await close() await close()
// authPending.value = false // authPending.value = false
} }
@@ -61,7 +60,7 @@ const handleSignIn = async (close: () => Promise<boolean>) => {
> >
<i class="fa-fw fa-solid fa-plus-circle scale-[115%]" /> <i class="fa-fw fa-solid fa-plus-circle scale-[115%]" />
</UIButton> </UIButton>
<UIModal v-if="(initialized && !user)"> <UIModal v-if="initialized && !user">
<template #activator="{ open }"> <template #activator="{ open }">
<UIButton <UIButton
size="sm" size="sm"
@@ -72,7 +71,7 @@ const handleSignIn = async (close: () => Promise<boolean>) => {
Sign in Sign in
</UIButton> </UIButton>
</template> </template>
<template #title>{{ 'Sign in' }}</template> <template #title>Sign in</template>
<template #default="{ close }"> <template #default="{ close }">
<Auth @signedIn="handleSignIn(close)" /> <Auth @signedIn="handleSignIn(close)" />
</template> </template>
@@ -87,19 +86,23 @@ const handleSignIn = async (close: () => Promise<boolean>) => {
</div> </div>
</div> </div>
</template> </template>
<style lang="scss"> <style lang="scss" scoped>
#logo { #logo {
@apply cursor-pointer transition-all duration-200 hover:text-primary; @apply cursor-pointer transition-all duration-200 active:text-primary;
} }
@media (hover: hover) and (pointer: fine) { @media (hover: hover) and (pointer: fine) {
#logo:hover { #logo:hover {
text-shadow: 0 0 5px white, 0 0 10px white, 0 0 15px white; text-shadow: 0 0 5px white, 0 0 10px white, 0 0 15px white;
@apply text-primary;
} }
} }
#logo:active { #logo:active {
text-shadow: 0 0 5px white, 0 0 10px white, 0 0 15px white; text-shadow: 0 0 5px white, 0 0 10px white, 0 0 15px white;
} }
.topbar-button { .topbar-button {
&:active {
@apply border-white bg-white text-primary;
}
@apply hover:border-white hover:bg-white hover:text-primary focus-visible:outline-white; @apply hover:border-white hover:bg-white hover:text-primary focus-visible:outline-white;
} }

View File

@@ -9,7 +9,7 @@ const emit = defineEmits<{
</script> </script>
<template> <template>
<label <label
class="dui-btn-ghost dui-btn-sm dui-btn-circle dui-btn relative inline-grid cursor-pointer select-none place-content-center" class="dui-btn-ghost dui-btn dui-btn-sm dui-btn-circle relative inline-grid cursor-pointer select-none place-content-center"
> >
<input type="checkbox" @click="emit('toggleSideBar')" :checked="!props.sideBarCollapsed" /> <input type="checkbox" @click="emit('toggleSideBar')" :checked="!props.sideBarCollapsed" />
<svg <svg

View File

@@ -1,11 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import { OnClickOutside } from '@vueuse/components' import { OnClickOutside } from '@vueuse/components'
import { vibrate } from '@/composables/useHaptics'
</script> </script>
<template> <template>
<OnClickOutside> <OnClickOutside>
<UIDropdown class="search-active-hide"> <UIDropdown class="search-active-hide">
<template #activator> <template #activator>
<UIButton :dropdown="true" size="sm" variant="outline" class="topbar-button text-white"> <UIButton
dropdown
size="sm"
variant="outline"
class="topbar-button text-white"
@mousedown="vibrate"
>
<i class="fa-fw fa-solid fa-user-gear" /> <i class="fa-fw fa-solid fa-user-gear" />
</UIButton> </UIButton>
</template> </template>
@@ -17,3 +24,11 @@ import { OnClickOutside } from '@vueuse/components'
</UIDropdown> </UIDropdown>
</OnClickOutside> </OnClickOutside>
</template> </template>
<style scoped lang="scss">
.topbar-button {
&:focus-within {
@apply border-white bg-white text-primary;
}
@apply hover:border-white hover:bg-white hover:text-primary focus-visible:outline-white;
}
</style>

View File

@@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { sendEmailVerification } from 'firebase/auth'
import { user } from '@/composables/useFirebase' import { user } from '@/composables/useFirebase'
import { encryptionKey, enableEncryption, disableEncryption } from '@/composables/useEncryption' import { encryptionKey, enableEncryption, disableEncryption } from '@/composables/useEncryption'
import { notes } from '@/composables/useNotes' import { notes } from '@/composables/useNotes'
@@ -9,12 +10,10 @@ import FileSaver from 'file-saver'
const verificationEmailSent = ref(false) const verificationEmailSent = ref(false)
const sendVerificationMail = () => { const sendVerificationMail = () => {
if (!user.value) throw Error("User doesn't exist, can't send verification email") if (!user.value) throw Error("User doesn't exist, can't send verification email")
user.value.sendEmailVerification() sendEmailVerification(user.value)
verificationEmailSent.value = true verificationEmailSent.value = true
} }
console.log(user.value)
const exportNotes = async () => { const exportNotes = async () => {
const zip = new JSZip() const zip = new JSZip()
notes.value.forEach((note) => { notes.value.forEach((note) => {
@@ -68,21 +67,22 @@ const toggleEncryption = async () => {
<UICard> <UICard>
<template #title>Account</template> <template #title>Account</template>
<template #default> <template #default>
<div class="w-full flex-row sm:flex" v-if="user?.email"> <template v-if="user?.email">
<div class="w-full flex-row sm:flex">
<div class="font-bold sm:w-4/12">E-mail address</div> <div class="font-bold sm:w-4/12">E-mail address</div>
<div>{{ user?.email }}</div> <div>{{ user.email }}</div>
</div> </div>
<div class="w-full flex-row sm:flex" v-if="user?.email"> <div class="w-full flex-row sm:flex">
<div class="font-bold sm:w-4/12">Account status</div> <div class="font-bold sm:w-4/12">Account status</div>
<div class="col-auto"> <div class="col-auto">
<UIBadge :color="user?.emailVerified ? 'success' : 'warning'"> <UIBadge :color="user.emailVerified ? 'success' : 'warning'">
{{ user?.emailVerified ? 'Verified' : 'Not yet verified' }} {{ user.emailVerified ? 'Verified' : 'Not yet verified' }}
</UIBadge> </UIBadge>
<UIButton <UIButton
size="sm" size="sm"
class="ml-2" class="ml-2"
@click="sendVerificationMail" @click="sendVerificationMail"
v-if="!user?.emailVerified" v-if="!user.emailVerified"
:disabled="verificationEmailSent" :disabled="verificationEmailSent"
> >
{{ {{
@@ -93,10 +93,16 @@ const toggleEncryption = async () => {
</UIButton> </UIButton>
</div> </div>
</div> </div>
</template>
<div class="w-full flex-row sm:flex"> <div class="w-full flex-row sm:flex">
<div class="font-bold sm:w-4/12">Account creation date</div> <div class="font-bold sm:w-4/12">Account creation date</div>
<div> <div>
{{ format(Date.parse(user?.metadata?.creationTime || ''), 'dd/MM/yyyy') }} {{
format(
Date.parse(user?.metadata?.creationTime || ''),
'dd/MM/yyyy'
)
}}
</div> </div>
</div> </div>
</template> </template>
@@ -107,15 +113,19 @@ const toggleEncryption = async () => {
<div class="items-top w-full flex-row sm:flex"> <div class="items-top w-full flex-row sm:flex">
<div class="font-bold sm:w-4/12">Export notes</div> <div class="font-bold sm:w-4/12">Export notes</div>
<UIButton size="sm" @click="exportNotes"> <UIButton size="sm" @click="exportNotes">
<i class="fa-fw fa-solid fa-file-export"></i> <i class="fa-fw fa-solid fa-file-export mr-2"></i>
Export notes Export notes
</UIButton> </UIButton>
</div> </div>
<div class="items-top w-full flex-row sm:flex sm:flex-grow"> <div class="items-top w-full flex-row sm:flex sm:flex-grow">
<div class="flex-shrink-0 font-bold sm:w-4/12">Delete account</div> <div class="flex-shrink-0 font-bold sm:w-4/12">Delete account</div>
<div> <div>
<UIButton size="sm" color="error" @click="showDeleteAccountDialog = true"> <UIButton
<i class="fa-fw fa-solid fa-trash"></i> size="sm"
color="error"
@click="showDeleteAccountDialog = true"
>
<i class="fa-fw fa-solid fa-trash mr-2"></i>
Delete account Delete account
</UIButton> </UIButton>
<UIAlert <UIAlert
@@ -125,14 +135,23 @@ const toggleEncryption = async () => {
v-if="showDeleteAccountDialog" v-if="showDeleteAccountDialog"
> >
<div> <div>
Are you sure you want to delete your Contexted account? This action cannot be Are you sure you want to delete your Contexted account? This
undone! action cannot be undone!
</div> </div>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<UIButton size="sm" variant="outline" color="primary" @click="deleteAccount"> <UIButton
size="sm"
variant="outline"
color="primary"
@click="deleteAccount"
>
Delete account Delete account
</UIButton> </UIButton>
<UIButton size="sm" variant="outline" @click="showDeleteAccountDialog = false"> <UIButton
size="sm"
variant="outline"
@click="showDeleteAccountDialog = false"
>
Cancel Cancel
</UIButton> </UIButton>
</div> </div>
@@ -140,7 +159,9 @@ const toggleEncryption = async () => {
</div> </div>
</div> </div>
<div class="items-top w-full flex-row sm:flex"> <div class="items-top w-full flex-row sm:flex">
<div class="flex-shrink-0 font-bold sm:w-4/12">End-to-end encryption</div> <div class="flex-shrink-0 font-bold sm:w-4/12">
End-to-end encryption
</div>
<div class="w-full"> <div class="w-full">
<template v-if="!encryptionEnabled"> <template v-if="!encryptionEnabled">
<UIButton <UIButton
@@ -158,11 +179,16 @@ const toggleEncryption = async () => {
@click="showEncryptionDialog = true" @click="showEncryptionDialog = true"
v-if="showEncryptionDialog === false" v-if="showEncryptionDialog === false"
> >
<i class="fa-fw fa-solid fa-key"></i> <i class="fa-fw fa-solid fa-key mr-2"></i>
Disable end-to-end encryption Disable end-to-end encryption
</UIButton> </UIButton>
</template> </template>
<UIAlert color="info" density="compact" class="text-sm" v-if="showEncryptionDialog"> <UIAlert
color="info"
density="compact"
class="text-sm"
v-if="showEncryptionDialog"
>
<div class="w-full space-y-2"> <div class="w-full space-y-2">
<div> <div>
Enter your passphrase to Enter your passphrase to
@@ -176,7 +202,11 @@ const toggleEncryption = async () => {
v-model="passphrase" v-model="passphrase"
class="w-full !max-w-full" class="w-full !max-w-full"
/> />
<UIAlert density="compact" color="error" v-if="toggleEncryptionError"> <UIAlert
density="compact"
color="error"
v-if="toggleEncryptionError"
>
<i class="fa-solid fa-triangle-exclamation"></i> <i class="fa-solid fa-triangle-exclamation"></i>
{{ toggleEncryptionError }} {{ toggleEncryptionError }}
</UIAlert> </UIAlert>
@@ -188,9 +218,16 @@ const toggleEncryption = async () => {
color="primary" color="primary"
@click="toggleEncryption" @click="toggleEncryption"
> >
{{ encryptionEnabled ? 'Disable' : 'Enable' }} encryption {{
encryptionEnabled ? 'Disable' : 'Enable'
}}
encryption
</UIButton> </UIButton>
<UIButton size="sm" variant="outline" @click="showEncryptionDialog = false"> <UIButton
size="sm"
variant="outline"
@click="showEncryptionDialog = false"
>
Cancel Cancel
</UIButton> </UIButton>
</div> </div>

View File

@@ -3,7 +3,7 @@ import { preferredNotesSource } from '@/composables/useSettings'
import { signOut as firebaseSignOut } from '@/composables/useFirebase' import { signOut as firebaseSignOut } from '@/composables/useFirebase'
import { clearEncryptionKeys } from '@/composables/useEncryption' import { clearEncryptionKeys } from '@/composables/useEncryption'
const signOut = async (close: () => Promise<boolean>) => { const signOut = async (close: () => Promise<void>) => {
await close() await close()
await firebaseSignOut() await firebaseSignOut()
preferredNotesSource.value = null preferredNotesSource.value = null

View File

@@ -34,11 +34,14 @@ const deleteSelectedNotes = (closeModal: () => void) => {
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div class="flex items-center"> <div class="flex items-center">
<span class="whitespace-nowrap"> <span class="whitespace-nowrap">
{{ notesWithReferences.length }} {{ notesWithReferences.length === 1 ? 'note' : 'notes' }} {{ notesWithReferences.length }}
{{ notesWithReferences.length === 1 ? 'note' : 'notes' }}
</span> </span>
<template v-if="countSelectedNotes > 0"> <template v-if="countSelectedNotes > 0">
<span class="mx-1">|</span> <span class="mx-1">|</span>
<div class="whitespace-nowrap font-semibold">{{ countSelectedNotes }} selected</div> <div class="whitespace-nowrap font-semibold">
{{ countSelectedNotes }} selected
</div>
</template> </template>
</div> </div>
<UIModal v-if="countSelectedNotes > 0"> <UIModal v-if="countSelectedNotes > 0">

View File

@@ -27,7 +27,9 @@ const renderMindmap = () => {
const elements = { const elements = {
nodes: nodes.value, nodes: nodes.value,
edges: [ edges: [
...links.value.map((link) => ({ data: { id: `${link.source}-${link.target}`, ...link } })) ...links.value.map((link) => ({
data: { id: `${link.source}-${link.target}`, ...link }
}))
] ]
} }
const cy = cytoscape({ const cy = cytoscape({
@@ -195,11 +197,13 @@ interface Mindmap {
} }
const selectedMindmap = ref<Mindmap>() const selectedMindmap = ref<Mindmap>()
const mindmaps = computed<Mindmap[]>(() => { const mindmaps = computed<Mindmap[]>(() => {
const mindmaps = Object.entries(notesRelations.value).reduce((mindmaps, [noteId, relations]) => { const mindmaps = Object.entries(notesRelations.value).reduce(
(mindmaps, [noteId, relations]) => {
const atomicMindmap = [noteId, ...relations.to, ...relations.from] const atomicMindmap = [noteId, ...relations.to, ...relations.from]
const indices = mindmaps const indices = mindmaps
.filter( .filter(
(mindmap) => [...mindmap].filter((noteId) => atomicMindmap.includes(noteId)).length > 0 (mindmap) =>
[...mindmap].filter((noteId) => atomicMindmap.includes(noteId)).length > 0
) )
.map((mindmap) => mindmaps.indexOf(mindmap)) .map((mindmap) => mindmaps.indexOf(mindmap))
if (indices.length > 0) { if (indices.length > 0) {
@@ -218,13 +222,16 @@ const mindmaps = computed<Mindmap[]>(() => {
mindmaps.push(atomicMindmap) mindmaps.push(atomicMindmap)
} }
return mindmaps return mindmaps
}, [] as string[][]) },
[] as string[][]
)
return mindmaps return mindmaps
.filter((mindmap) => mindmap.length > 1) .filter((mindmap) => mindmap.length > 1)
.sort((a, b) => b.length - a.length) .sort((a, b) => b.length - a.length)
.sort((a, b) => { .sort((a, b) => {
return ( return (
Number(b.includes(rootNote.value?.id || '')) - Number(a.includes(rootNote.value?.id || '')) Number(b.includes(rootNote.value?.id || '')) -
Number(a.includes(rootNote.value?.id || ''))
) )
}) })
.slice(0, 5) .slice(0, 5)
@@ -273,6 +280,7 @@ const links = computed(() => {
</script> </script>
<template> <template>
<div class="flex h-full flex-grow flex-col"> <div class="flex h-full flex-grow flex-col">
<div class="flex">
<UITabs> <UITabs>
<UITab <UITab
v-for="mindmap in mindmaps" v-for="mindmap in mindmaps"
@@ -284,6 +292,7 @@ const links = computed(() => {
{{ mindmap.notes.length }} notes {{ mindmap.notes.length }} notes
</UITab> </UITab>
</UITabs> </UITabs>
</div>
<div id="mindmap" ref="mindmapElement" class="h-full"></div> <div id="mindmap" ref="mindmapElement" class="h-full"></div>
</div> </div>
</template> </template>

View File

@@ -30,13 +30,14 @@ const updateNoteContent = (content: string) => {
const references = computed<Note[]>(() => getNoteReferences(props.note)) const references = computed<Note[]>(() => getNoteReferences(props.note))
const handleAction = async (action: string, closeModal: () => Promise<Boolean>) => { const handleAction = async (action: 'delete' | 'setRoot', closeModal?: () => Promise<void>) => {
if (action === 'delete') { switch (action) {
case 'delete':
if (closeModal) await closeModal() if (closeModal) await closeModal()
setActiveNote(rootNote.value?.id) setActiveNote(rootNote.value?.id)
deleteNote(props.note.id) deleteNote(props.note.id)
} break
if (action === 'setRoot') { case 'setRoot':
setRootNote(props.note.id) setRootNote(props.note.id)
if (closeModal) closeModal() if (closeModal) closeModal()
} }
@@ -50,7 +51,11 @@ const handleAction = async (action: string, closeModal: () => Promise<Boolean>)
class="fas fa-fw fa-home mr-2 text-base text-secondary opacity-40" class="fas fa-fw fa-home mr-2 text-base text-secondary opacity-40"
v-if="props.note.isRoot" v-if="props.note.isRoot"
></i> ></i>
<input type="text" class="w-full bg-transparent pb-1 outline-none" v-model="noteTitle" /> <input
type="text"
class="w-full bg-transparent pb-1 outline-none"
v-model="noteTitle"
/>
</template> </template>
</NoteToolbar> </NoteToolbar>
<NoteEditor <NoteEditor

View File

@@ -51,7 +51,12 @@ const styleClass = computed(() => {
> >
<slot></slot> <slot></slot>
</label> </label>
<button type="button" class="dui-btn h-auto px-3 py-2 duration-0" :class="styleClass" v-else> <button
type="button"
class="dui-btn inline-block h-auto max-w-full truncate px-3 py-2 duration-0"
:class="styleClass"
v-else
>
<slot></slot> <slot></slot>
</button> </button>
</template> </template>

View File

@@ -3,7 +3,7 @@
<slot name="activator" tabindex="0"></slot> <slot name="activator" tabindex="0"></slot>
<ul <ul
tabindex="0" tabindex="0"
class="dui-menu-compact dui-dropdown-content dui-menu rounded-box mt-1 w-52 bg-base-100 p-2 text-base-content shadow" class="dui-menu-compact dui-dropdown-content dui-menu mt-1 w-52 rounded-box bg-base-100 p-2 text-base-content shadow"
> >
<slot name="items"></slot> <slot name="items"></slot>
</ul> </ul>

View File

@@ -28,7 +28,7 @@ const styleClass = computed(() => {
type="checkbox" type="checkbox"
class="dui-checkbox dui-checkbox-sm border-secondary" class="dui-checkbox dui-checkbox-sm border-secondary"
:class="styleClass" :class="styleClass"
:checked="props.modelValue" :checked="props.modelValue || props.checked"
@change="emit('update:modelValue', ($event.target as HTMLInputElement).checked)" @change="emit('update:modelValue', ($event.target as HTMLInputElement).checked)"
:disabled="props.disabled" :disabled="props.disabled"
/> />

View File

@@ -16,7 +16,9 @@ const styleClass = computed(() => {
</script> </script>
<template> <template>
<li :class="styleClass"> <li :class="styleClass">
<span class="flex items-center" v-if="props.title"><slot></slot></span> <span class="flex items-center" v-if="props.title">
<slot></slot>
</span>
<a <a
class="flex w-full rounded-md" class="flex w-full rounded-md"
:class="{ 'dui-disabled': props.disabled, 'dui-active': props.active }" :class="{ 'dui-disabled': props.disabled, 'dui-active': props.active }"
@@ -26,3 +28,8 @@ const styleClass = computed(() => {
</a> </a>
</li> </li>
</template> </template>
<style scoped>
.dui-active {
@apply bg-primary;
}
</style>

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { onClickOutside } from '@vueuse/core' import { onClickOutside } from '@vueuse/core'
import { Haptics, ImpactStyle } from '@capacitor/haptics' import { vibrate } from '@/composables/useHaptics'
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@@ -20,9 +20,8 @@ const show = ref<boolean>(false)
watch( watch(
() => props.open, () => props.open,
() => { () => {
if (show.value) { if (show.value) vibrate()
Haptics.impact({ style: ImpactStyle.Light })
}
show.value = props.open show.value = props.open
}, },
{ immediate: true } { immediate: true }
@@ -32,9 +31,9 @@ const modal = ref<HTMLElement | null>(null)
const modalBox = ref(null) const modalBox = ref(null)
const open = () => (show.value = true) const open = () => (show.value = true)
const close = (): Promise<boolean> => { const close = (): Promise<void> => {
return new Promise((resolve) => { return new Promise((resolve) => {
modal.value?.addEventListener('transitionend', () => resolve(true)) modal.value?.addEventListener('transitionend', () => resolve())
show.value = false show.value = false
}) })
} }

View File

@@ -6,12 +6,12 @@ interface Props {
const props = defineProps<Props>() const props = defineProps<Props>()
const styleClass = computed(() => { const styleClass = computed(() => {
const activeClass = props.active && 'dui-tab-active !border-primary text-primary' const activeClass = props.active && 'dui-tab-active font-bold !border-primary text-primary'
return [activeClass] return [activeClass]
}) })
</script> </script>
<template> <template>
<a class="dui-tab-bordered dui-tab-lifted dui-tab dui-tab-md" :class="styleClass"> <a class="dui-tab-bordered dui-tab hover:font-bold hover:text-primary" :class="styleClass">
<slot></slot> <slot></slot>
</a> </a>
</template> </template>

View File

@@ -1,3 +1,3 @@
<template> <template>
<div class="dui-tabs"><slot></slot></div> <div class="dui-tabs dui-tabs-boxed dui-tabs-md"><slot></slot></div>
</template> </template>

View File

@@ -97,7 +97,10 @@ const decryptNote = (note: BaseNote, key: EncryptionKey) => {
export const decryptNotes = (notes: BaseNotes, encryptionKey: EncryptionKey) => { export const decryptNotes = (notes: BaseNotes, encryptionKey: EncryptionKey) => {
const decryptedNotes = Object.fromEntries( const decryptedNotes = Object.fromEntries(
Object.entries(notes).map(([noteId, note]) => [noteId, { ...decryptNote(note, encryptionKey) }]) Object.entries(notes).map(([noteId, note]) => [
noteId,
{ ...decryptNote(note, encryptionKey) }
])
) )
return decryptedNotes return decryptedNotes
} }
@@ -112,7 +115,10 @@ const encryptNote = (note: BaseNote, key: EncryptionKey) => {
export const encryptNotes = (notes: BaseNotes, encryptionKey: EncryptionKey) => { export const encryptNotes = (notes: BaseNotes, encryptionKey: EncryptionKey) => {
const encryptedNotes = Object.fromEntries( const encryptedNotes = Object.fromEntries(
Object.entries(notes).map(([noteId, note]) => [noteId, { ...encryptNote(note, encryptionKey) }]) Object.entries(notes).map(([noteId, note]) => [
noteId,
{ ...encryptNote(note, encryptionKey) }
])
) )
return encryptedNotes return encryptedNotes
} }

View File

@@ -1,13 +1,24 @@
// import { initializeApp } from 'firebase/app' // import { initializeApp } from 'firebase/app'
import firebase from 'firebase/compat/app' import { Capacitor } from '@capacitor/core'
import type { User } from '@firebase/auth-types' // import firebase from 'firebase/compat/app'
// import type { User } from '@firebase/auth-types'
import { initializeApp } from 'firebase/app'
import { import {
initializeFirestore, initializeFirestore,
persistentLocalCache, persistentLocalCache,
persistentMultipleTabManager persistentMultipleTabManager
} from 'firebase/firestore' } from 'firebase/firestore'
import {
getAuth,
indexedDBLocalPersistence,
initializeAuth,
onAuthStateChanged,
signOut as firebaseSignOut
} from 'firebase/auth'
import { type FirebaseApp } from 'firebase/app'
import { type User } from '@firebase/auth'
import type { Firestore } from 'firebase/firestore' import type { Firestore } from 'firebase/firestore'
import { getAuth, getRedirectResult } from 'firebase/auth'
// import { getAnalytics } from "firebase/analytics"; // import { getAnalytics } from "firebase/analytics";
// TODO: Add SDKs for Firebase products that you want to use // TODO: Add SDKs for Firebase products that you want to use
@@ -30,43 +41,26 @@ export const user = ref<User | null>()
export const initialized = computed<boolean>(() => user.value !== undefined) export const initialized = computed<boolean>(() => user.value !== undefined)
export const signOut = () => firebase.auth().signOut() export const signOut = async () => firebaseSignOut(getAuth())
export const db = ref<Firestore>() export const db = ref<Firestore>()
// Initialize Firebase const getFirebaseAuth = async (app: FirebaseApp) => {
export const initializeFirebase = async () => { if (Capacitor.isNativePlatform()) {
const app = firebase.initializeApp(firebaseConfig) return initializeAuth(app, {
const auth = getAuth() persistence: indexedDBLocalPersistence
try { })
const authRedirectResult = await getRedirectResult(auth) } else {
console.log(authRedirectResult) return getAuth()
// user.value = result }
// This gives you a Google Access Token. You can use it to access Google APIs.
// const credential = GoogleAuthProvider.credentialFromResult(result)
// const token = credential.accessToken
// The signed-in user info.
// const user = result.user
// IdP data available using getAdditionalUserInfo(result)
// ...
} catch (error: any) {
console.error(error)
// const errorCode = error.code
// // const errorMessage = error.message
// const email = error.customData.email
// Handle Errors here.
// const errorCode = error.code
// const errorMessage = error.message
// The email of the user's account used.
// const email = error.customData.email
// The AuthCredential type that was used.
// const credential = GoogleAuthProvider.credentialFromError(error)
// ...
} }
firebase.auth().onAuthStateChanged((firebaseUser) => { // Initialize Firebase
export const initializeFirebase = async () => {
const app = initializeApp(firebaseConfig)
const auth = await getFirebaseAuth(app)
onAuthStateChanged(auth, (firebaseUser) => {
console.log('auth state changed', firebaseUser)
user.value = firebaseUser user.value = firebaseUser
}) })
db.value = markRaw( db.value = markRaw(

View File

@@ -0,0 +1,3 @@
import { Haptics, ImpactStyle } from '@capacitor/haptics'
export const vibrate = () => Haptics.impact({ style: ImpactStyle.Light })

View File

@@ -8,7 +8,7 @@ import { defaultNotes } from '@/utils/defaultNotes'
import { mdToHtml } from '@/utils/markdown' import { mdToHtml } from '@/utils/markdown'
import { getAllMatches } from '@/utils/helpers' import { getAllMatches } from '@/utils/helpers'
import { preferredNotesSource } from '@/composables/useSettings' import { preferredNotesSource } from '@/composables/useSettings'
import { Haptics, ImpactStyle } from '@capacitor/haptics' import { vibrate } from '@/composables/useHaptics'
export const notesSources = computed(() => ({ export const notesSources = computed(() => ({
local: true, local: true,
@@ -104,7 +104,7 @@ export const setActiveNote = (noteId: string | undefined, haptic: boolean = true
if (noteId) { if (noteId) {
activeNoteId.value = noteId activeNoteId.value = noteId
activeViewMode.value = viewModes.find((mode) => mode.name === 'Note') || viewModes[0] activeViewMode.value = viewModes.find((mode) => mode.name === 'Note') || viewModes[0]
if (haptic) Haptics.impact({ style: ImpactStyle.Light }) if (haptic) vibrate()
} }
} }
@@ -151,7 +151,9 @@ export const findNotes = (query: string): Note[] => {
} }
return notes.value.filter((note) => { return notes.value.filter((note) => {
const matchTitle = note.title.toLowerCase().includes(query.toLowerCase()) const matchTitle = note.title.toLowerCase().includes(query.toLowerCase())
const matchContent = removeMdFromText(note.content).toLowerCase().includes(query.toLowerCase()) const matchContent = removeMdFromText(note.content)
.toLowerCase()
.includes(query.toLowerCase())
return matchTitle || matchContent return matchTitle || matchContent
}) })
} }
@@ -205,7 +207,9 @@ export const notesRelations = computed(() => {
.map((noteRelations, _, notesRelations): NoteRelations => { .map((noteRelations, _, notesRelations): NoteRelations => {
const from = [...notesRelations] const from = [...notesRelations]
.map((noteRelation) => .map((noteRelation) =>
noteRelation.to.filter((toId) => toId === noteRelations.id).map(() => noteRelation.id) noteRelation.to
.filter((toId) => toId === noteRelations.id)
.map(() => noteRelation.id)
) )
.reduce((arr, elem) => arr.concat(elem), []) .reduce((arr, elem) => arr.concat(elem), [])
.filter((value, index, self) => self.indexOf(value) === index) .filter((value, index, self) => self.indexOf(value) === index)
@@ -262,7 +266,9 @@ export const getNotes = async () => {
} }
} else if (activeNotesSource.value === 'firebase') { } else if (activeNotesSource.value === 'firebase') {
if (encryptionKey.value === undefined || !user.value || !db.value) return if (encryptionKey.value === undefined || !user.value || !db.value) return
const firebaseNotes = (await getDoc(doc(db.value, 'pages', user.value.uid))).data() as BaseNotes const firebaseNotes = (
await getDoc(doc(db.value, 'pages', user.value.uid))
).data() as BaseNotes
notes = encryptionKey.value notes = encryptionKey.value
? decryptNotes(firebaseNotes, encryptionKey.value) ? decryptNotes(firebaseNotes, encryptionKey.value)
: firebaseNotes || {} : firebaseNotes || {}

View File

@@ -1,3 +1,4 @@
import { Capacitor } from '@capacitor/core'
import { createApp } from 'vue' import { createApp } from 'vue'
import '@/style.scss' import '@/style.scss'
import '@fortawesome/fontawesome-free/css/all.min.css' import '@fortawesome/fontawesome-free/css/all.min.css'
@@ -7,9 +8,10 @@ import { initializeFirebase } from '@/composables/useFirebase'
import { StatusBar, Style } from '@capacitor/status-bar' import { StatusBar, Style } from '@capacitor/status-bar'
import { Keyboard } from '@capacitor/keyboard' import { Keyboard } from '@capacitor/keyboard'
if (Capacitor.isNativePlatform()) {
StatusBar.setStyle({ style: Style.Dark }) StatusBar.setStyle({ style: Style.Dark })
Keyboard.setAccessoryBarVisible({ isVisible: true }) Keyboard.setAccessoryBarVisible({ isVisible: true })
}
initializeFirebase() initializeFirebase()
const isDark = usePreferredDark() const isDark = usePreferredDark()

View File

@@ -38,12 +38,12 @@ export default {
themes: [ themes: [
{ {
contexted: { contexted: {
...require('daisyui/src/theming/themes')['[data-theme=light]'], ...require('daisyui/src/theming/themes')['light'],
primary, primary,
secondary, secondary,
'--btn-text-case': 'uppercase' // set default text transform for buttons '--btn-text-case': 'uppercase', // set default text transform for buttons
// accent: '#37CDBE', // accent: '#37CDBE',
// neutral: '#F7F7F7', neutral: primary,
// 'base-100': '#FFFFFF', // 'base-100': '#FFFFFF',
// info: '#3ABFF8', // info: '#3ABFF8',
// success: '#36D399', // success: '#36D399',