@ -0,0 +1,34 @@ | |||
package Auth | |||
import ( | |||
"net/http" | |||
"time" | |||
) | |||
func Logout(w http.ResponseWriter, r *http.Request) { | |||
var ( | |||
c *http.Cookie | |||
sessionToken string | |||
err error | |||
) | |||
c, err = r.Cookie("session_token") | |||
if err != nil { | |||
if err == http.ErrNoCookie { | |||
w.WriteHeader(http.StatusUnauthorized) | |||
return | |||
} | |||
w.WriteHeader(http.StatusBadRequest) | |||
return | |||
} | |||
sessionToken = c.Value | |||
delete(Sessions, sessionToken) | |||
http.SetCookie(w, &http.Cookie{ | |||
Name: "session_token", | |||
Value: "", | |||
Expires: time.Now(), | |||
}) | |||
} |
@ -0,0 +1,41 @@ | |||
package Friends | |||
import ( | |||
"encoding/json" | |||
"net/http" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Api/Auth" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" | |||
) | |||
func EncryptedFriendList(w http.ResponseWriter, r *http.Request) { | |||
var ( | |||
userSession Auth.Session | |||
friends []Models.Friend | |||
returnJson []byte | |||
err error | |||
) | |||
userSession, err = Auth.CheckCookie(r) | |||
if err != nil { | |||
http.Error(w, "Forbidden", http.StatusUnauthorized) | |||
return | |||
} | |||
friends, err = Database.GetFriendsByUserId(userSession.UserID) | |||
if err != nil { | |||
http.Error(w, "Error", http.StatusInternalServerError) | |||
return | |||
} | |||
returnJson, err = json.MarshalIndent(friends, "", " ") | |||
if err != nil { | |||
http.Error(w, "Error", http.StatusInternalServerError) | |||
return | |||
} | |||
w.WriteHeader(http.StatusOK) | |||
w.Write(returnJson) | |||
} |
@ -0,0 +1,59 @@ | |||
package Friends | |||
import ( | |||
"encoding/json" | |||
"io/ioutil" | |||
"net/http" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Util" | |||
) | |||
func FriendRequest(w http.ResponseWriter, r *http.Request) { | |||
var ( | |||
user Models.User | |||
requestBody []byte | |||
requestJson map[string]interface{} | |||
friendID []byte | |||
friendRequest Models.Friend | |||
err error | |||
) | |||
user, err = Util.GetUserById(w, r) | |||
if err != nil { | |||
http.Error(w, "Not Found", http.StatusNotFound) | |||
return | |||
} | |||
requestBody, err = ioutil.ReadAll(r.Body) | |||
if err != nil { | |||
http.Error(w, "Not Found", http.StatusNotFound) | |||
return | |||
} | |||
json.Unmarshal(requestBody, &requestJson) | |||
if requestJson["id"] == nil { | |||
http.Error(w, "Invalid Data", http.StatusBadRequest) | |||
return | |||
} | |||
friendID, err = Util.ToBytes(requestJson["id"]) | |||
if requestJson["id"] == nil { | |||
http.Error(w, "Error", http.StatusInternalServerError) | |||
return | |||
} | |||
friendRequest = Models.Friend{ | |||
UserID: user.ID, | |||
FriendID: friendID, | |||
} | |||
err = Database.CreateFriendRequest(&friendRequest) | |||
if requestJson["id"] == nil { | |||
http.Error(w, "Error", http.StatusInternalServerError) | |||
return | |||
} | |||
w.WriteHeader(http.StatusNoContent) | |||
} |
@ -0,0 +1,32 @@ | |||
package Friends | |||
import ( | |||
"encoding/json" | |||
"net/http" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Util" | |||
) | |||
func Friend(w http.ResponseWriter, r *http.Request) { | |||
var ( | |||
userData Models.User | |||
returnJson []byte | |||
err error | |||
) | |||
userData, err = Util.GetUserById(w, r) | |||
if err != nil { | |||
http.Error(w, "Not Found", http.StatusNotFound) | |||
return | |||
} | |||
returnJson, err = json.MarshalIndent(userData, "", " ") | |||
if err != nil { | |||
http.Error(w, "Error", http.StatusInternalServerError) | |||
return | |||
} | |||
w.WriteHeader(http.StatusOK) | |||
w.Write(returnJson) | |||
} |
@ -0,0 +1,43 @@ | |||
package Messages | |||
import ( | |||
"encoding/json" | |||
"net/http" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" | |||
"github.com/gorilla/mux" | |||
) | |||
func MessageThread(w http.ResponseWriter, r *http.Request) { | |||
var ( | |||
messages []Models.Message | |||
urlVars map[string]string | |||
threadID string | |||
returnJson []byte | |||
ok bool | |||
err error | |||
) | |||
urlVars = mux.Vars(r) | |||
threadID, ok = urlVars["threadID"] | |||
if !ok { | |||
http.Error(w, "Not Found", http.StatusNotFound) | |||
return | |||
} | |||
messages, err = Database.GetMessagesByThreadId(threadID) | |||
if !ok { | |||
http.Error(w, "Not Found", http.StatusNotFound) | |||
return | |||
} | |||
returnJson, err = json.MarshalIndent(messages, "", " ") | |||
if err != nil { | |||
http.Error(w, "Error", http.StatusInternalServerError) | |||
return | |||
} | |||
w.WriteHeader(http.StatusOK) | |||
w.Write(returnJson) | |||
} |
@ -0,0 +1,57 @@ | |||
package Database | |||
import ( | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" | |||
"github.com/gofrs/uuid" | |||
"gorm.io/gorm" | |||
"gorm.io/gorm/clause" | |||
) | |||
func GetFriendById(id string) (Models.Friend, error) { | |||
var ( | |||
friend Models.Friend | |||
err error | |||
) | |||
err = DB.Preload(clause.Associations). | |||
First(&friend, "id = ?", id). | |||
Error | |||
return friend, err | |||
} | |||
func GetFriendsByUserId(userID string) ([]Models.Friend, error) { | |||
var ( | |||
friends []Models.Friend | |||
err error | |||
) | |||
err = DB.Model(Models.Friend{}). | |||
Where("user_id = ?", userID). | |||
Find(&friends). | |||
Error | |||
return friends, err | |||
} | |||
func CreateFriendRequest(friend *Models.Friend) error { | |||
var ( | |||
err error | |||
) | |||
friend.ThreadID, err = uuid.NewV1() | |||
if err != nil { | |||
return err | |||
} | |||
return DB.Session(&gorm.Session{FullSaveAssociations: true}). | |||
Create(friend). | |||
Error | |||
} | |||
func DeleteFriend(friend *Models.Friend) error { | |||
return DB.Session(&gorm.Session{FullSaveAssociations: true}). | |||
Delete(friend). | |||
Error | |||
} |
@ -0,0 +1,52 @@ | |||
package Database | |||
import ( | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" | |||
"gorm.io/gorm" | |||
"gorm.io/gorm/clause" | |||
) | |||
func GetMessageById(id string) (Models.Message, error) { | |||
var ( | |||
message Models.Message | |||
err error | |||
) | |||
err = DB.Preload(clause.Associations). | |||
First(&message, "id = ?", id). | |||
Error | |||
return message, err | |||
} | |||
func GetMessagesByThreadId(id string) ([]Models.Message, error) { | |||
var ( | |||
messages []Models.Message | |||
err error | |||
) | |||
err = DB.Preload(clause.Associations). | |||
Find(&messages, "thread_id = ?", id). | |||
Error | |||
return messages, err | |||
} | |||
func CreateMessage(message *Models.Message) error { | |||
var ( | |||
err error | |||
) | |||
err = DB.Session(&gorm.Session{FullSaveAssociations: true}). | |||
Create(message). | |||
Error | |||
return err | |||
} | |||
func DeleteMessage(message *Models.Message) error { | |||
return DB.Session(&gorm.Session{FullSaveAssociations: true}). | |||
Delete(message). | |||
Error | |||
} |
@ -0,0 +1,67 @@ | |||
package Seeder | |||
import ( | |||
"time" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" | |||
) | |||
func seedFriend(user, friendUser Models.User) error { | |||
var ( | |||
friend Models.Friend | |||
err error | |||
) | |||
friend = Models.Friend{ | |||
UserID: user.ID, | |||
FriendID: encryptWithPublicKey(friendUser.ID.Bytes(), decodedPublicKey), | |||
AcceptedAt: time.Now(), | |||
} | |||
err = Database.CreateFriendRequest(&friend) | |||
if err != nil { | |||
return err | |||
} | |||
friend = Models.Friend{ | |||
UserID: friendUser.ID, | |||
FriendID: encryptWithPublicKey(user.ID.Bytes(), decodedPublicKey), | |||
AcceptedAt: time.Now(), | |||
} | |||
return Database.CreateFriendRequest(&friend) | |||
} | |||
func SeedFriends() { | |||
var ( | |||
primaryUser Models.User | |||
secondaryUser Models.User | |||
i int | |||
err error | |||
) | |||
primaryUser, err = Database.GetUserByUsername("testUser") | |||
if err != nil { | |||
panic(err) | |||
} | |||
secondaryUser, err = Database.GetUserByUsername("testUser2") | |||
if err != nil { | |||
panic(err) | |||
} | |||
err = seedFriend(primaryUser, secondaryUser) | |||
for i = 0; i <= 3; i++ { | |||
secondaryUser, err = Database.GetUserByUsername(userNames[i]) | |||
if err != nil { | |||
panic(err) | |||
} | |||
err = seedFriend(primaryUser, secondaryUser) | |||
if err != nil { | |||
panic(err) | |||
} | |||
} | |||
} |
@ -0,0 +1,136 @@ | |||
package Seeder | |||
import ( | |||
"bytes" | |||
"crypto/aes" | |||
"crypto/cipher" | |||
"crypto/rand" | |||
"crypto/rsa" | |||
"crypto/sha512" | |||
"encoding/pem" | |||
"hash" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" | |||
"github.com/gofrs/uuid" | |||
) | |||
// EncryptWithPublicKey encrypts data with public key | |||
func encryptWithPublicKey(msg []byte, pub *rsa.PublicKey) []byte { | |||
var ( | |||
hash hash.Hash | |||
) | |||
hash = sha512.New() | |||
ciphertext, err := rsa.EncryptOAEP(hash, rand.Reader, pub, msg, nil) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return ciphertext | |||
} | |||
func PKCS5Padding(ciphertext []byte, blockSize int, after int) []byte { | |||
var ( | |||
padding int | |||
padtext []byte | |||
) | |||
padding = (blockSize - len(ciphertext)%blockSize) | |||
padtext = bytes.Repeat([]byte{byte(padding)}, padding) | |||
return append(ciphertext, padtext...) | |||
} | |||
func seedMessage(primaryUser, secondaryUser Models.User, threadID uuid.UUID, i int) error { | |||
var ( | |||
messageKey Models.Message | |||
messageData Models.MessageData | |||
block cipher.Block | |||
mode cipher.BlockMode | |||
pemBlock *pem.Block | |||
plaintext string | |||
ciphertext []byte | |||
bKey []byte | |||
bIV []byte | |||
bPlaintext []byte | |||
err error | |||
) | |||
plaintext = "Test Message" | |||
bKey = make([]byte, 32) | |||
_, err = rand.Read(bKey) | |||
if err != nil { | |||
panic(err) | |||
} | |||
bIV = make([]byte, 16) | |||
_, err = rand.Read(bIV) | |||
if err != nil { | |||
panic(err) | |||
} | |||
bPlaintext = PKCS5Padding([]byte(plaintext), aes.BlockSize, len(plaintext)) | |||
pemBlock = &pem.Block{ | |||
Type: "AES KEY", | |||
Bytes: bKey, | |||
} | |||
block, err = aes.NewCipher(bKey) | |||
if err != nil { | |||
panic(err) | |||
} | |||
ciphertext = make([]byte, len(bPlaintext)) | |||
mode = cipher.NewCBCEncrypter(block, bIV) | |||
mode.CryptBlocks(ciphertext, bPlaintext) | |||
messageData = Models.MessageData{ | |||
Data: ciphertext, | |||
} | |||
messageKey = Models.Message{ | |||
UserID: primaryUser.ID, | |||
MessageData: messageData, | |||
MessageType: "sender", | |||
RelationalUserId: encryptWithPublicKey(secondaryUser.ID.Bytes(), decodedPublicKey), | |||
SymmetricKey: string(pem.EncodeToMemory(pemBlock)), | |||
} | |||
return Database.CreateMessage(&messageKey) | |||
} | |||
func SeedMessages() { | |||
var ( | |||
primaryUser Models.User | |||
secondaryUser Models.User | |||
threadID uuid.UUID | |||
i int | |||
err error | |||
) | |||
primaryUser, err = Database.GetUserByUsername("testUser") | |||
if err != nil { | |||
panic(err) | |||
} | |||
secondaryUser, err = Database.GetUserByUsername("testUser2") | |||
if err != nil { | |||
panic(err) | |||
} | |||
threadID, err = uuid.NewV4() | |||
if err != nil { | |||
panic(err) | |||
} | |||
for i = 0; i <= 20; i++ { | |||
err = seedMessage(primaryUser, secondaryUser, threadID, i) | |||
if err != nil { | |||
panic(err) | |||
} | |||
} | |||
} |
@ -0,0 +1,50 @@ | |||
package Seeder | |||
import ( | |||
"crypto/rsa" | |||
"crypto/x509" | |||
"encoding/pem" | |||
"log" | |||
) | |||
const ( | |||
// Encrypted with "password" | |||
encryptedPrivateKey string = ` | |||
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 | |||
` | |||
publicKey string = ` | |||
-----BEGIN PUBLIC KEY----- | |||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAidHsO9H0fMd6cu9dQiOwu/qtaDq+lG59kudoHGx8WZhucpOSp2df95UzjnXmMw1fz0Mx5fjgBaunG7u1MVBy+7IdYwXp+iz3xZulVy1Yv3e+GMzgQhAuxhz/8wsHkFkUTweDZCrCZPhkTXlCrxDIuKykQ0el+RnpSjyyljOsAWAdTmrz0a2Nh9FOmF1v49pWy3Z3aJG58xl1dmFkpXjT3m36GB4Z30iR1uOMnNdrtfwIfLQAc7nmle2LxCHeEMYzlA9y6JChm5kGI6FmglSKYTxvDm40jA5mYyDCPADeCodbIw4Mtm0nwrM3QqKWj+jlaL8BY7/jjaosmz7VK2do4wIDAQAB | |||
-----END PUBLIC KEY----- | |||
` | |||
) | |||
var ( | |||
decodedPublicKey *rsa.PublicKey | |||
) | |||
func Seed() { | |||
var ( | |||
block *pem.Block | |||
decKey any | |||
err error | |||
) | |||
// TODO: Fix this parsing | |||
block, _ = pem.Decode([]byte(publicKey)) | |||
decKey, err = x509.ParsePKIXPublicKey(block.Bytes) | |||
if err != nil { | |||
panic(err) | |||
} | |||
decodedPublicKey = decKey.(*rsa.PublicKey) | |||
log.Println("Seeding users...") | |||
SeedUsers() | |||
log.Println("Seeding friend connections...") | |||
SeedFriends() | |||
log.Println("Seeding messages...") | |||
SeedMessages() | |||
} |
@ -0,0 +1,68 @@ | |||
package Seeder | |||
import ( | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Api/Auth" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" | |||
) | |||
var userNames = []string{ | |||
"assuredcoot", | |||
"quotesteeve", | |||
"blueberriessiemens", | |||
"eliteexaggerate", | |||
"twotrice", | |||
"moderagged", | |||
"duleelderly", | |||
"stringdetailed", | |||
"nodesanymore", | |||
"sacredpolitical", | |||
"pajamasenergy", | |||
} | |||
func createUser(username string) (Models.User, error) { | |||
var ( | |||
userData Models.User | |||
password string | |||
err error | |||
) | |||
password, err = Auth.HashPassword("password") | |||
if err != nil { | |||
return Models.User{}, err | |||
} | |||
userData = Models.User{ | |||
Username: username, | |||
Password: password, | |||
AsymmetricPrivateKey: encryptedPrivateKey, | |||
AsymmetricPublicKey: publicKey, | |||
} | |||
err = Database.CreateUser(&userData) | |||
return userData, err | |||
} | |||
func SeedUsers() { | |||
var ( | |||
i int | |||
err error | |||
) | |||
// Seed users used for conversation seeding | |||
_, err = createUser("testUser") | |||
if err != nil { | |||
panic(err) | |||
} | |||
_, err = createUser("testUser2") | |||
if err != nil { | |||
panic(err) | |||
} | |||
for i = 0; i <= 10; i++ { | |||
_, err = createUser(userNames[i]) | |||
if err != nil { | |||
panic(err) | |||
} | |||
} | |||
} |
@ -1,9 +1,18 @@ | |||
package Models | |||
import "github.com/gofrs/uuid" | |||
import ( | |||
"time" | |||
"github.com/gofrs/uuid" | |||
) | |||
// Set with User being the requestee, and FriendID being the requester | |||
type Friend struct { | |||
Base | |||
UserID uuid.UUID `gorm:"type:uuid;column:user_id;not null;" json:"user_id"` | |||
FriendId string `gorm:"column:friend_id;not null" json:"friend_id"` // Stored encrypted | |||
UserID uuid.UUID `gorm:"type:uuid;column:user_id;not null;" json:"user_id"` | |||
User User `json:"user"` | |||
FriendID []byte `gorm:"not null" json:"friend_id"` // Stored encrypted | |||
ThreadID uuid.UUID `gorm:"type:uuid;column:thread_id;not null;" json:"thread_id"` | |||
Thread []Message `gorm:"foreignKey:thread_id" json:"-"` | |||
AcceptedAt time.Time `json:"accepted_at"` | |||
} |
@ -0,0 +1,21 @@ | |||
package Util | |||
import ( | |||
"bytes" | |||
"encoding/gob" | |||
) | |||
func ToBytes(key interface{}) ([]byte, error) { | |||
var ( | |||
buf bytes.Buffer | |||
enc *gob.Encoder | |||
err error | |||
) | |||
enc = gob.NewEncoder(&buf) | |||
err = enc.Encode(key) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return buf.Bytes(), nil | |||
} |
@ -0,0 +1,51 @@ | |||
package Util | |||
import ( | |||
"errors" | |||
"log" | |||
"net/http" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Database" | |||
"git.tovijaeschke.xyz/tovi/Envelope/Backend/Models" | |||
"github.com/gorilla/mux" | |||
) | |||
func GetUserId(r *http.Request) (string, error) { | |||
var ( | |||
urlVars map[string]string | |||
id string | |||
ok bool | |||
) | |||
urlVars = mux.Vars(r) | |||
id, ok = urlVars["userID"] | |||
if !ok { | |||
return id, errors.New("Could not get id") | |||
} | |||
return id, nil | |||
} | |||
func GetUserById(w http.ResponseWriter, r *http.Request) (Models.User, error) { | |||
var ( | |||
postData Models.User | |||
id string | |||
err error | |||
) | |||
id, err = GetUserId(r) | |||
if err != nil { | |||
log.Printf("Error encountered getting id\n") | |||
http.Error(w, "Error", http.StatusInternalServerError) | |||
return postData, err | |||
} | |||
postData, err = Database.GetUserById(id) | |||
if err != nil { | |||
log.Printf("Could not find user with id %s\n", id) | |||
http.Error(w, "Error", http.StatusInternalServerError) | |||
return postData, err | |||
} | |||
return postData, nil | |||
} |
@ -0,0 +1,30 @@ | |||
const messageTypeSender = 'sender'; | |||
const messageTypeReceiver = 'receiver'; | |||
class Message { | |||
String id; | |||
String conversationId; | |||
String symmetricKey; | |||
String data; | |||
String messageType; | |||
String? decryptedData; | |||
Message({ | |||
required this.id, | |||
required this.conversationId, | |||
required this.symmetricKey, | |||
required this.data, | |||
required this.messageType, | |||
this.decryptedData, | |||
}); | |||
} | |||
class Conversation { | |||
String id; | |||
String friendId; | |||
String recentMessageId; | |||
Conversation({ | |||
required this.id, | |||
required this.friendId, | |||
required this.recentMessageId, | |||
}); | |||
} |
@ -0,0 +1,162 @@ | |||
import 'package:flutter/material.dart'; | |||
import '/models/conversations.dart'; | |||
class ConversationDetail extends StatefulWidget{ | |||
const ConversationDetail({Key? key}) : super(key: key); | |||
@override | |||
_ConversationDetailState createState() => _ConversationDetailState(); | |||
} | |||
class _ConversationDetailState extends State<ConversationDetail> { | |||
Conversation conversation = Conversation( | |||
id: '777', | |||
friendId: 'abc', | |||
recentMessageId: '111', | |||
); | |||
List<Message> messages = [ | |||
Message( | |||
id: '444', | |||
conversationId: '777', | |||
symmetricKey: '', | |||
data: 'This is a message', | |||
messageType: messageTypeSender, | |||
), | |||
Message( | |||
id: '444', | |||
conversationId: '777', | |||
symmetricKey: '', | |||
data: 'This is a message', | |||
messageType: messageTypeReceiver, | |||
), | |||
Message( | |||
id: '444', | |||
conversationId: '777', | |||
symmetricKey: '', | |||
data: 'This is a message', | |||
messageType: messageTypeSender | |||
), | |||
Message( | |||
id: '444', | |||
conversationId: '777', | |||
symmetricKey: '', | |||
data: 'This is a message', | |||
messageType: messageTypeReceiver, | |||
), | |||
]; | |||
@override | |||
Widget build(BuildContext context) { | |||
return Scaffold( | |||
appBar: AppBar( | |||
elevation: 0, | |||
automaticallyImplyLeading: false, | |||
backgroundColor: Colors.white, | |||
flexibleSpace: SafeArea( | |||
child: Container( | |||
padding: const EdgeInsets.only(right: 16), | |||
child: Row( | |||
children: <Widget>[ | |||
IconButton( | |||
onPressed: (){ | |||
Navigator.pop(context); | |||
}, | |||
icon: const Icon(Icons.arrow_back,color: Colors.black,), | |||
), | |||
const SizedBox(width: 2,), | |||
Expanded( | |||
child: Column( | |||
crossAxisAlignment: CrossAxisAlignment.start, | |||
mainAxisAlignment: MainAxisAlignment.center, | |||
children: const <Widget>[ | |||
Text("Kriss Benwat",style: TextStyle( fontSize: 16 ,fontWeight: FontWeight.w600),), | |||
], | |||
), | |||
), | |||
const Icon(Icons.settings,color: Colors.black54,), | |||
], | |||
), | |||
), | |||
), | |||
), | |||
body: Stack( | |||
children: <Widget>[ | |||
ListView.builder( | |||
itemCount: messages.length, | |||
shrinkWrap: true, | |||
padding: const EdgeInsets.only(top: 10,bottom: 10), | |||
physics: const NeverScrollableScrollPhysics(), | |||
itemBuilder: (context, index) { | |||
return Container( | |||
padding: const EdgeInsets.only(left: 14,right: 14,top: 10,bottom: 10), | |||
child: Align( | |||
alignment: (messages[index].messageType == messageTypeReceiver ? Alignment.topLeft:Alignment.topRight), | |||
child: Container( | |||
decoration: BoxDecoration( | |||
borderRadius: BorderRadius.circular(20), | |||
color: (messages[index].messageType == messageTypeReceiver ? Colors.grey.shade200:Colors.blue[200]), | |||
), | |||
padding: const EdgeInsets.all(16), | |||
child: Text(messages[index].data, style: const TextStyle(fontSize: 15)), | |||
), | |||
), | |||
); | |||
}, | |||
), | |||
Align( | |||
alignment: Alignment.bottomLeft, | |||
child: ConstrainedBox( | |||
constraints: const BoxConstraints( | |||
maxHeight: 200.0, | |||
), | |||
child: Container( | |||
padding: const EdgeInsets.only(left: 10,bottom: 10,top: 10), | |||
// height: 60, | |||
width: double.infinity, | |||
color: Colors.white, | |||
child: Row( | |||
children: <Widget>[ | |||
GestureDetector( | |||
onTap: (){ | |||
}, | |||
child: Container( | |||
height: 30, | |||
width: 30, | |||
decoration: BoxDecoration( | |||
color: Colors.lightBlue, | |||
borderRadius: BorderRadius.circular(30), | |||
), | |||
child: const Icon(Icons.add, color: Colors.white, size: 20, ), | |||
), | |||
), | |||
const SizedBox(width: 15,), | |||
const Expanded( | |||
child: TextField( | |||
decoration: InputDecoration( | |||
hintText: "Write message...", | |||
hintStyle: TextStyle(color: Colors.black54), | |||
border: InputBorder.none, | |||
), | |||
maxLines: null, | |||
), | |||
), | |||
const SizedBox(width: 15,), | |||
FloatingActionButton( | |||
onPressed: () { | |||
}, | |||
child: const Icon(Icons.send,color: Colors.white,size: 18,), | |||
backgroundColor: Colors.blue, | |||
elevation: 0, | |||
), | |||
], | |||
), | |||
), | |||
), | |||
), | |||
], | |||
), | |||
); | |||
} | |||
} |
@ -0,0 +1,60 @@ | |||
import 'package:flutter/material.dart'; | |||
import '/views/main/conversation_detail.dart'; | |||
class ConversationListItem extends StatefulWidget{ | |||
final String id; | |||
final String username; | |||
const ConversationListItem({ | |||
Key? key, | |||
required this.id, | |||
required this.username, | |||
}) : super(key: key); | |||
@override | |||
_ConversationListItemState createState() => _ConversationListItemState(); | |||
} | |||
class _ConversationListItemState extends State<ConversationListItem> { | |||
@override | |||
Widget build(BuildContext context) { | |||
return GestureDetector( | |||
onTap: () { | |||
Navigator.push(context, MaterialPageRoute(builder: (context){ | |||
return ConversationDetail(); | |||
})); | |||
}, | |||
child: Container( | |||
padding: const EdgeInsets.only(left: 16,right: 16,top: 10,bottom: 10), | |||
child: Row( | |||
children: <Widget>[ | |||
Expanded( | |||
child: Row( | |||
children: <Widget>[ | |||
// CircleAvatar( | |||
// backgroundImage: NetworkImage(widget.imageUrl), | |||
// maxRadius: 30, | |||
// ), | |||
//const SizedBox(width: 16), | |||
Expanded( | |||
child: Container( | |||
color: Colors.transparent, | |||
child: Column( | |||
crossAxisAlignment: CrossAxisAlignment.start, | |||
children: <Widget>[ | |||
Text(widget.username, style: const TextStyle(fontSize: 16)), | |||
const SizedBox(height: 6), | |||
//Text(widget.messageText,style: TextStyle(fontSize: 13,color: Colors.grey.shade600, fontWeight: widget.isMessageRead?FontWeight.bold:FontWeight.normal),), | |||
const Divider(), | |||
], | |||
), | |||
), | |||
), | |||
], | |||
), | |||
), | |||
], | |||
), | |||
), | |||
); | |||
} | |||
} |
@ -0,0 +1,64 @@ | |||
import 'package:flutter/material.dart'; | |||
import 'package:shared_preferences/shared_preferences.dart'; | |||
import '/utils/storage/encryption_keys.dart'; | |||
class Profile extends StatefulWidget { | |||
const Profile({Key? key}) : super(key: key); | |||
@override | |||
State<Profile> createState() => _ProfileState(); | |||
} | |||
class _ProfileState extends State<Profile> { | |||
@override | |||
Widget build(BuildContext context) { | |||
return Scaffold( | |||
body: SingleChildScrollView( | |||
physics: const BouncingScrollPhysics(), | |||
child: Column( | |||
crossAxisAlignment: CrossAxisAlignment.start, | |||
children: <Widget>[ | |||
SafeArea( | |||
child: Padding( | |||
padding: const EdgeInsets.only(left: 16,right: 16,top: 10), | |||
child: Row( | |||
mainAxisAlignment: MainAxisAlignment.spaceBetween, | |||
children: <Widget>[ | |||
const Text("Profile",style: TextStyle(fontSize: 32,fontWeight: FontWeight.bold),), | |||
Container( | |||
padding: const EdgeInsets.only(left: 8,right: 8,top: 2,bottom: 2), | |||
height: 30, | |||
decoration: BoxDecoration( | |||
borderRadius: BorderRadius.circular(30), | |||
color: Colors.pink[50], | |||
), | |||
child: GestureDetector( | |||
onTap: () async { | |||
final preferences = await SharedPreferences.getInstance(); | |||
preferences.setBool('islogin', false); | |||
preferences.remove(rsaPrivateKeyName); | |||
Navigator.pushNamedAndRemoveUntil(context, '/landing', ModalRoute.withName('/landing')); | |||
}, | |||
child: Row( | |||
children: const <Widget>[ | |||
Icon(Icons.logout, color: Colors.pink, size: 20,), | |||
SizedBox(width: 2,), | |||
Text("Logout",style: TextStyle(fontSize: 14,fontWeight: FontWeight.bold),), | |||
], | |||
), | |||
), | |||
) | |||
], | |||
), | |||
), | |||
), | |||
const Padding( | |||
padding: EdgeInsets.only(top: 16,left: 16,right: 16), | |||
child: Text('Test'), | |||
), | |||
], | |||
), | |||
), | |||
); | |||
} | |||
} |