Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Attachments #81

Merged
merged 2 commits into from
Sep 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 52 additions & 2 deletions app/schemas/tech.relaycorp.letro.storage.LetroDatabase/1.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "1c40ad8823e534038e8cc251f7b87ade",
"identityHash": "e714cb3de1eddf34851bc19c7da0dd6f",
"entities": [
{
"tableName": "account",
Expand Down Expand Up @@ -361,12 +361,62 @@
]
}
]
},
{
"tableName": "attachments",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `fileId` BLOB NOT NULL, `path` TEXT NOT NULL, `messageId` INTEGER NOT NULL, FOREIGN KEY(`messageId`) REFERENCES `messages`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "fileId",
"columnName": "fileId",
"affinity": "BLOB",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "messageId",
"columnName": "messageId",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": [
{
"table": "messages",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"messageId"
],
"referencedColumns": [
"id"
]
}
]
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1c40ad8823e534038e8cc251f7b87ade')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e714cb3de1eddf34851bc19c7da0dd6f')"
]
}
}
11 changes: 11 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="tech.relaycorp.letro.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths">
</meta-data>
</provider>
</application>

</manifest>
21 changes: 21 additions & 0 deletions app/src/main/java/tech/relaycorp/letro/di/AndroidModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package tech.relaycorp.letro.di

import android.content.ContentResolver
import android.content.Context
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent

@Module
@InstallIn(SingletonComponent::class)
object AndroidModule {

@Provides
fun provideContentResolver(
@ApplicationContext context: Context,
): ContentResolver {
return context.contentResolver
}
}
14 changes: 14 additions & 0 deletions app/src/main/java/tech/relaycorp/letro/di/ConversationsModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import tech.relaycorp.letro.messages.attachments.AttachmentsRepository
import tech.relaycorp.letro.messages.attachments.AttachmentsRepositoryImpl
import tech.relaycorp.letro.messages.converter.ExtendedConversationConverter
import tech.relaycorp.letro.messages.converter.ExtendedConversationConverterImpl
import tech.relaycorp.letro.messages.converter.MessageTimestampFormatter
Expand All @@ -23,6 +25,7 @@ import tech.relaycorp.letro.messages.processor.NewMessageProcessor
import tech.relaycorp.letro.messages.processor.NewMessageProcessorImpl
import tech.relaycorp.letro.messages.repository.ConversationsRepository
import tech.relaycorp.letro.messages.repository.ConversationsRepositoryImpl
import tech.relaycorp.letro.messages.storage.AttachmentsDao
import tech.relaycorp.letro.messages.storage.ConversationsDao
import tech.relaycorp.letro.messages.storage.MessagesDao
import tech.relaycorp.letro.storage.LetroDatabase
Expand All @@ -42,6 +45,11 @@ object ConversationsModule {
letroDatabase: LetroDatabase,
): MessagesDao = letroDatabase.messagesDao()

@Provides
fun provideAttachmentsDao(
letroDatabase: LetroDatabase,
): AttachmentsDao = letroDatabase.attachmentsDao()

@Module
@InstallIn(SingletonComponent::class)
interface Bindings {
Expand Down Expand Up @@ -91,5 +99,11 @@ object ConversationsModule {
fun bindOnboardingMessageManager(
impl: ConversationsOnboardingManagerImpl,
): ConversationsOnboardingManager

@Binds
@Singleton
fun bindAttachmentsRepository(
impl: AttachmentsRepositoryImpl,
): AttachmentsRepository
}
}
31 changes: 31 additions & 0 deletions app/src/main/java/tech/relaycorp/letro/di/FileModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package tech.relaycorp.letro.di

import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import tech.relaycorp.letro.messages.filepicker.FileConverter
import tech.relaycorp.letro.messages.filepicker.FileConverterImpl
import tech.relaycorp.letro.messages.filepicker.FileSaver
import tech.relaycorp.letro.messages.filepicker.FileSaverImpl
import tech.relaycorp.letro.messages.ui.utils.AttachmentInfoConverter
import tech.relaycorp.letro.messages.ui.utils.AttachmentInfoConverterImpl

