prepare to display diff hunks

This commit is contained in:
k0shk0sh 2019-09-19 23:03:44 +02:00
parent 2104cfac18
commit 30b44cbceb
21 changed files with 601 additions and 20 deletions

View File

@ -129,6 +129,7 @@ dependencies {
implementation project(':markdown')
implementation project(':editor')
implementation project(':commits')
implementation project(':diff')
implementation dependency.kotlin
implementation dependency.supportLibraries
implementation dependency.extrasLibraries

View File

@ -138,6 +138,10 @@
android:name="com.fastaccess.fasthub.commit.list.CommitsListActivity"
android:parentActivityName=".ui.modules.main.MainActivity" />
<activity
android:name="com.fastaccess.fasthub.diff.DiffViewerActivity"
android:parentActivityName=".ui.modules.main.MainActivity" />
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"

View File

@ -2,9 +2,10 @@ package com.fastaccess.github.di.modules
import com.fastaccess.fasthub.commit.list.CommitsListActivity
import com.fastaccess.fasthub.dagger.scopes.PerActivity
import com.fastaccess.github.ui.modules.auth.LoginChooserActivity
import com.fastaccess.github.editor.comment.CommentActivity
import com.fastaccess.fasthub.diff.DiffViewerActivity
import com.fastaccess.github.editor.EditorActivity
import com.fastaccess.github.editor.comment.CommentActivity
import com.fastaccess.github.ui.modules.auth.LoginChooserActivity
import com.fastaccess.github.ui.modules.issue.IssueActivity
import com.fastaccess.github.ui.modules.issuesprs.edit.EditIssuePrActivity
import com.fastaccess.github.ui.modules.main.MainActivity
@ -40,4 +41,5 @@ abstract class ActivityBindingModule {
@PerActivity @ContributesAndroidInjector abstract fun pullRequestActivity(): PullRequestActivity
@PerActivity @ContributesAndroidInjector abstract fun listReviewActivity(): ListReviewsActivity
@PerActivity @ContributesAndroidInjector abstract fun commitsListActivity(): CommitsListActivity
@PerActivity @ContributesAndroidInjector abstract fun diffViewerActivity(): DiffViewerActivity
}

View File

@ -10,13 +10,13 @@ import androidx.lifecycle.ViewModelProvider
import com.fastaccess.data.persistence.models.LoginModel
import com.fastaccess.domain.BuildConfig
import com.fastaccess.github.R
import com.fastaccess.github.base.deeplink.AppDeepLink
import com.fastaccess.github.base.extensions.*
import com.fastaccess.github.base.utils.LOGIN_PATH
import com.fastaccess.github.base.utils.REDIRECT_URL
import com.fastaccess.github.base.viewmodel.ViewModelProviders
import com.fastaccess.github.extensions.getColorAttr
import com.fastaccess.github.extensions.observeNotNull
import com.fastaccess.github.base.deeplink.AppDeepLink
import com.fastaccess.github.base.viewmodel.ViewModelProviders
import com.fastaccess.github.ui.modules.auth.callback.LoginChooserCallback
import com.fastaccess.github.ui.modules.auth.chooser.LoginChooserFragment
import com.fastaccess.github.ui.modules.auth.login.AuthLoginFragment

View File

@ -2,22 +2,21 @@ package com.fastaccess.github.ui.modules.auth.login
import android.content.Context
import android.os.Bundle
import android.view.Gravity
import android.view.View
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.transition.Slide
import com.fastaccess.data.model.FastHubErrors
import com.fastaccess.data.model.ValidationError.FieldType.*
import com.fastaccess.github.R
import com.fastaccess.github.base.BaseFragment
import com.fastaccess.github.base.extensions.asString
import com.fastaccess.github.base.extensions.beginDelayedTransition
import com.fastaccess.github.base.extensions.hideKeyboard
import com.fastaccess.github.base.utils.EXTRA
import com.fastaccess.github.base.utils.EXTRA_TWO
import com.fastaccess.github.extensions.isTrue
import com.fastaccess.github.base.viewmodel.ViewModelProviders
import com.fastaccess.github.extensions.isTrue
import com.fastaccess.github.ui.modules.auth.callback.LoginChooserCallback
import kotlinx.android.synthetic.main.login_form_layout.*
import javax.inject.Inject
@ -25,7 +24,7 @@ import javax.inject.Inject
/**
* Created by Kosh on 18.05.18.
*/
class AuthLoginFragment : com.fastaccess.github.base.BaseFragment() {
class AuthLoginFragment : BaseFragment() {
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
private var callback: LoginChooserCallback? = null
@ -68,11 +67,13 @@ class AuthLoginFragment : com.fastaccess.github.base.BaseFragment() {
loginBtn.setOnClickListener {
it.isEnabled = false
if (progressBar.isVisible) return@setOnClickListener
viewModel.login(usernameEditText.asString(),
viewModel.login(
usernameEditText.asString(),
passwordEditText.asString(),
twoFactorEditText.asString(),
endpointEditText.asString(),
(isAccessToken || accessTokenCheckbox.isVisible && accessTokenCheckbox.isChecked))
(isAccessToken || accessTokenCheckbox.isVisible && accessTokenCheckbox.isChecked)
)
}
passwordEditText.setOnEditorActionListener { v, _, _ ->
@ -123,7 +124,8 @@ class AuthLoginFragment : com.fastaccess.github.base.BaseFragment() {
viewModel.loggedInUser.observe(this, Observer { model ->
if (model != null) {
addDisposal(viewModel.clearDb()
.subscribe({ callback?.onUserLoggedIn(model) }, { throwable -> view?.let { showSnackBar(it, message = throwable.message) } }))
.subscribe({ callback?.onUserLoggedIn(model) }, { throwable -> view?.let { showSnackBar(it, message = throwable.message) } })
)
} else {
view?.let { showSnackBar(it, resId = R.string.failed_login) }
}
@ -134,10 +136,6 @@ class AuthLoginFragment : com.fastaccess.github.base.BaseFragment() {
const val TAG = "AuthLoginFragment"
fun newInstance(accessToken: Boolean = false, isEnterprise: Boolean = false): AuthLoginFragment = AuthLoginFragment()
.apply {
val enter = Slide(Gravity.END)
val exit = Slide(Gravity.START)
enterTransition = enter
exitTransition = exit
arguments = Bundle().apply {
putBoolean(EXTRA, accessToken)
putBoolean(EXTRA_TWO, isEnterprise)

View File

@ -5,7 +5,9 @@ configurations {
ktlint
}
buildscript {
buildscript {
ext.kotlin_version = '1.3.50'
ext {
kotlin = '1.3.50'
kotlin_version = '1.3.50'
@ -27,7 +29,7 @@ buildscript {
classpath 'com.apollographql.apollo:apollo-gradle-plugin:1.0.1'
classpath 'gradle.plugin.org.jlleitschuh.gradle:ktlint-gradle:2.3.0'
classpath "com.github.ben-manes:gradle-versions-plugin:0.21.0"
}
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" }
}
allprojects {
@ -47,4 +49,4 @@ task clean(type: Delete) {
subprojects {
apply plugin: "org.jlleitschuh.gradle.ktlint"
}
}

View File

@ -53,6 +53,7 @@ dependencies {
implementation project(':editor')
implementation project(':dagger')
implementation project(':reactions')
implementation project(':diff')
implementation dependency.kotlin
implementation dependency.supportLibraries
implementation dependency.extrasLibraries

View File

@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModelProvider
import com.fastaccess.data.model.FragmentType
import com.fastaccess.fasthub.commit.R
import com.fastaccess.fasthub.commit.adapter.CommitFilesAdapter
import com.fastaccess.fasthub.diff.DiffViewerActivity
import com.fastaccess.github.base.BaseFragment
import com.fastaccess.github.base.BaseViewModel
import com.fastaccess.github.base.extensions.isConnected
@ -27,7 +28,7 @@ class CommitFilesFragment : BaseFragment() {
private val viewModel by lazy { ViewModelProviders.of(this, viewModelFactory).get(CommitFilesViewModel::class.java) }
private val adapter by lazy {
CommitFilesAdapter { position, commitFilesModel ->
// TODO(open file)
DiffViewerActivity.startActivity(requireContext(), commitFilesModel.patch ?: "")
}
}

1
diff/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

64
diff/build.gradle Normal file
View File

@ -0,0 +1,64 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
android {
def conf = rootProject.ext.android
compileSdkVersion conf.compileSdkVersion
defaultConfig {
minSdkVersion conf.minSdkVersion
targetSdkVersion conf.targetSdkVersion
versionCode conf.versionCode
versionName conf.versionName
multiDexEnabled
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath = true
}
}
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
def dependency = rootProject.ext
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':base')
implementation project(':data')
implementation project(':domain')
implementation project(':resources')
implementation project(':extensions')
implementation project(':dagger')
implementation dependency.kotlin
implementation dependency.supportLibraries
implementation dependency.extrasLibraries
implementation dependency.networking
implementation dependency.rxJava
implementation dependency.extrasLibraries
implementation dependency.dagger
implementation dependency.archs
kapt dependency.processing
}

0
diff/consumer-rules.pro Normal file
View File

21
diff/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.fastaccess.fasthub.diff" />

View File

@ -0,0 +1,10 @@
function loadDiff(diff) {
var diffHtml = Diff2Html.getPrettyHtml(diff, {
inputFormat: 'diff',
showFiles: true,
matching: 'lines',
outputFormat: 'line-by-line'
});
console.log(diffHtml);
document.getElementById("diff").innerHTML = diffHtml;
}

View File

@ -0,0 +1,365 @@
/*
*
* Diff to HTML (diff2html.css)
* Author: rtfpessoa
*
*/
.d2h-wrapper {
text-align: left;
}
.d2h-file-header {
height: 35px;
padding: 5px 10px;
border-bottom: 1px solid #d8d8d8;
background-color: #f7f7f7;
}
.d2h-file-stats {
display: flex;
margin-left: auto;
font-size: 14px;
}
.d2h-lines-added {
text-align: right;
border: 1px solid #b4e2b4;
border-radius: 5px 0 0 5px;
color: #399839;
padding: 2px;
vertical-align: middle;
}
.d2h-lines-deleted {
text-align: left;
border: 1px solid #e9aeae;
border-radius: 0 5px 5px 0;
color: #c33;
padding: 2px;
vertical-align: middle;
margin-left: 1px;
}
.d2h-file-name-wrapper {
display: flex;
align-items: center;
width: 100%;
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 15px;
}
.d2h-file-name {
white-space: nowrap;
text-overflow: ellipsis;
overflow-x: hidden;
}
.d2h-file-wrapper {
border: 1px solid #ddd;
border-radius: 3px;
margin-bottom: 1em;
}
.d2h-diff-table {
width: 100%;
border-collapse: collapse;
font-family: "Menlo", "Consolas", monospace;
font-size: 13px;
}
.d2h-files-diff {
display: block;
width: 100%;
height: 100%;
}
.d2h-file-diff {
overflow-y: hidden;
}
.d2h-file-side-diff {
display: inline-block;
overflow-x: scroll;
overflow-y: hidden;
width: 50%;
margin-right: -4px;
margin-bottom: -8px;
}
.d2h-code-line {
display: inline-block;
white-space: nowrap;
/* Compensate for the absolute positioning of the line numbers */
padding: 0 8em;
}
.d2h-code-side-line {
display: inline-block;
white-space: nowrap;
/* Compensate for the absolute positioning of the line numbers */
padding: 0 4.5em;
}
.d2h-code-line del,
.d2h-code-side-line del {
display: inline-block;
margin-top: -1px;
text-decoration: none;
background-color: #ffb6ba;
border-radius: 0.2em;
}
.d2h-code-line ins,
.d2h-code-side-line ins {
display: inline-block;
margin-top: -1px;
text-decoration: none;
background-color: #97f295;
border-radius: 0.2em;
text-align: left;
}
.d2h-code-line-prefix {
display: inline;
background: none;
padding: 0;
word-wrap: normal;
white-space: pre;
}
.d2h-code-line-ctn {
display: inline;
background: none;
padding: 0;
word-wrap: normal;
white-space: pre;
}
.line-num1 {
box-sizing: border-box;
float: left;
width: 3.5em;
overflow: hidden;
text-overflow: ellipsis;
padding: 0 0.5em 0 0.5em;
}
.line-num2 {
box-sizing: border-box;
float: right;
width: 3.5em;
overflow: hidden;
text-overflow: ellipsis;
padding: 0 0.5em 0 0.5em;
}
.d2h-code-linenumber {
box-sizing: border-box;
width: 7.5em;
/* Keep the numbers fixed on line contents scroll */
position: absolute;
display: inline-block;
background-color: #fff;
color: rgba(0, 0, 0, 0.3);
text-align: right;
border: solid #eeeeee;
border-width: 0 1px 0 1px;
cursor: pointer;
}
.d2h-code-linenumber:after {
content: '\200b';
}
.d2h-code-side-linenumber {
/* Keep the numbers fixed on line contents scroll */
position: absolute;
display: inline-block;
box-sizing: border-box;
width: 4em;
background-color: #fff;
color: rgba(0, 0, 0, 0.3);
text-align: right;
border: solid #eeeeee;
border-width: 0 1px 0 1px;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
}
.d2h-code-side-linenumber:after {
content: '\200b';
}
.d2h-code-side-emptyplaceholder,
.d2h-emptyplaceholder {
background-color: #f1f1f1;
border-color: #e1e1e1;
}
/*
* Changes Highlight
*/
.d2h-del {
background-color: #fee8e9;
border-color: #e9aeae;
}
.d2h-ins {
background-color: #dfd;
border-color: #b4e2b4;
}
.d2h-info {
background-color: #f8fafd;
color: rgba(0, 0, 0, 0.3);
border-color: #d5e4f2;
}
.d2h-file-diff .d2h-del.d2h-change {
background-color: #fdf2d0;
}
.d2h-file-diff .d2h-ins.d2h-change {
background-color: #ded;
}
/*
* File Summary List
*/
.d2h-file-list-wrapper {
margin-bottom: 10px;
}
.d2h-file-list-wrapper a {
text-decoration: none;
color: #3572b0;
}
.d2h-file-list-wrapper a:visited {
color: #3572b0;
}
.d2h-file-list-header {
text-align: left;
}
.d2h-file-list-title {
font-weight: bold;
}
.d2h-file-list-line {
display: flex;
text-align: left;
}
.d2h-file-list {
display: block;
list-style: none;
padding: 0;
margin: 0;
}
.d2h-file-list > li {
border-bottom: #ddd solid 1px;
padding: 5px 10px;
margin: 0;
}
.d2h-file-list > li:last-child {
border-bottom: none;
}
.d2h-file-switch {
display: none;
font-size: 10px;
cursor: pointer;
}
.d2h-icon {
vertical-align: middle;
margin-right: 10px;
fill: currentColor;
}
.d2h-deleted {
color: #c33;
}
.d2h-added {
color: #399839;
}
.d2h-changed {
color: #d0b44c;
}
.d2h-moved {
color: #3572b0;
}
.d2h-tag {
display: flex;
font-size: 10px;
margin-left: 5px;
padding: 0 2px;
background-color: #fff;
}
.d2h-deleted-tag {
border: #c33 1px solid;
}
.d2h-added-tag {
border: #399839 1px solid;
}
.d2h-changed-tag {
border: #d0b44c 1px solid;
}
.d2h-moved-tag {
border: #3572b0 1px solid;
}
/*
* Selection util.
*/
.selecting-left .d2h-code-line,
.selecting-left .d2h-code-line *,
.selecting-right td.d2h-code-linenumber,
.selecting-right td.d2h-code-linenumber *,
.selecting-left .d2h-code-side-line,
.selecting-left .d2h-code-side-line *,
.selecting-right td.d2h-code-side-linenumber,
.selecting-right td.d2h-code-side-linenumber * {
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.selecting-left .d2h-code-line::-moz-selection,
.selecting-left .d2h-code-line *::-moz-selection,
.selecting-right td.d2h-code-linenumber::-moz-selection,
.selecting-left .d2h-code-side-line::-moz-selection,
.selecting-left .d2h-code-side-line *::-moz-selection,
.selecting-right td.d2h-code-side-linenumber::-moz-selection,
.selecting-right td.d2h-code-side-linenumber *::-moz-selection {
background: transparent;
}
.selecting-left .d2h-code-line::selection,
.selecting-left .d2h-code-line *::selection,
.selecting-right td.d2h-code-linenumber::selection,
.selecting-left .d2h-code-side-line::selection,
.selecting-left .d2h-code-side-line *::selection,
.selecting-right td.d2h-code-side-linenumber::selection,
.selecting-right td.d2h-code-side-linenumber *::selection {
background: transparent;
}

1
diff/src/main/assets/diff2html.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,13 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no"/>
<link rel="stylesheet" type="text/css" href="diff2html.css"/>
<script src="diff2html.min.js"></script>
<script src="diff.js"></script>
</head>
<body id="diff">
</body>
</html>

View File

@ -0,0 +1,37 @@
package com.fastaccess.fasthub.diff
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import com.fastaccess.fasthub.dagger.annotations.ForActivity
import com.fastaccess.github.base.BaseActivity
import com.fastaccess.github.base.utils.EXTRA
import kotlinx.android.synthetic.main.diff_patch_viewer_layout.*
class DiffViewerActivity : BaseActivity() {
override fun layoutRes(): Int = R.layout.diff_patch_viewer_layout
override fun onActivityCreatedWithUser(savedInstanceState: Bundle?) {
title = ""
findViewById<Toolbar>(R.id.toolbar).apply {
title = ""
findViewById<TextView>(R.id.toolbarTitle).text = getString(R.string.commit)
setNavigationIcon(R.drawable.ic_clear)
setSupportActionBar(this)
}
intent?.getStringExtra(EXTRA)?.let {
webview.loadDiff(it)
} ?: run { finish() }
}
companion object {
fun startActivity(@ForActivity context: Context, patch: String) {
context.startActivity(Intent(context, DiffViewerActivity::class.java).apply {
putExtra(EXTRA, patch)
})
}
}
}

View File

@ -0,0 +1,35 @@
package com.fastaccess.fasthub.diff
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.webkit.WebChromeClient
import android.webkit.WebView
import androidx.webkit.WebViewClientCompat
import timber.log.Timber
class DiffWebView : WebView {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
@SuppressLint("SetJavaScriptEnabled")
fun loadDiff(diff: String) {
settings.apply {
javaScriptEnabled = true
defaultTextEncodingName = "utf-8"
webChromeClient = WebChromeClient()
}
post {
webViewClient = object : WebViewClientCompat() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
Timber.e("here!")
loadUrl("javascript:loadDiff('$diff')")
}
}
loadUrl("file:///android_asset/index.html")
}
}
}

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/appbar_center_title_layout" />
<ProgressBar
android:id="@+id/progressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="gone" />
<com.fastaccess.fasthub.diff.DiffWebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@ -1,4 +1,4 @@
include ':app', ':data', ':domain', ':resources', ':extensions', ':markdown', ':editor'
include ':app', ':data', ':domain', ':resources', ':extensions', ':markdown', ':editor', ':diff'
include ':commits'
include ':base'
include ':dagger'