Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
2
240
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-240
240
Commits
888e41c8
Commit
888e41c8
authored
Nov 14, 2022
by
Malsha Rathnasiri
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
major changes
parent
bcf49e15
Changes
12
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
2927 additions
and
1119 deletions
+2927
-1119
DriverApp/api/constants.js
DriverApp/api/constants.js
+1
-1
DriverApp/app.json
DriverApp/app.json
+2
-2
DriverApp/package.json
DriverApp/package.json
+24
-24
DriverApp/screens/TabOneScreen.jsx
DriverApp/screens/TabOneScreen.jsx
+441
-174
DriverApp/screens/loginScreen.js
DriverApp/screens/loginScreen.js
+128
-84
DriverApp/yarn.lock
DriverApp/yarn.lock
+2299
-808
MobileApp/api/constants.js
MobileApp/api/constants.js
+1
-1
MobileApp/screens/TabOneScreen.jsx
MobileApp/screens/TabOneScreen.jsx
+27
-22
backend/backend/cms/serializers.py
backend/backend/cms/serializers.py
+1
-1
backend/backend/cms/views.py
backend/backend/cms/views.py
+3
-2
backend/db.sqlite3
backend/db.sqlite3
+0
-0
backend/output.m4a
backend/output.m4a
+0
-0
No files found.
DriverApp/api/constants.js
View file @
888e41c8
// export const BACKEND_URL = "http://192.168.8.103:8000"
// export const BACKEND_URL = "http://192.168.8.103:8000"
import
{
Platform
}
from
'
react-native
'
import
{
Platform
}
from
'
react-native
'
export
const
BACKEND_ADDRESS
=
Platform
.
OS
==
'
web
'
?
"
http://127.0.0.1:8000
"
:
"
https://
4c61-2401-dd00-10-20-7542-a875-30e7-8931
.ap.ngrok.io
"
export
const
BACKEND_ADDRESS
=
Platform
.
OS
==
'
web
'
?
"
http://127.0.0.1:8000
"
:
"
https://
21a1-112-134-220-172
.ap.ngrok.io
"
export
const
BACKEND_URL
=
`
${
BACKEND_ADDRESS
}
`
export
const
BACKEND_URL
=
`
${
BACKEND_ADDRESS
}
`
DriverApp/app.json
View file @
888e41c8
{
{
"expo"
:
{
"expo"
:
{
"name"
:
"
Mobile
App"
,
"name"
:
"
Driver
App"
,
"slug"
:
"
Mobile
App"
,
"slug"
:
"
Driver
App"
,
"version"
:
"1.0.0"
,
"version"
:
"1.0.0"
,
"orientation"
:
"portrait"
,
"orientation"
:
"portrait"
,
"icon"
:
"./assets/images/icon.png"
,
"icon"
:
"./assets/images/icon.png"
,
...
...
DriverApp/package.json
View file @
888e41c8
...
@@ -14,43 +14,43 @@
...
@@ -14,43 +14,43 @@
"preset"
:
"jest-expo"
"preset"
:
"jest-expo"
},
},
"dependencies"
:
{
"dependencies"
:
{
"@expo/vector-icons"
:
"^1
2
.0.0"
,
"@expo/vector-icons"
:
"^1
3
.0.0"
,
"@react-native-async-storage/async-storage"
:
"~1.1
5.0
"
,
"@react-native-async-storage/async-storage"
:
"~1.1
7.3
"
,
"@react-native-community/slider"
:
"4.
1.12
"
,
"@react-native-community/slider"
:
"4.
2.1
"
,
"@react-navigation/bottom-tabs"
:
"^6.0.5"
,
"@react-navigation/bottom-tabs"
:
"^6.0.5"
,
"@react-navigation/native"
:
"^6.0.2"
,
"@react-navigation/native"
:
"^6.0.2"
,
"@react-navigation/native-stack"
:
"^6.1.0"
,
"@react-navigation/native-stack"
:
"^6.1.0"
,
"axios"
:
"^0.27.2"
,
"axios"
:
"^0.27.2"
,
"expo"
:
"
~44
.0.0"
,
"expo"
:
"
^45
.0.0"
,
"expo-asset"
:
"~8.
4.4
"
,
"expo-asset"
:
"~8.
5.0
"
,
"expo-av"
:
"~1
0.2.0
"
,
"expo-av"
:
"~1
1.2.3
"
,
"expo-constants"
:
"~13.
0.0
"
,
"expo-constants"
:
"~13.
1.1
"
,
"expo-file-system"
:
"~1
3.1.4
"
,
"expo-file-system"
:
"~1
4.0.0
"
,
"expo-font"
:
"~10.
0.4
"
,
"expo-font"
:
"~10.
1.0
"
,
"expo-linking"
:
"~3.
0
.0"
,
"expo-linking"
:
"~3.
1
.0"
,
"expo-media-library"
:
"~14.
0
.0"
,
"expo-media-library"
:
"~14.
1
.0"
,
"expo-speech"
:
"~10.
1
.0"
,
"expo-speech"
:
"~10.
2
.0"
,
"expo-splash-screen"
:
"~0.1
4.0
"
,
"expo-splash-screen"
:
"~0.1
5.1
"
,
"expo-status-bar"
:
"~1.
2
.0"
,
"expo-status-bar"
:
"~1.
3
.0"
,
"expo-web-browser"
:
"~10.
1.0
"
,
"expo-web-browser"
:
"~10.
2.1
"
,
"jwt-decode"
:
"^3.1.2"
,
"jwt-decode"
:
"^3.1.2"
,
"native-base"
:
"^3.4.11"
,
"native-base"
:
"^3.4.11"
,
"react"
:
"17.0.
1
"
,
"react"
:
"17.0.
2
"
,
"react-dom"
:
"17.0.
1
"
,
"react-dom"
:
"17.0.
2
"
,
"react-native"
:
"0.6
4.3
"
,
"react-native"
:
"0.6
8.2
"
,
"react-native-multi-selectbox"
:
"^1.5.0"
,
"react-native-multi-selectbox"
:
"^1.5.0"
,
"react-native-safe-area-context"
:
"
3.3.2
"
,
"react-native-safe-area-context"
:
"
4.2.4
"
,
"react-native-screens"
:
"~3.1
0
.1"
,
"react-native-screens"
:
"~3.1
1
.1"
,
"react-native-svg"
:
"12.
1.1
"
,
"react-native-svg"
:
"12.
3.0
"
,
"react-native-web"
:
"0.17.
1
"
,
"react-native-web"
:
"0.17.
7
"
,
"styled-system"
:
"^5.1.5"
"styled-system"
:
"^5.1.5"
},
},
"devDependencies"
:
{
"devDependencies"
:
{
"@babel/core"
:
"^7.12.9"
,
"@babel/core"
:
"^7.12.9"
,
"@types/react"
:
"~17.0.21"
,
"@types/react"
:
"~17.0.21"
,
"@types/react-native"
:
"~0.6
4.12
"
,
"@types/react-native"
:
"~0.6
7.6
"
,
"jest"
:
"^26.6.3"
,
"jest"
:
"^26.6.3"
,
"jest-expo"
:
"
~44.0.1
"
,
"jest-expo"
:
"
^45.0.0
"
,
"react-test-renderer"
:
"17.0.1"
,
"react-test-renderer"
:
"17.0.1"
,
"typescript"
:
"~4.3.5"
"typescript"
:
"~4.3.5"
},
},
...
...
DriverApp/screens/TabOneScreen.jsx
View file @
888e41c8
This diff is collapsed.
Click to expand it.
DriverApp/screens/loginScreen.js
View file @
888e41c8
import
React
,
{
useState
}
from
'
react
'
import
React
,
{
useState
}
from
"
react
"
;
import
{
StyleSheet
,
TextInput
,
Button
,
Image
,
Dimensions
,
View
,
Text
,
ActivityIndicator
}
from
'
react-native
'
;
import
{
import
EditScreenInfo
from
'
../components/EditScreenInfo
'
;
StyleSheet
,
TextInput
,
Button
,
Image
,
Dimensions
,
View
,
Text
,
ActivityIndicator
,
}
from
"
react-native
"
;
import
EditScreenInfo
from
"
../components/EditScreenInfo
"
;
// import { Text, View } from '../components/Themed';
// import { Text, View } from '../components/Themed';
import
{
TouchableOpacity
}
from
'
react-native
'
;
import
{
TouchableOpacity
}
from
"
react-native
"
;
import
{
ScrollView
,
Toast
}
from
'
native-base
'
;
import
{
ScrollView
,
Toast
}
from
"
native-base
"
;
import
{
ERROR_TOAST_PROPS
}
from
'
../util/util
'
;
import
{
ERROR_TOAST_PROPS
}
from
"
../util/util
"
;
import
{
screenWidth
,
styles
}
from
'
../util/styles
'
;
import
{
screenWidth
,
styles
}
from
"
../util/styles
"
;
import
TTS_logo
from
'
../assets/images/TTS_logo.jpeg
'
import
TTS_logo
from
"
../assets/images/TTS_logo.jpeg
"
;
export
const
LoginScreen
=
({
onLogin
,
navigation
})
=>
{
export
const
LoginScreen
=
({
onLogin
,
navigation
})
=>
{
return
(
return
(
<
ScrollView
style
=
{
styles
.
loginScreenContainer
}
>
<
ScrollView
style
=
{
styles
.
loginScreenContainer
}
>
<
View
style
=
{{
alignContent
:
'
center
'
,
justifyContent
:
'
center
'
}}
>
<
View
style
=
{{
alignContent
:
"
center
"
,
justifyContent
:
"
center
"
}}
>
<
Image
source
=
{
TTS_logo
}
style
=
{{
height
:
screenWidth
-
30
,
width
:
screenWidth
-
30
,
margin
:
'
auto
'
}}
/
>
<
Text
<
/View
>
style
=
{{
padding
:
10
,
<
Text
style
=
{{
textAlign
:
"
center
"
,
padding
:
10
,
fontWeight
:
"
bold
"
,
textAlign
:
'
center
'
,
fontSize
:
30
,
// fontWeight: 'bold',
}}
fontSize
:
40
>
}}
>
Login
<
/Text
>
Driver
App
<
View
style
=
{{
flexDirection
:
'
row
'
,
padding
:
10
,
justifyContent
:
'
center
'
}}
>
<
/Text
>
<
Text
style
=
{{
textAlign
:
'
center
'
}}
>
Don
'
t have an account? </Text>
<
Image
<Text style={{ fontWeight:
'
bold
'
, textAlign:
'
center
'
, }} onPress={() => navigation.replace(
'
Signup
'
)}>Sign up</Text>
source
=
{
TTS_logo
}
</View>
style
=
{{
<View style={styles.formContainer}>
height
:
screenWidth
-
30
,
<LoginForm onLogin={onLogin} />
width
:
screenWidth
-
30
,
</View>
margin
:
"
auto
"
,
</ScrollView>
}}
/
>
<
/View
>
);
<
Text
}
style
=
{{
padding
:
10
,
textAlign
:
"
center
"
,
// fontWeight: 'bold',
fontSize
:
40
,
}}
>
Login
<
/Text
>
<
View
style
=
{{
flexDirection
:
"
row
"
,
padding
:
10
,
justifyContent
:
"
center
"
}}
>
<
Text
style
=
{{
textAlign
:
"
center
"
}}
>
Don
'
t have an account? </Text>
<Text
style={{ fontWeight: "bold", textAlign: "center" }}
onPress={() => navigation.replace("Signup")}
>
Sign up
</Text>
</View>
<View style={styles.formContainer}>
<LoginForm onLogin={onLogin} />
</View>
</ScrollView>
);
};
const LoginForm = ({ onLogin, navigation }) => {
const LoginForm = ({ onLogin, navigation }) => {
var passwordInput;
var passwordInput
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [username, setUsername] = useState(
''
)
const [password, setPassword] = useState(
''
)
const [loading, setLoading] = useState(false)
const [loading, setLoading] = useState(false);
const onSubmit = () => {
console.log({ username, password })
setLoading(true)
if (!username || !password) {
Toast.show({ title:
'
Please
fill
in
all
the
fields
!
'
, ...ERROR_TOAST_PROPS })
} else {
onLogin(username, password)
}
setLoading(false)
const onSubmit = () => {
console.log({ username, password });
setLoading(true);
if (!username || !password) {
Toast.show({
title: "Please fill in all the fields!",
...ERROR_TOAST_PROPS,
});
} else {
onLogin(username, password);
}
}
setLoading(false);
};
return (
return (
<View >
<View>
<TextInput style={styles.input}
<TextInput
defaultValue={username}
style={styles.input}
onChangeText={(e) => {
defaultValue={username}
console.log(e);
onChangeText={(e) => {
setUsername(e)
console.log(e);
}}
setUsername(e);
autoCapitalize="none"
}}
onSubmitEditing={() => passwordInput.focus()}
autoCapitalize="none"
autoCorrect={false}
onSubmitEditing={() => passwordInput.focus()}
keyboardType=
'
email
-
address
'
autoCorrect={false}
returnKeyType="next"
keyboardType="email-address"
placeholder=
'
Email
'
returnKeyType="next"
// placeholderTextColor=
'
rgba
(
225
,
225
,
225
,
0.7
)
'
placeholder="Email"
/>
// placeholderTextColor=
'
rgba
(
225
,
225
,
225
,
0.7
)
'
/>
<TextInput style={styles.input}
defaultValue={password}
onChangeText={(e) => setPassword(e)}
returnKeyType="go"
autoCapitalize="none"
ref={(input) => passwordInput = input}
placeholder=
'
Password
'
onSubmitEditing={onSubmit}
// placeholderTextColor=
'
rgba
(
225
,
225
,
225
,
0.7
)
'
secureTextEntry />
<TouchableOpacity style={styles.buttonContainer}
<TextInput
// onPress={onButtonPress}
style={styles.input}
// disabled={!username || !password}
defaultValue={password}
onChangeText={(e) => setPassword(e)}
returnKeyType="go"
autoCapitalize="none"
ref={(input) => (passwordInput = input)}
placeholder="Password"
onSubmitEditing={onSubmit}
// placeholderTextColor=
'
rgba
(
225
,
225
,
225
,
0.7
)
'
secureTextEntry
/>
onPress={onSubmit}
<TouchableOpacity
disabled={loading}
style={styles.buttonContainer}
>{loading ? <ActivityIndicator /> : <Text style={styles.buttonText}>LOGIN</Text>}
// onPress={onButtonPress}
// disabled={!username || !password}
</TouchableOpacity>
onPress={onSubmit}
</View>
disabled={loading}
// define your styles
>
)
{loading ? (
}
<ActivityIndicator />
) : (
<Text style={styles.buttonText}>LOGIN</Text>
)}
</TouchableOpacity>
</View>
// define your styles
);
};
DriverApp/yarn.lock
View file @
888e41c8
This diff is collapsed.
Click to expand it.
MobileApp/api/constants.js
View file @
888e41c8
// export const BACKEND_URL = "http://192.168.8.103:8000"
// export const BACKEND_URL = "http://192.168.8.103:8000"
import
{
Platform
}
from
'
react-native
'
import
{
Platform
}
from
'
react-native
'
export
const
BACKEND_ADDRESS
=
Platform
.
OS
==
'
web
'
?
"
http://127.0.0.1:8000
"
:
"
https://
4c61-2401-dd00-10-20-7542-a875-30e7-8931
.ap.ngrok.io
"
export
const
BACKEND_ADDRESS
=
Platform
.
OS
==
'
web
'
?
"
http://127.0.0.1:8000
"
:
"
https://
21a1-112-134-220-172
.ap.ngrok.io
"
export
const
BACKEND_URL
=
`
${
BACKEND_ADDRESS
}
`
export
const
BACKEND_URL
=
`
${
BACKEND_ADDRESS
}
`
MobileApp/screens/TabOneScreen.jsx
View file @
888e41c8
...
@@ -94,7 +94,7 @@ export default function ChatScreen({ navigation }) {
...
@@ -94,7 +94,7 @@ export default function ChatScreen({ navigation }) {
const
[
detectedText
,
setDetectedText
]
=
useState
(
""
);
const
[
detectedText
,
setDetectedText
]
=
useState
(
""
);
const
[
playinId
,
setPlayingId
]
=
useState
(
3
);
const
[
playinId
,
setPlayingId
]
=
useState
(
3
);
const
[
chatDetails
,
setChatDetails
]
=
useState
();
const
[
chatDetails
,
setChatDetails
]
=
useState
(
null
);
const
[
chats
,
setChats
]
=
useState
([]);
const
[
chats
,
setChats
]
=
useState
([]);
const
[
input
,
setInput
]
=
useState
(
"
test
"
);
const
[
input
,
setInput
]
=
useState
(
"
test
"
);
...
@@ -172,13 +172,14 @@ export default function ChatScreen({ navigation }) {
...
@@ -172,13 +172,14 @@ export default function ChatScreen({ navigation }) {
},
[]);
},
[]);
const
loadChatDetails
=
async
()
=>
{
const
loadChatDetails
=
async
()
=>
{
await
getOne
(
"
conversations
"
,
1
)
await
AsyncStorage
.
getItem
(
"
user_id
"
).
then
((
user_id
)
=>
{
.
then
((
res
)
=>
{
getOne
(
"
conversations
"
,
1
)
return
res
.
json
();
.
then
((
res
)
=>
{
})
return
res
.
json
();
.
then
((
res
)
=>
{
})
console
.
log
(
res
);
.
then
((
res
)
=>
{
AsyncStorage
.
getItem
(
"
user_id
"
).
then
((
user_id
)
=>
{
console
.
log
(
res
);
console
.
log
(
"
chat details
"
,
user_id
);
console
.
log
(
"
chat details
"
,
user_id
);
//change from user and to user depending on the current user
//change from user and to user depending on the current user
...
@@ -193,7 +194,7 @@ export default function ChatScreen({ navigation }) {
...
@@ -193,7 +194,7 @@ export default function ChatScreen({ navigation }) {
setChatDetails
(
n_res
);
setChatDetails
(
n_res
);
}
}
});
});
});
});
};
};
const
onDefaultMessagePressed
=
(
item
)
=>
{
const
onDefaultMessagePressed
=
(
item
)
=>
{
...
@@ -203,22 +204,26 @@ export default function ChatScreen({ navigation }) {
...
@@ -203,22 +204,26 @@ export default function ChatScreen({ navigation }) {
from_user
:
chatDetails
.
from_user
,
from_user
:
chatDetails
.
from_user
,
to_user
:
chatDetails
.
to_user
,
to_user
:
chatDetails
.
to_user
,
conversation
:
chatDetails
.
id
,
conversation
:
chatDetails
.
id
,
}).
then
((
response
)
=>
{
})
// console.log(response)
.
then
((
response
)
=>
{
}).
catch
(
e
=>
{
// console.log(response)
// console.log({e})
})
});
.
catch
((
e
)
=>
{
// console.log({e})
});
addChats
({
addChats
({
message
:
item
.
A
,
message
:
item
.
A
,
from_user
:
chatDetails
.
to_user
,
from_user
:
chatDetails
.
to_user
,
to_user
:
chatDetails
.
from_user
,
to_user
:
chatDetails
.
from_user
,
conversation
:
chatDetails
.
id
,
conversation
:
chatDetails
.
id
,
}).
then
((
response
)
=>
{
// console.log(response)
}).
catch
(
e
=>
{
// console.log({e})
})
})
.
then
((
response
)
=>
{
// console.log(response)
})
.
catch
((
e
)
=>
{
// console.log({e})
});
setLoading
(
true
);
setLoading
(
true
);
setInput
(
""
);
setInput
(
""
);
...
@@ -231,8 +236,8 @@ export default function ChatScreen({ navigation }) {
...
@@ -231,8 +236,8 @@ export default function ChatScreen({ navigation }) {
}
}
};
};
const
loadChats
=
async
()
=>
{
const
loadChats
=
()
=>
{
await
getList
(
"
chats
"
)
getList
(
"
chats
"
,
{
conversation
:
chatDetails
?.
id
}
)
.
then
((
res
)
=>
{
.
then
((
res
)
=>
{
return
res
.
json
();
return
res
.
json
();
})
})
...
@@ -251,7 +256,7 @@ export default function ChatScreen({ navigation }) {
...
@@ -251,7 +256,7 @@ export default function ChatScreen({ navigation }) {
),
),
}));
}));
setChats
(
sectionChats
);
setChats
(
sectionChats
);
});
})
.
catch
(
e
=>
{
console
.
log
(
e
)})
;
setLoading
(
false
);
setLoading
(
false
);
};
};
...
@@ -299,7 +304,7 @@ export default function ChatScreen({ navigation }) {
...
@@ -299,7 +304,7 @@ export default function ChatScreen({ navigation }) {
<
ActivityIndicator
/>
<
ActivityIndicator
/>
)
:
(
)
:
(
<
SectionList
<
SectionList
style=
{
{
flex
:
1
}
}
//
style=
{{
flex
:
1
}}
refreshing=
{
loading
}
refreshing=
{
loading
}
onRefresh=
{
()
=>
{
onRefresh=
{
()
=>
{
// loadChatDetails() //remove
// loadChatDetails() //remove
...
...
backend/backend/cms/serializers.py
View file @
888e41c8
...
@@ -31,7 +31,7 @@ class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
...
@@ -31,7 +31,7 @@ class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
class
UserSerializer
(
serializers
.
HyperlinkedModelSerializer
):
class
UserSerializer
(
serializers
.
HyperlinkedModelSerializer
):
class
Meta
:
class
Meta
:
model
=
User
model
=
User
fields
=
[
'u
rl'
,
'username'
,
'email'
,
'groups
'
]
fields
=
[
'u
sername'
,
'id
'
]
class
GroupSerializer
(
serializers
.
HyperlinkedModelSerializer
):
class
GroupSerializer
(
serializers
.
HyperlinkedModelSerializer
):
...
...
backend/backend/cms/views.py
View file @
888e41c8
...
@@ -74,7 +74,7 @@ class MlModelViewSet(viewsets.ViewSet):
...
@@ -74,7 +74,7 @@ class MlModelViewSet(viewsets.ViewSet):
# Custom api to add sample chats
# Custom api to add sample chats
@
action
(
detail
=
False
)
@
action
(
detail
=
False
)
def
add
C
hats
(
*
args
,
**
kwargs
):
def
add
_default_c
hats
(
*
args
,
**
kwargs
):
admin
=
User
.
objects
.
get
(
username
=
'admin'
)
admin
=
User
.
objects
.
get
(
username
=
'admin'
)
user2
=
User
.
objects
.
get
(
username
=
'user2'
)
user2
=
User
.
objects
.
get
(
username
=
'user2'
)
...
@@ -160,9 +160,10 @@ class ChatViewSet(viewsets.ModelViewSet):
...
@@ -160,9 +160,10 @@ class ChatViewSet(viewsets.ModelViewSet):
# ovveride defualt list action to get chats of specific user conversation
# ovveride defualt list action to get chats of specific user conversation
def
list
(
self
,
request
,
pk
=
None
):
def
list
(
self
,
request
,
pk
=
None
):
conv_id
=
request
.
query_params
.
get
(
"conversation"
)
if
pk
==
None
:
if
pk
==
None
:
chats
=
Chat
.
objects
\
chats
=
Chat
.
objects
\
.
filter
(
conversation_id
=
1
)
\
.
filter
(
conversation_id
=
int
(
conv_id
)
)
\
.
filter
(
Q
(
from_user_id
=
request
.
user
.
id
)
|
Q
(
.
filter
(
Q
(
from_user_id
=
request
.
user
.
id
)
|
Q
(
to_user_id
=
request
.
user
.
id
))
to_user_id
=
request
.
user
.
id
))
else
:
else
:
...
...
backend/db.sqlite3
View file @
888e41c8
No preview for this file type
backend/output.m4a
View file @
888e41c8
No preview for this file type
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