Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
B
Better you
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
2022-301
Better you
Commits
19284a8a
Commit
19284a8a
authored
Sep 22, 2022
by
Malith Anjana
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
voice conversation buld
parent
47173fbd
Changes
13
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
378 additions
and
22 deletions
+378
-22
App.js
App.js
+8
-0
android/app/build.gradle
android/app/build.gradle
+8
-0
android/app/src/main/java/com/better_you/MainApplication.java
...oid/app/src/main/java/com/better_you/MainApplication.java
+2
-1
android/settings.gradle
android/settings.gradle
+11
-0
ios/Podfile
ios/Podfile
+2
-0
package.json
package.json
+4
-1
src/api.js
src/api.js
+10
-2
src/assets/utils/UnixConverter.js
src/assets/utils/UnixConverter.js
+6
-0
src/components/chatbot/ChatHeader.js
src/components/chatbot/ChatHeader.js
+2
-2
src/screens/Chatbot.js
src/screens/Chatbot.js
+55
-12
src/screens/Home.js
src/screens/Home.js
+2
-1
src/screens/VoiceConversation.js
src/screens/VoiceConversation.js
+69
-0
yarn.lock
yarn.lock
+199
-3
No files found.
App.js
View file @
19284a8a
...
@@ -7,8 +7,11 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
...
@@ -7,8 +7,11 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
import
{
Home
}
from
'
./src/screens/Home
'
;
import
{
Home
}
from
'
./src/screens/Home
'
;
import
AsyncStorage
from
'
@react-native-async-storage/async-storage
'
;
import
AsyncStorage
from
'
@react-native-async-storage/async-storage
'
;
import
{
Chatbot
}
from
'
./src/screens/Chatbot
'
;
import
{
Chatbot
}
from
'
./src/screens/Chatbot
'
;
import
{
VoiceConversation
}
from
'
./src/screens/VoiceConversation
'
;
const
Stack
=
createNativeStackNavigator
();
const
Stack
=
createNativeStackNavigator
();
// create a component
// create a component
const
App
=
()
=>
{
const
App
=
()
=>
{
const
[
onBoardState
,
setonBoardState
]
=
useState
();
const
[
onBoardState
,
setonBoardState
]
=
useState
();
...
@@ -48,6 +51,11 @@ const App = () => {
...
@@ -48,6 +51,11 @@ const App = () => {
component
=
{
Chatbot
}
component
=
{
Chatbot
}
options
=
{{
headerShown
:
false
}}
options
=
{{
headerShown
:
false
}}
/
>
/
>
<
Stack
.
Screen
name
=
"
VoiceConversation
"
component
=
{
VoiceConversation
}
options
=
{{
headerShown
:
false
}}
/
>
<
/Stack.Navigator
>
<
/Stack.Navigator
>
<
/NavigationContainer
>
<
/NavigationContainer
>
...
...
android/app/build.gradle
View file @
19284a8a
...
@@ -135,6 +135,13 @@ android {
...
@@ -135,6 +135,13 @@ android {
compileSdkVersion
rootProject
.
ext
.
compileSdkVersion
compileSdkVersion
rootProject
.
ext
.
compileSdkVersion
packagingOptions
{
pickFirst
'lib/x86/libc++_shared.so'
pickFirst
'lib/x86_64/libc++_shared.so'
pickFirst
'lib/armeabi-v7a/libc++_shared.so'
pickFirst
'lib/arm64-v8a/libc++_shared.so'
}
defaultConfig
{
defaultConfig
{
applicationId
"com.better_you"
applicationId
"com.better_you"
minSdkVersion
rootProject
.
ext
.
minSdkVersion
minSdkVersion
rootProject
.
ext
.
minSdkVersion
...
@@ -266,6 +273,7 @@ dependencies {
...
@@ -266,6 +273,7 @@ dependencies {
implementation
fileTree
(
dir:
"libs"
,
include:
[
"*.jar"
])
implementation
fileTree
(
dir:
"libs"
,
include:
[
"*.jar"
])
implementation
project
(
':react-native-vector-icons'
)
implementation
project
(
':react-native-vector-icons'
)
//noinspection GradleDynamicVersion
//noinspection GradleDynamicVersion
implementation
project
(
':@react-native-voice_voice'
)
implementation
"com.facebook.react:react-native:+"
// From node_modules
implementation
"com.facebook.react:react-native:+"
// From node_modules
implementation
"androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
implementation
"androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
...
...
android/app/src/main/java/com/better_you/MainApplication.java
View file @
19284a8a
...
@@ -4,6 +4,7 @@ import android.app.Application;
...
@@ -4,6 +4,7 @@ import android.app.Application;
import
android.content.Context
;
import
android.content.Context
;
import
com.facebook.react.PackageList
;
import
com.facebook.react.PackageList
;
import
com.facebook.react.ReactApplication
;
import
com.facebook.react.ReactApplication
;
import
com.wenkesj.voice.VoicePackage
;
import
com.facebook.react.ReactInstanceManager
;
import
com.facebook.react.ReactInstanceManager
;
import
com.facebook.react.ReactNativeHost
;
import
com.facebook.react.ReactNativeHost
;
import
com.facebook.react.ReactPackage
;
import
com.facebook.react.ReactPackage
;
...
@@ -27,7 +28,7 @@ public class MainApplication extends Application implements ReactApplication {
...
@@ -27,7 +28,7 @@ public class MainApplication extends Application implements ReactApplication {
@SuppressWarnings
(
"UnnecessaryLocalVariable"
)
@SuppressWarnings
(
"UnnecessaryLocalVariable"
)
List
<
ReactPackage
>
packages
=
new
PackageList
(
this
).
getPackages
();
List
<
ReactPackage
>
packages
=
new
PackageList
(
this
).
getPackages
();
// Packages that cannot be autolinked yet can be added manually here, for example:
// Packages that cannot be autolinked yet can be added manually here, for example:
//
packages.add(new MyReactNativ
ePackage());
//
packages.add(new Voic
ePackage());
return
packages
;
return
packages
;
}
}
...
...
android/settings.gradle
View file @
19284a8a
rootProject
.
name
=
'better_you'
rootProject
.
name
=
'better_you'
include
':@react-native-voice_voice'
project
(
':@react-native-voice_voice'
).
projectDir
=
new
File
(
rootProject
.
projectDir
,
'../node_modules/@react-native-voice/voice/android'
)
include
':@react-native-voice_voice'
project
(
':@react-native-voice_voice'
).
projectDir
=
new
File
(
rootProject
.
projectDir
,
'../node_modules/@react-native-voice/voice/android'
)
include
':@react-native-voice_voice'
project
(
':@react-native-voice_voice'
).
projectDir
=
new
File
(
rootProject
.
projectDir
,
'../node_modules/@react-native-voice/voice/android'
)
include
':@react-native-community_voice'
project
(
':@react-native-community_voice'
).
projectDir
=
new
File
(
rootProject
.
projectDir
,
'../node_modules/@react-native-community/voice/android'
)
apply
from:
file
(
"../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"
);
applyNativeModulesSettingsGradle
(
settings
)
apply
from:
file
(
"../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"
);
applyNativeModulesSettingsGradle
(
settings
)
include
':app'
include
':app'
includeBuild
(
'../node_modules/react-native-gradle-plugin'
)
includeBuild
(
'../node_modules/react-native-gradle-plugin'
)
...
@@ -7,6 +15,9 @@ includeBuild('../node_modules/react-native-gradle-plugin')
...
@@ -7,6 +15,9 @@ includeBuild('../node_modules/react-native-gradle-plugin')
include
':react-native-vector-icons'
include
':react-native-vector-icons'
project
(
':react-native-vector-icons'
).
projectDir
=
new
File
(
rootProject
.
projectDir
,
'../node_modules/react-native-vector-icons/android'
)
project
(
':react-native-vector-icons'
).
projectDir
=
new
File
(
rootProject
.
projectDir
,
'../node_modules/react-native-vector-icons/android'
)
include
':@react-native-voice_voice'
,
':app'
project
(
':@react-native-voice_voice'
).
projectDir
=
new
File
(
rootProject
.
projectDir
,
'../node_modules/@react-native-voice/voice/android'
)
if
(
settings
.
hasProperty
(
"newArchEnabled"
)
&&
settings
.
newArchEnabled
==
"true"
)
{
if
(
settings
.
hasProperty
(
"newArchEnabled"
)
&&
settings
.
newArchEnabled
==
"true"
)
{
include
(
":ReactAndroid"
)
include
(
":ReactAndroid"
)
project
(
":ReactAndroid"
).
projectDir
=
file
(
'../node_modules/react-native/ReactAndroid'
)
project
(
":ReactAndroid"
).
projectDir
=
file
(
'../node_modules/react-native/ReactAndroid'
)
...
...
ios/Podfile
View file @
19284a8a
...
@@ -19,6 +19,8 @@ target 'better_you' do
...
@@ -19,6 +19,8 @@ target 'better_you' do
:app_path
=>
"
#{
Pod
::
Config
.
instance
.
installation_root
}
/.."
:app_path
=>
"
#{
Pod
::
Config
.
instance
.
installation_root
}
/.."
)
)
pod
'react-native-voice'
,
:path
=>
'../node_modules/@react-native-community/voice'
target
'better_youTests'
do
target
'better_youTests'
do
inherit!
:complete
inherit!
:complete
# Pods for testing
# Pods for testing
...
...
package.json
View file @
19284a8a
...
@@ -10,7 +10,9 @@
...
@@ -10,7 +10,9 @@
"lint"
:
"eslint ."
"lint"
:
"eslint ."
},
},
"dependencies"
:
{
"dependencies"
:
{
"
@alan-ai/alan-sdk-react-native
"
:
"
^1.15.0
"
,
"
@react-native-async-storage/async-storage
"
:
"
^1.17.3
"
,
"
@react-native-async-storage/async-storage
"
:
"
^1.17.3
"
,
"
@react-native-voice/voice
"
:
"
^3.2.4
"
,
"
@react-navigation/core
"
:
"
^6.2.1
"
,
"
@react-navigation/core
"
:
"
^6.2.1
"
,
"
@react-navigation/native
"
:
"
^6.0.10
"
,
"
@react-navigation/native
"
:
"
^6.0.10
"
,
"
@react-navigation/native-stack
"
:
"
^6.6.2
"
,
"
@react-navigation/native-stack
"
:
"
^6.6.2
"
,
...
@@ -23,7 +25,8 @@
...
@@ -23,7 +25,8 @@
"
react-native-safe-area-context
"
:
"
^4.2.4
"
,
"
react-native-safe-area-context
"
:
"
^4.2.4
"
,
"
react-native-screens
"
:
"
^3.13.1
"
,
"
react-native-screens
"
:
"
^3.13.1
"
,
"
react-native-uuid
"
:
"
^2.0.1
"
,
"
react-native-uuid
"
:
"
^2.0.1
"
,
"
react-native-vector-icons
"
:
"
^9.2.0
"
"
react-native-vector-icons
"
:
"
^9.2.0
"
,
"
unix-timestamp
"
:
"
^1.0.2
"
},
},
"devDependencies"
:
{
"devDependencies"
:
{
"
@babel/core
"
:
"
^7.17.9
"
,
"
@babel/core
"
:
"
^7.17.9
"
,
...
...
src/api.js
View file @
19284a8a
import
axios
from
"
axios
"
;
import
axios
from
"
axios
"
;
const
RASA_BASE
=
"
http://192.168.124.34:5005/webhooks/rest/webhook
"
const
RASA_BASE
=
"
http://192.168.170.35:5005/webhooks/rest/webhook/
"
const
SENTI_BASE
=
"
http://127.0.0.1:8000/chats/
"
export
async
function
sendToRasa
(
body
){
export
async
function
sendToRasa
(
body
){
return
axios
({
return
axios
({
url
:
RASA_BASE
,
url
:
RASA_BASE
,
method
:
"
POST
"
,
method
:
"
POST
"
,
data
:
{
message
:
body
}
data
:
body
})
}
export
async
function
getChats
(
id
){
return
axios
({
url
:
SENTI_BASE
+
id
,
method
:
"
GET
"
})
})
}
}
\ No newline at end of file
src/assets/utils/UnixConverter.js
0 → 100644
View file @
19284a8a
const
timestamp
=
require
(
'
unix-timestamp
'
);
export
function
unixConvertion
(
val
){
return
timestamp
.
toDate
(
val
)
}
\ No newline at end of file
src/components/chatbot/ChatHeader.js
View file @
19284a8a
...
@@ -20,7 +20,7 @@ export const ChatHeader = () => {
...
@@ -20,7 +20,7 @@ export const ChatHeader = () => {
<
/View
>
<
/View
>
<
/TouchableOpacity
>
<
/TouchableOpacity
>
<
View
style
=
{
styles
.
options
}
>
<
View
style
=
{
styles
.
options
}
>
<
TouchableOpacity
{
/*
<TouchableOpacity
style={{ paddingHorizontal: 5 }}
style={{ paddingHorizontal: 5 }}
>
>
<Icon
<Icon
...
@@ -28,7 +28,7 @@ export const ChatHeader = () => {
...
@@ -28,7 +28,7 @@ export const ChatHeader = () => {
size={30}
size={30}
color={COLOR.white}
color={COLOR.white}
/>
/>
<
/TouchableOpacity
>
</TouchableOpacity>
*/
}
<
TouchableOpacity
style
=
{{
paddingHorizontal
:
20
}}
>
<
TouchableOpacity
style
=
{{
paddingHorizontal
:
20
}}
>
<
Icon
<
Icon
name
=
"
ellipsis-v
"
name
=
"
ellipsis-v
"
...
...
src/screens/Chatbot.js
View file @
19284a8a
...
@@ -2,12 +2,14 @@ import axios from 'axios';
...
@@ -2,12 +2,14 @@ import axios from 'axios';
import
React
,
{
useCallback
,
useEffect
,
useState
}
from
'
react
'
import
React
,
{
useCallback
,
useEffect
,
useState
}
from
'
react
'
import
{
Text
,
View
}
from
'
react-native
'
import
{
Text
,
View
}
from
'
react-native
'
import
{
Bubble
,
GiftedChat
,
Send
}
from
'
react-native-gifted-chat
'
;
import
{
Bubble
,
GiftedChat
,
Send
}
from
'
react-native-gifted-chat
'
;
import
{
sendToRasa
}
from
'
../api
'
;
import
{
getChats
,
sendToRasa
}
from
'
../api
'
;
import
{
ChatHeader
}
from
'
../components/chatbot/ChatHeader
'
import
{
ChatHeader
}
from
'
../components/chatbot/ChatHeader
'
import
uuid
from
'
react-native-uuid
'
;
import
uuid
from
'
react-native-uuid
'
;
import
{
IMAGE
}
from
'
../assets/images/chatbotImage
'
import
{
IMAGE
}
from
'
../assets/images/chatbotImage
'
import
{
COLOR
,
FONT
}
from
'
../themes
'
;
import
{
COLOR
,
FONT
}
from
'
../themes
'
;
import
MaterialCommunityIcons
from
'
react-native-vector-icons/MaterialCommunityIcons
'
import
MaterialCommunityIcons
from
'
react-native-vector-icons/MaterialCommunityIcons
'
import
{
unixConvertion
}
from
'
../assets/utils/UnixConverter
'
;
import
{
AlanView
}
from
'
@alan-ai/alan-sdk-react-native
'
;
export
const
Chatbot
=
()
=>
{
export
const
Chatbot
=
()
=>
{
const
[
messages
,
setMessages
]
=
useState
([]);
const
[
messages
,
setMessages
]
=
useState
([]);
...
@@ -22,23 +24,64 @@ export const Chatbot = () => {
...
@@ -22,23 +24,64 @@ export const Chatbot = () => {
name
:
'
USER
'
,
name
:
'
USER
'
,
};
};
const
BOT_MSG
=
[{
_id
:
uuid
.
v4
(),
text
:
`Hi! I am the FAQ bot 🤖 from TEST.\n\nHow may I help you with today?`
,
createdAt
:
new
Date
(),
user
:
BOT_USER
}]
useEffect
(()
=>
{
useEffect
(()
=>
{
setMessages
([
getChatForUser
();
{
_id
:
1
,
text
:
`Hi! I am the FAQ bot 🤖 from TEST.\n\nHow may I help you with today?`
,
createdAt
:
new
Date
(),
user
:
BOT_USER
},
])
},
[])
},
[])
const
getChatForUser
=
async
()
=>
{
try
{
const
res
=
await
getChats
(
USER
.
_id
)
const
filter1
=
res
.
data
[
0
].
events
//Filter the User and Bot messaages
const
chats
=
filter1
.
filter
((
n
)
=>
{
return
(
n
.
event
==
"
user
"
||
n
.
event
==
"
bot
"
)
&&
n
})
console
.
log
(
chats
)
let
conv
=
[];
chats
.
map
((
c
)
=>
{
if
(
c
.
event
==
"
bot
"
){
const
chat
=
{
...
c
,
_id
:
uuid
.
v4
(),
createdAt
:
new
Date
(
unixConvertion
(
c
.
timestamp
)),
user
:
BOT_USER
}
conv
.
push
(
chat
)
}
else
{
const
chat
=
{
...
c
,
_id
:
uuid
.
v4
(),
createdAt
:
new
Date
(
unixConvertion
(
c
.
timestamp
)),
user
:
USER
}
conv
.
push
(
chat
)
}
}
)
console
.
log
(
conv
)
setMessages
(
conv
.
concat
(
BOT_MSG
));
}
catch
(
err
){
console
.
log
(
err
)
}
}
const
onSend
=
useCallback
(
async
(
msg
=
[])
=>
{
const
onSend
=
useCallback
(
async
(
msg
=
[])
=>
{
console
.
log
(
msg
[
0
].
text
);
console
.
log
(
msg
[
0
].
text
);
setMessages
(
previousMessages
=>
(
GiftedChat
.
append
(
previousMessages
,
msg
)));
setMessages
(
previousMessages
=>
(
GiftedChat
.
append
(
previousMessages
,
msg
)));
try
{
try
{
const
res
=
await
sendToRasa
(
msg
[
0
].
text
);
const
req
=
{
message
:
msg
[
0
].
text
,
sender
:
USER
.
_id
}
const
res
=
await
sendToRasa
(
req
);
let
reply
=
[];
let
reply
=
[];
res
.
data
.
map
((
d
)
=>
{
res
.
data
.
map
((
d
)
=>
{
console
.
log
(
d
.
text
);
console
.
log
(
d
.
text
);
...
@@ -104,12 +147,12 @@ export const Chatbot = () => {
...
@@ -104,12 +147,12 @@ export const Chatbot = () => {
return
(
return
(
<
View
style
=
{{
flex
:
1
}}
>
<
View
style
=
{{
flex
:
1
}}
>
<
ChatHeader
/>
<
ChatHeader
/>
<
AlanView
projectid
=
{
'
900e11c36e836f117bff78fe3fac34872e956eca572e1d8b807a3e2338fdd0dc/stage
'
}
/
>
<
GiftedChat
<
GiftedChat
messages
=
{
messages
.
reverse
()}
messages
=
{
messages
.
reverse
()}
onSend
=
{
msg
=>
onSend
(
msg
)}
onSend
=
{
msg
=>
onSend
(
msg
)}
user
=
{
USER
}
user
=
{
USER
}
alwaysShowSend
=
{
true
}
alwaysShowSend
=
{
true
}
loadEarlier
=
{
true
}
renderAvatarOnTop
=
{
true
}
renderAvatarOnTop
=
{
true
}
renderBubble
=
{
renderBubble
}
renderBubble
=
{
renderBubble
}
renderSend
=
{
renderSend
}
renderSend
=
{
renderSend
}
...
...
src/screens/Home.js
View file @
19284a8a
...
@@ -9,8 +9,9 @@ export const Home = () => {
...
@@ -9,8 +9,9 @@ export const Home = () => {
const
navigation
=
useNavigation
();
const
navigation
=
useNavigation
();
return
(
return
(
<
View
style
=
{
styles
.
container
}
>
<
View
style
=
{
styles
.
container
}
>
<
Text
style
=
{{
fontSize
:
20
,
fontFamily
:
FONT
.
Regular
}}
>
Ho
a
me
<
/Text
>
<
Text
style
=
{{
fontSize
:
20
,
fontFamily
:
FONT
.
Regular
}}
>
Home
<
/Text
>
<
Button
title
=
"
Chatbot
"
onPress
=
{()
=>
navigation
.
navigate
(
'
Chatbot
'
)}
/
>
<
Button
title
=
"
Chatbot
"
onPress
=
{()
=>
navigation
.
navigate
(
'
Chatbot
'
)}
/
>
<
Button
title
=
"
Alan
"
onPress
=
{()
=>
navigation
.
navigate
(
'
VoiceConversation
'
)}
/
>
<
/View
>
<
/View
>
);
);
};
};
...
...
src/screens/VoiceConversation.js
0 → 100644
View file @
19284a8a
import
React
,
{
useEffect
,
useState
}
from
'
react
'
;
import
{
View
,
Text
,
ScrollView
,
TouchableHighlight
,
Image
,
StyleSheet
,
TextInput
,
TouchableOpacity
}
from
'
react-native
'
;
import
Voice
from
'
@react-native-voice/voice
'
;
import
MaterialCommunityIcons
from
'
react-native-vector-icons/MaterialCommunityIcons
'
import
{
COLOR
}
from
'
../themes
'
;
export
function
VoiceConversation
()
{
const
[
result
,
setResult
]
=
useState
(
''
)
useEffect
(()
=>
{
Voice
.
onSpeechStart
=
onSpeechStartHandler
;
Voice
.
onSpeechEnd
=
onSpeechEndHandler
;
Voice
.
onSpeechResults
=
onSpeechResultsHandler
;
return
()
=>
{
Voice
.
destroy
().
then
(
Voice
.
removeAllListeners
);
}
},
[])
const
onSpeechStartHandler
=
(
e
)
=>
{
console
,
log
(
"
e>>>>
"
,
e
)
}
const
onSpeechEndHandler
=
(
e
)
=>
{
console
,
log
(
"
start handler
"
,
e
)
}
const
onSpeechResultsHandler
=
(
e
)
=>
{
console
,
log
(
"
Result handler
"
,
e
)
}
const
startRecording
=
async
()
=>
{
try
{
await
Voice
.
start
(
'
en-Us
'
);
console
.
log
(
"
hit
"
)
}
catch
(
err
){
console
.
log
(
'
error raised
'
,
err
)
}
}
const
stopRecording
=
async
()
=>
{
try
{
await
Voice
.
stop
();
}
catch
(
err
){
console
.
log
(
'
error raised
'
,
err
)
}
}
return
(
<
View
style
=
{
styles
.
container
}
>
<
TextInput
value
=
{
result
}
placeholder
=
'
your text here
'
>
<
/TextInput
>
<
TouchableOpacity
onPress
=
{
startRecording
}
>
<
MaterialCommunityIcons
name
=
'
microphone
'
size
=
{
40
}
color
=
{
COLOR
.
primary
}
/
>
<
/TouchableOpacity
>
<
TouchableOpacity
onPress
=
{
stopRecording
}
>
<
MaterialCommunityIcons
name
=
'
stop
'
size
=
{
40
}
color
=
{
COLOR
.
primary
}
/
>
<
/TouchableOpacity
>
<
/View
>
);
}
const
styles
=
StyleSheet
.
create
({
container
:{
flex
:
1
,
justifyContent
:
'
center
'
,
alignSelf
:
'
center
'
}
})
yarn.lock
View file @
19284a8a
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment