1
1
import { Notice , Plugin , PluginSettingTab , Setting } from "obsidian" ;
2
2
import simpleGit , { CheckRepoActions , SimpleGit } from "simple-git" ;
3
+ import { FileStatusResult } from "simple-git/typings/response" ;
3
4
4
5
enum PluginState {
5
6
idle ,
6
- checkRepoClean ,
7
+ status ,
7
8
pull ,
8
9
add ,
9
10
commit ,
@@ -21,20 +22,24 @@ export default class ObsidianGit extends Plugin {
21
22
22
23
setState ( state : PluginState ) {
23
24
this . state = state ;
24
- this . statusBar . display ( this ) ;
25
- this . updateStatusBar ( ) ;
25
+ this . refreshStatusBar ( ) ;
26
26
}
27
27
28
28
getState ( ) : PluginState {
29
29
return this . state ;
30
30
}
31
31
32
32
async onload ( ) {
33
+ let statusBarEl = this . addStatusBarItem ( ) ;
34
+ this . statusBar = new StatusBar ( statusBarEl ) ;
35
+ this . setState ( PluginState . status ) ;
36
+
33
37
const adapter : any = this . app . vault . adapter ;
34
38
const git = simpleGit ( adapter . basePath ) ;
39
+
35
40
let isValidRepo = git . checkIsRepo ( CheckRepoActions . IS_REPO_ROOT ) ;
36
41
if ( ! isValidRepo ) {
37
- new Notice ( "Valid git repository not found." ) ;
42
+ this . displayMessage ( "Valid git repository not found." , 0 ) ;
38
43
return ;
39
44
}
40
45
@@ -49,34 +54,27 @@ export default class ObsidianGit extends Plugin {
49
54
if ( typeof remote === "string" ) {
50
55
this . settings . remote = remote . trim ( ) ;
51
56
} else {
52
- new Notice ( "Failed to detect remote." ) ;
57
+ this . displayMessage ( "Failed to detect remote." , 0 ) ;
53
58
return ;
54
59
}
55
60
56
- let statusBarEl = this . addStatusBarItem ( ) ;
57
- this . statusBar = new StatusBar ( statusBarEl ) ;
58
- this . statusBar . displayIdle ( ) ;
59
- this . setState ( PluginState . idle ) ;
60
-
61
61
if ( this . settings . autoPullOnBoot ) {
62
- setTimeout (
63
- async ( ) =>
64
- await this . pull ( ) . then ( ( filesUpdated ) => {
65
- this . setState ( PluginState . idle ) ;
66
- this . maybeNotice (
67
- `Pulled new changes. ${ filesUpdated } files updated.`
68
- ) ;
69
- } ) ,
70
- 700
71
- ) ;
62
+ await this . pull ( ) . then ( ( filesUpdated ) => {
63
+ this . setState ( PluginState . idle ) ;
64
+ let message =
65
+ filesUpdated > 0
66
+ ? `Pulled new changes. ${ filesUpdated } files updated`
67
+ : "Everything up-to-date" ;
68
+ this . displayMessage ( message ) ;
69
+ } ) ;
72
70
}
73
71
74
72
if ( this . settings . autoSaveInterval > 0 ) {
75
73
this . enableAutoBackup ( ) ;
76
74
}
77
75
78
76
this . registerInterval (
79
- window . setInterval ( ( ) => this . updateStatusBar ( ) , 10 * 1000 )
77
+ window . setInterval ( ( ) => this . refreshStatusBar ( ) , 1000 )
80
78
) ;
81
79
82
80
this . addSettingTab ( new ObsidianGitSettingsTab ( this . app , this ) ) ;
@@ -85,14 +83,15 @@ export default class ObsidianGit extends Plugin {
85
83
id : "pull" ,
86
84
name : "Pull from remote repository" ,
87
85
callback : async ( ) => {
88
- let filesUpdated = await this . pull ( ) ;
89
- if ( filesUpdated > 0 ) {
90
- this . maybeNotice (
91
- `Pulled new changes. ${ filesUpdated } files updated.`
92
- ) ;
93
- } else {
94
- this . maybeNotice ( "Everything is up-to-date" ) ;
95
- }
86
+ await this . pull ( ) . then ( ( filesUpdated ) => {
87
+ if ( filesUpdated > 0 ) {
88
+ this . displayMessage (
89
+ `Pulled new changes. ${ filesUpdated } files updated`
90
+ ) ;
91
+ } else {
92
+ this . displayMessage ( "Everything is up-to-date" ) ;
93
+ }
94
+ } ) ;
96
95
this . setState ( PluginState . idle ) ;
97
96
} ,
98
97
} ) ;
@@ -101,19 +100,18 @@ export default class ObsidianGit extends Plugin {
101
100
id : "push" ,
102
101
name : "Commit *all* changes and push to remote repository" ,
103
102
callback : async ( ) =>
104
- await this . isRepoClean ( ) . then ( async ( isClean ) => {
105
- if ( isClean ) {
106
- this . maybeNotice (
107
- "No updates detected. Nothing to push."
108
- ) ;
103
+ await this . getFilesChanged ( ) . then ( async ( files ) => {
104
+ if ( ! files . length ) {
105
+ this . displayMessage ( "No changes detected" ) ;
109
106
} else {
110
- this . maybeNotice (
111
- "Pushing changes to remote repository.."
112
- ) ;
113
107
await this . add ( )
114
108
. then ( async ( ) => await this . commit ( ) )
115
- . then ( async ( ) => await this . push ( ) ) ;
116
- this . maybeNotice ( "Pushed!" ) ;
109
+ . then ( async ( ) => await this . push ( ) )
110
+ . then ( ( ) =>
111
+ this . displayMessage (
112
+ `Pushed ${ files . length } files`
113
+ )
114
+ ) ;
117
115
}
118
116
this . setState ( PluginState . idle ) ;
119
117
} ) ,
@@ -125,15 +123,19 @@ export default class ObsidianGit extends Plugin {
125
123
}
126
124
127
125
// region: main methods
128
- async isRepoClean ( ) : Promise < boolean > {
129
- this . setState ( PluginState . checkRepoClean ) ;
126
+ async getFilesChanged ( ) : Promise < FileStatusResult [ ] > {
127
+ this . setState ( PluginState . status ) ;
130
128
let status = await this . git . status ( ) ;
131
- return status . isClean ( ) ;
129
+ return status . files ;
132
130
}
133
131
134
132
async add ( ) : Promise < void > {
135
133
this . setState ( PluginState . add ) ;
136
- await this . git . add ( "./*" , ( err : Error ) => { } ) ;
134
+ await this . git . add (
135
+ "./*" ,
136
+ ( err : Error | null ) =>
137
+ err && this . displayError ( `Cannot add files: ${ err . message } ` )
138
+ ) ;
137
139
}
138
140
139
141
async commit ( ) : Promise < void > {
@@ -146,34 +148,47 @@ export default class ObsidianGit extends Plugin {
146
148
147
149
async push ( ) : Promise < void > {
148
150
this . setState ( PluginState . push ) ;
149
- await this . git . push ( this . settings . remote , this . settings . currentBranch ) ;
151
+ await this . git . push (
152
+ this . settings . remote ,
153
+ this . settings . currentBranch ,
154
+ null ,
155
+ ( err : Error | null ) => {
156
+ err && this . displayError ( `Push failed ${ err . message } ` ) ;
157
+ }
158
+ ) ;
150
159
151
160
this . lastUpdate = Date . now ( ) ;
152
161
}
153
162
154
163
async pull ( ) : Promise < number > {
155
164
this . setState ( PluginState . pull ) ;
156
- let pullResult = await this . git . pull ( ) ;
165
+ let pullResult = await this . git . pull (
166
+ null ,
167
+ null ,
168
+ null ,
169
+ ( err : Error | null ) =>
170
+ err && this . displayError ( `Pull failed ${ err . message } ` )
171
+ ) ;
157
172
this . lastUpdate = Date . now ( ) ;
158
173
return pullResult . files . length ;
159
174
}
160
175
161
176
// endregion: main methods
162
177
163
- isIdle ( ) : boolean {
164
- return this . getState ( ) === PluginState . idle ;
165
- }
166
-
167
178
enableAutoBackup ( ) {
168
179
let minutes = this . settings . autoSaveInterval ;
169
180
this . intervalID = window . setInterval (
170
181
async ( ) =>
171
- await this . isRepoClean ( ) . then ( async ( isClean ) => {
172
- if ( ! isClean ) {
182
+ await this . getFilesChanged ( ) . then ( async ( files ) => {
183
+ if ( files . length > 0 ) {
173
184
await this . add ( )
174
185
. then ( async ( ) => await this . commit ( ) )
175
- . then ( async ( ) => await this . push ( ) ) ;
176
- this . maybeNotice ( "Pushed changes to remote repository" ) ;
186
+ . then ( async ( ) => await this . push ( ) )
187
+ . then ( ( ) =>
188
+ this . displayMessage (
189
+ `Pushed ${ files . length } files`
190
+ )
191
+ ) ;
177
192
}
178
193
this . setState ( PluginState . idle ) ;
179
194
} ) ,
@@ -191,22 +206,24 @@ export default class ObsidianGit extends Plugin {
191
206
return false ;
192
207
}
193
208
194
- // region: displaying / formatting stuff
195
- maybeNotice ( text : string ) : void {
209
+ // region: displaying / formatting messages
210
+ displayMessage ( message : string , timeout : number = 4 * 1000 ) : void {
211
+ this . statusBar . displayMessage ( message . toLowerCase ( ) , timeout ) ;
212
+
196
213
if ( ! this . settings . disablePopups ) {
197
- new Notice ( text ) ;
198
- } else {
199
- // todo: replace this with a message in a status bar
200
- console . log ( `git obsidian: ${ text } ` ) ;
214
+ new Notice ( message ) ;
201
215
}
216
+
217
+ console . log ( `git obsidian: ${ message } ` ) ;
202
218
}
203
219
204
- updateStatusBar ( ) : void {
205
- if ( this . lastUpdate && this . isIdle ( ) ) {
206
- this . statusBar . displayFromNow ( this . lastUpdate ) ;
207
- } else if ( ! this . lastUpdate && this . isIdle ( ) ) {
208
- this . statusBar . displayIdle ( ) ;
209
- }
220
+ displayError ( message : string , timeout : number = 0 ) : void {
221
+ new Notice ( message ) ;
222
+ this . statusBar . displayMessage ( message . toLowerCase ( ) , timeout ) ;
223
+ }
224
+
225
+ refreshStatusBar ( ) : void {
226
+ this . statusBar . displayState ( this . getState ( ) , this . lastUpdate ) ;
210
227
}
211
228
212
229
async formatCommitMessage ( template : string ) : Promise < string > {
@@ -377,25 +394,34 @@ class ObsidianGitSettingsTab extends PluginSettingTab {
377
394
378
395
class StatusBar {
379
396
private statusBarEl : HTMLElement ;
397
+ private isDisplayingMessage : boolean = false ;
380
398
381
399
constructor ( statusBarEl : HTMLElement ) {
382
400
this . statusBarEl = statusBarEl ;
383
401
}
384
402
385
- displayFromNow ( timestamp : number ) : void {
386
- let moment = ( window as any ) . moment ;
387
- let fromNow = moment ( timestamp ) . fromNow ( ) ;
388
- this . statusBarEl . setText ( `git: last update ${ fromNow } ..` ) ;
389
- }
403
+ displayMessage ( message : string , timeout : number ) {
404
+ this . isDisplayingMessage = true ;
405
+ this . statusBarEl . setText ( `git: ${ message . slice ( 0 , 100 ) } ` ) ;
390
406
391
- displayIdle ( ) : void {
392
- this . statusBarEl . setText ( "git: ready" ) ;
407
+ if ( timeout && timeout > 0 ) {
408
+ window . setTimeout ( ( ) => {
409
+ this . isDisplayingMessage = false ;
410
+ } , timeout ) ;
411
+ }
393
412
}
394
413
395
- display ( plugin : ObsidianGit ) {
396
- switch ( plugin . getState ( ) ) {
397
- case PluginState . checkRepoClean :
398
- this . statusBarEl . setText ( "git: checking repo.." ) ;
414
+ displayState ( state : PluginState , lastUpdate : number ) {
415
+ if ( this . isDisplayingMessage ) {
416
+ return ;
417
+ }
418
+
419
+ switch ( state ) {
420
+ case PluginState . idle :
421
+ this . displayFromNow ( lastUpdate ) ;
422
+ break ;
423
+ case PluginState . status :
424
+ this . statusBarEl . setText ( "git: checking repo status.." ) ;
399
425
break ;
400
426
case PluginState . add :
401
427
this . statusBarEl . setText ( "git: adding files to repo.." ) ;
@@ -407,10 +433,18 @@ class StatusBar {
407
433
this . statusBarEl . setText ( "git: pushing changes.." ) ;
408
434
break ;
409
435
case PluginState . pull :
410
- this . statusBarEl . setText (
411
- "git: pulling changes from remote repo.."
412
- ) ;
436
+ this . statusBarEl . setText ( "git: pulling changes.." ) ;
413
437
break ;
414
438
}
415
439
}
440
+
441
+ displayFromNow ( timestamp : number ) : void {
442
+ if ( timestamp ) {
443
+ let moment = ( window as any ) . moment ;
444
+ let fromNow = moment ( timestamp ) . fromNow ( ) ;
445
+ this . statusBarEl . setText ( `git: last update ${ fromNow } ..` ) ;
446
+ } else {
447
+ this . statusBarEl . setText ( `git: ready` ) ;
448
+ }
449
+ }
416
450
}
0 commit comments