@Module
@InstallIn(SingletonComponent::class)
interface FileModule {
@Binds
fun bindFileConverter(
impl: FileConverterImpl,
): FileConverter

@Binds
fun bindAttachmentInfoConverter(
impl: AttachmentInfoConverterImpl,
): AttachmentInfoConverter

@Binds
fun bindFileSaver(
impl: FileSaverImpl,
): FileSaver
}
23 changes: 23 additions & 0 deletions app/src/main/java/tech/relaycorp/letro/main/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,23 @@ import tech.relaycorp.letro.account.storage.AccountRepository
import tech.relaycorp.letro.awala.AwalaInitializationState
import tech.relaycorp.letro.awala.AwalaManager
import tech.relaycorp.letro.contacts.storage.ContactsRepository
import tech.relaycorp.letro.messages.attachments.AttachmentsRepository
import tech.relaycorp.letro.messages.filepicker.FileConverter
import tech.relaycorp.letro.messages.filepicker.model.File
import tech.relaycorp.letro.push.model.PushAction
import tech.relaycorp.letro.ui.navigation.RootNavigationScreen
import tech.relaycorp.letro.utils.ext.emitOn
import tech.relaycorp.letro.utils.ext.sendOn
import java.util.UUID
import javax.inject.Inject

@HiltViewModel
class MainViewModel @Inject constructor(
private val awalaManager: AwalaManager,
private val accountRepository: AccountRepository,
private val contactsRepository: ContactsRepository,
private val attachmentsRepository: AttachmentsRepository,
private val fileConverter: FileConverter,
) : ViewModel() {

private val _uiState = MutableStateFlow(MainUiState())
Expand All @@ -45,6 +52,10 @@ class MainViewModel @Inject constructor(
val joinMeOnLetroSignal: SharedFlow<String>
get() = _joinMeOnLetroSignal

private val _openFileSignal = MutableSharedFlow<File.FileWithoutContent>()
val openFileSignal: SharedFlow<File.FileWithoutContent>
get() = _openFileSignal

private val _rootNavigationScreen: MutableStateFlow<RootNavigationScreen> =
MutableStateFlow(RootNavigationScreen.Splash)
val rootNavigationScreen: StateFlow<RootNavigationScreen> get() = _rootNavigationScreen
Expand Down Expand Up @@ -118,6 +129,18 @@ class MainViewModel @Inject constructor(
}
}

fun onAttachmentClick(fileId: UUID) {
viewModelScope.launch {
attachmentsRepository.getById(fileId)?.let { attachment ->
fileConverter.getFile(attachment)?.let { file ->
if (file.exists()) {
_openFileSignal.emitOn(file, viewModelScope)
}
}
}
}
}

private fun getJoinMeLink(accountId: String) = "$JOIN_ME_ON_LETRO_COMMON_PART_OF_LINK$accountId"

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package tech.relaycorp.letro.messages.attachments

import kotlinx.coroutines.flow.Flow
import tech.relaycorp.letro.messages.filepicker.FileSaver
import tech.relaycorp.letro.messages.filepicker.model.File
import tech.relaycorp.letro.messages.storage.AttachmentsDao
import tech.relaycorp.letro.messages.storage.entity.Attachment
import java.util.UUID
import javax.inject.Inject

interface AttachmentsRepository {
val attachments: Flow<List<Attachment>>
suspend fun saveAttachments(messageId: Long, attachments: List<File.FileWithContent>)
suspend fun getById(id: UUID): Attachment?
}

class AttachmentsRepositoryImpl @Inject constructor(
private val attachmentsDao: AttachmentsDao,
private val fileSaver: FileSaver,
) : AttachmentsRepository {

override val attachments: Flow<List<Attachment>>
get() = attachmentsDao.getAll()

override suspend fun saveAttachments(messageId: Long, attachments: List<File.FileWithContent>) {
attachmentsDao.insert(
attachments
.map { file ->
val path = fileSaver.save(file)
Attachment(
fileId = file.id,
path = path,
messageId = messageId,
)
},
)
}

override suspend fun getById(id: UUID): Attachment? {
return attachmentsDao.getById(id)
}
}
Loading