Skip to content
Open
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ hs_err_pid*
.externalNativeBuild
fastlane/.env.staging
fastlane/.env.production

# Kotlin compiler cache
.kotlin/
14 changes: 14 additions & 0 deletions .idea/appInsightsSettings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/deploymentTargetSelector.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 14 additions & 3 deletions app/src/main/java/org/permanent/permanent/models/AccessRole.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.permanent.permanent.models
import android.os.Parcel
import android.os.Parcelable
import org.permanent.permanent.ArchivePermission
import java.util.*
import java.util.Locale

enum class AccessRole(val backendString: String) : Parcelable {
OWNER("access.role.owner") {
Expand Down Expand Up @@ -113,7 +113,7 @@ enum class AccessRole(val backendString: String) : Parcelable {
fun toTitleCase(): String = this.name.lowercase(Locale.getDefault())
.replaceFirstChar { it.titlecase(Locale.getDefault()) }

fun toLowerCase(): String = this.name.lowercase(Locale.getDefault())
fun lowerCase(): String = this.name.lowercase(Locale.getDefault())

fun getInferior(otherAccessRole: AccessRole): AccessRole {
return if (otherAccessRole in inferiors()) otherAccessRole else this
Expand Down Expand Up @@ -153,7 +153,7 @@ enum class AccessRole(val backendString: String) : Parcelable {
return values()[parcel.readInt()]
}

fun createFromBackendString(accessRoleString: String?): AccessRole {
fun fromBackendValue(accessRoleString: String?): AccessRole {
return when (accessRoleString) {
OWNER.backendString -> OWNER
MANAGER.backendString -> MANAGER
Expand All @@ -164,6 +164,17 @@ enum class AccessRole(val backendString: String) : Parcelable {
}
}

fun fromStelaBackendValue(accessRoleString: String?): AccessRole {
return when (accessRoleString) {
OWNER.lowerCase() -> OWNER
MANAGER.lowerCase() -> MANAGER
CURATOR.lowerCase() -> CURATOR
EDITOR.lowerCase() -> EDITOR
CONTRIBUTOR.lowerCase() -> CONTRIBUTOR
else -> VIEWER
}
}

override fun newArray(size: Int): Array<AccessRole?> {
return arrayOfNulls(size)
}
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/java/org/permanent/permanent/models/Record.kt
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ open class Record : Parcelable {
isThumbBlurred = false
type = if (recordInfo.folderId != null) RecordType.FOLDER else RecordType.FILE
size = recordInfo.size ?: -1L
accessRole = AccessRole.createFromBackendString(recordInfo.accessRole)
accessRole = AccessRole.fromBackendValue(recordInfo.accessRole)
initShares(recordInfo.ShareVOs)
displayFirstInCarousel = false
val thumbStatus = ThumbStatus.createFromBackendString(recordInfo.thumbStatus)
Expand All @@ -109,7 +109,7 @@ open class Record : Parcelable {
thumbURL2000 = recordInfo.thumbURL2000
isThumbBlurred = false
type = RecordType.FOLDER
accessRole = AccessRole.createFromBackendString(recordInfo.accessRole)
accessRole = AccessRole.fromBackendValue(recordInfo.accessRole)
initShares(recordInfo.ShareVOs)
displayFirstInCarousel = false
val thumbStatus = ThumbStatus.createFromBackendString(recordInfo.thumbStatus)
Expand Down Expand Up @@ -138,7 +138,7 @@ open class Record : Parcelable {
thumbURL2000 = itemVO.thumbURL2000
isThumbBlurred = false
type = if (itemVO.folderId != null) RecordType.FOLDER else RecordType.FILE
accessRole = AccessRole.createFromBackendString(itemVO.accessRole)
accessRole = AccessRole.fromBackendValue(itemVO.accessRole)
initShares(itemVO.ShareVOs)
displayFirstInCarousel = false
isProcessing = itemVO.thumbURL200.isNullOrEmpty()
Expand Down
24 changes: 22 additions & 2 deletions app/src/main/java/org/permanent/permanent/network/NetworkClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.ResponseBody
import okhttp3.logging.HttpLoggingInterceptor
import org.json.JSONObject
import org.permanent.permanent.BuildConfig
import org.permanent.permanent.Constants
import org.permanent.permanent.PermanentApplication
Expand Down Expand Up @@ -58,7 +59,6 @@ import org.permanent.permanent.ui.myFiles.upload.CountingRequestListener
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.Query
import java.io.File
import java.util.Date

Expand Down Expand Up @@ -788,10 +788,30 @@ class NetworkClient(private var okHttpClient: OkHttpClient?, context: Context) {

fun disableTwoFactor(twoFAVO: TwoFAVO): Call<ResponseBody> = stelaAccountService.disableTwoFactor(twoFAVO)

fun getShareLink(shareTokens: List<String>?,shareLinkIds: List<String>?): Call<ShareLinkVOResponse> = stelaAccountService.getShareLink(shareTokens,shareLinkIds)
fun getShareLink(shareLinkIds: List<Int>): Call<ShareLinkVOResponse> = stelaAccountService.getShareLink(shareLinkIds)

fun generateShareLink(shareLink: ShareLinkVO): Call<ShareLinkResponse> = stelaAccountService.generateShareLink(shareLink)

fun updateShareLink(shareLink: ShareLinkVO): Call<ResponseVO> {
val json = JSONObject()

shareLink.permissionsLevel?.let { json.put("permissionsLevel", it) }
shareLink.accessRestrictions?.let { json.put("accessRestrictions", it) }
if (shareLink.expirationTimestamp == null) {
json.put("expirationTimestamp", JSONObject.NULL)
} else {
json.put("expirationTimestamp", shareLink.expirationTimestamp)
}
val body = json.toString()
.toRequestBody("application/json; charset=utf-8".toMediaType())

return stelaAccountService.updateShareLink(shareLink.id, body)
}

fun deleteShareLink(shareLinkId: String): Call<ResponseVO> {
return stelaAccountService.deleteShareLink(shareLinkId)
}

fun getPaymentIntent(
accountId: Int,
accountEmail: String?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,28 @@ import org.permanent.permanent.network.models.ShareLinkVOResponse
import org.permanent.permanent.network.models.TwoFAVO
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.PATCH
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
import retrofit2.http.Query

interface StelaAccountService {

@GET("api/v2/share-links")
fun getShareLink(@Query("shareTokens[]") shareTokens: List<String>?,
@Query("shareLinkIds[]") shareLinkIds: List<String>?
): Call<ShareLinkVOResponse>
fun getShareLink(@Query("shareLinkIds[]") shareLinkIds: List<Int>): Call<ShareLinkVOResponse>

@POST("api/v2/share-links")
fun generateShareLink(@Body shareLink: ShareLinkVO): Call<ShareLinkResponse>

@PATCH("api/v2/share-links/{shareLinkId}")
fun updateShareLink(@Path("shareLinkId") shareLinkId: String?, @Body body: RequestBody): Call<ResponseVO>

@DELETE("api/v2/share-links/{shareLinkId}")
fun deleteShareLink(@Path("shareLinkId") shareLinkId: String?): Call<ResponseVO>

@PUT("api/v2/account/tags")
fun addRemoveTags(@Body tags: Tags): Call<ResponseVO>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ class Datum {
var TagVO: TagVO? = null
var TagLinkVO: TagLinkVO? = null
var PromoVO: PromoVO? = null
var ShareLinkVO: ShareLinkVO? = null

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class FileData private constructor() : Parcelable {
folderLinkId = recordVO.folder_linkId ?: -1
archiveId = recordVO.archiveId ?: -1
archiveNr = recordVO.archiveNbr
accessRole = AccessRole.createFromBackendString(recordVO.accessRole)
accessRole = AccessRole.fromBackendValue(recordVO.accessRole)
val fileVOs = recordVO.FileVOs ?: emptyList()

val fileVO: FileVO? = when {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,8 @@ class ResponseVO {
fun getPromoVO(): PromoVO? {
return getData()?.get(0)?.PromoVO
}

fun getShareLinkVO(): ShareLinkVO? {
return getData()?.get(0)?.ShareLinkVO
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@ data class ShareLinkVOResponse(
data class ShareLinkResponse(
val data: ShareLinkVO
)
class ShareLinkVO {
var id: String? = null
var itemId: String? = null
var itemType: String? = null
var permissionsLevel: String? = null
var accessRestrictions: String? = null
data class ShareLinkVO (
var id: String? = null,
var itemId: String? = null,
var itemType: String? = null,
var permissionsLevel: String? = null,
var accessRestrictions: String? = null,

var token: String? = null
var token: String? = null,

var maxUses: Int? = null // can be 0 for unlimited uses
var maxUses: Int? = null, // can be 0 for unlimited uses

var usesExpended: Int? = null
var expirationTimestamp: String? = null // can be null for no expiration
var usesExpended: Int? = null,
var expirationTimestamp: String? = null, // can be null for no expiration, or this format: "2026-11-17 13:56:31"

var createdAt: String? = null
var updatedAt: String? = null
}
var createdAt: String? = null,
var updatedAt: String? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ interface StelaAccountRepository {

fun disableTwoFactor(twoFAVO: TwoFAVO, listener: IResponseListener)

fun getShareLink(shareTokens: List<String>?,shareLinkIds: List<String>?, listener: IResponseListener)
fun getShareLink(shareLinkIds: List<Int>, listener: ILinkListener)

fun generateShareLink(shareLinkVO: ShareLinkVO, listener: ILinkListener)

fun updateShareLink(shareLinkVO: ShareLinkVO, listener: IResponseListener)

fun deleteShareLink(shareLinkId: String, listener: IResponseListener)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import org.permanent.permanent.network.ITwoFAListener
import org.permanent.permanent.network.NetworkClient
import org.permanent.permanent.network.models.ErrorResponse
import org.permanent.permanent.network.models.ResponseVO
import org.permanent.permanent.network.models.ShareLinkResponse
import org.permanent.permanent.network.models.ShareLinkVO
import org.permanent.permanent.network.models.ShareLinkVOResponse
import org.permanent.permanent.network.models.ShareLinkResponse
import org.permanent.permanent.network.models.TwoFAVO
import retrofit2.Call
import retrofit2.Callback
Expand Down Expand Up @@ -197,14 +197,14 @@ class StelaAccountRepositoryImpl(context: Context) : StelaAccountRepository {
})
}

override fun getShareLink(shareTokens: List<String>?,shareLinkIds: List<String>?, listener: IResponseListener) {
NetworkClient.instance().getShareLink(shareTokens, shareLinkIds).enqueue( object : Callback<ShareLinkVOResponse> {
override fun getShareLink(shareLinkIds: List<Int>, listener: ILinkListener) {
NetworkClient.instance().getShareLink(shareLinkIds).enqueue( object : Callback<ShareLinkVOResponse> {

override fun onResponse(call: Call<ShareLinkVOResponse>, response: Response<ShareLinkVOResponse>) {
if (response.isSuccessful) {
val responseVO = response.body()
if (responseVO != null) {
listener.onSuccess("")
listener.onSuccess(responseVO.items[0])
} else {
listener.onFailed(appContext.getString(R.string.generic_error))
}
Expand Down Expand Up @@ -240,4 +240,53 @@ class StelaAccountRepositoryImpl(context: Context) : StelaAccountRepository {
}
})
}

override fun updateShareLink(shareLinkVO: ShareLinkVO, listener: IResponseListener) {
NetworkClient.instance().updateShareLink(shareLinkVO)
.enqueue(object : Callback<ResponseVO> {

override fun onResponse(call: Call<ResponseVO>, response: Response<ResponseVO>) {
if (response.isSuccessful) {
val responseVO = response.body()
if (responseVO != null) {
listener.onSuccess("")
} else {
listener.onFailed(appContext.getString(R.string.generic_error))
}
} else {
try {
listener.onFailed(response.errorBody().toString())
} catch (e: Exception) {
listener.onFailed(e.message)
}
}
}

override fun onFailure(call: Call<ResponseVO>, t: Throwable) {
listener.onFailed(t.message)
}
})
}

override fun deleteShareLink(shareLinkId: String, listener: IResponseListener) {
NetworkClient.instance().deleteShareLink(shareLinkId)
.enqueue(object : Callback<ResponseVO> {

override fun onResponse(call: Call<ResponseVO>, response: Response<ResponseVO>) {
if (response.isSuccessful) {
listener.onSuccess("")
} else {
try {
listener.onFailed(response.errorBody().toString())
} catch (e: Exception) {
listener.onFailed(e.message)
}
}
}

override fun onFailure(call: Call<ResponseVO>, t: Throwable) {
listener.onFailed(t.message)
}
})
}
}
24 changes: 24 additions & 0 deletions app/src/main/java/org/permanent/permanent/ui/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStream
import java.text.SimpleDateFormat
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.util.Date
import java.util.Locale
Expand Down Expand Up @@ -117,6 +121,26 @@ fun bytesToCustomHumanReadableString(bytes: Long, showDecimal: Boolean): String
}.toString()
}

private val BACKEND_DATE_TIME_FORMATTER: DateTimeFormatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")

/**
* Convert a LocalDate to a backend datetime string.
*
* @param useEndOfDay if true, result will be at 23:59:59 of the date (default = true).
* if false, result will be at 00:00:00 of the date.
*/
fun LocalDate.toBackendDateTimeString(useEndOfDay: Boolean = true): String? {
val time = if (useEndOfDay) LocalTime.of(23, 59, 59) else LocalTime.MIDNIGHT
val dt = LocalDateTime.of(this, time)
return dt.format(BACKEND_DATE_TIME_FORMATTER)
}

fun String.toLocalDateUtc(): LocalDate =
Instant.parse(this)
.atZone(ZoneOffset.UTC)
.toLocalDate()

fun String?.toDisplayDate(): String {
if (this.isNullOrBlank()) return ""
return try {
Expand Down
Loading