├── .DS_Store
├── .github
└── ISSUE_TEMPLATE
│ ├── Bug_report.md
│ └── Feature_request.md
├── .gitignore
├── .npmrc
├── .travis.yml
├── .vscode
└── launch.json
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── app
├── .DS_Store
├── ci-integration
│ └── appveyor.js
├── frontend
│ ├── .angular-cli.json
│ ├── .editorconfig
│ ├── .gitignore
│ ├── README.md
│ ├── e2e
│ │ ├── app.e2e-spec.ts
│ │ ├── app.po.ts
│ │ └── tsconfig.e2e.json
│ ├── karma-ci.conf.js
│ ├── karma.conf.js
│ ├── package-lock.json
│ ├── package.json
│ ├── protractor.conf.js
│ ├── src
│ │ ├── app
│ │ │ ├── app.component.css
│ │ │ ├── app.component.html
│ │ │ ├── app.component.spec.ts
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── core
│ │ │ │ ├── action-toolbar
│ │ │ │ │ ├── action-toolbar.component.html
│ │ │ │ │ ├── action-toolbar.component.scss
│ │ │ │ │ ├── action-toolbar.component.spec.ts
│ │ │ │ │ └── action-toolbar.component.ts
│ │ │ │ ├── branch-item
│ │ │ │ │ ├── branch-item.component.html
│ │ │ │ │ ├── branch-item.component.scss
│ │ │ │ │ ├── branch-item.component.spec.ts
│ │ │ │ │ └── branch-item.component.ts
│ │ │ │ ├── branch-list
│ │ │ │ │ ├── branch-list.component.html
│ │ │ │ │ ├── branch-list.component.scss
│ │ │ │ │ ├── branch-list.component.spec.ts
│ │ │ │ │ └── branch-list.component.ts
│ │ │ │ ├── branch-viewer
│ │ │ │ │ ├── branch-viewer.component.html
│ │ │ │ │ ├── branch-viewer.component.scss
│ │ │ │ │ ├── branch-viewer.component.spec.ts
│ │ │ │ │ └── branch-viewer.component.ts
│ │ │ │ ├── ci-console-output
│ │ │ │ │ ├── ci-console-output.component.html
│ │ │ │ │ ├── ci-console-output.component.scss
│ │ │ │ │ ├── ci-console-output.component.spec.ts
│ │ │ │ │ └── ci-console-output.component.ts
│ │ │ │ ├── commit-detail-ci
│ │ │ │ │ ├── commit-detail-ci.component.html
│ │ │ │ │ ├── commit-detail-ci.component.scss
│ │ │ │ │ ├── commit-detail-ci.component.spec.ts
│ │ │ │ │ └── commit-detail-ci.component.ts
│ │ │ │ ├── commit-detail-info
│ │ │ │ │ ├── commit-detail-info.component.html
│ │ │ │ │ ├── commit-detail-info.component.scss
│ │ │ │ │ ├── commit-detail-info.component.spec.ts
│ │ │ │ │ └── commit-detail-info.component.ts
│ │ │ │ ├── commit-detail
│ │ │ │ │ ├── commit-detail.component.html
│ │ │ │ │ ├── commit-detail.component.scss
│ │ │ │ │ ├── commit-detail.component.spec.ts
│ │ │ │ │ └── commit-detail.component.ts
│ │ │ │ ├── commit-file-list
│ │ │ │ │ ├── commit-file-list.component.html
│ │ │ │ │ ├── commit-file-list.component.scss
│ │ │ │ │ ├── commit-file-list.component.spec.ts
│ │ │ │ │ ├── commit-file-list.component.ts
│ │ │ │ │ └── file-list-filter.ts
│ │ │ │ ├── committer-card
│ │ │ │ │ ├── committer-card.component.html
│ │ │ │ │ ├── committer-card.component.scss
│ │ │ │ │ ├── committer-card.component.spec.ts
│ │ │ │ │ └── committer-card.component.ts
│ │ │ │ ├── core.module.ts
│ │ │ │ ├── create-branch-prompt
│ │ │ │ │ ├── create-branch-prompt.component.html
│ │ │ │ │ ├── create-branch-prompt.component.scss
│ │ │ │ │ ├── create-branch-prompt.component.spec.ts
│ │ │ │ │ └── create-branch-prompt.component.ts
│ │ │ │ ├── d3
│ │ │ │ │ ├── d3.service.spec.ts
│ │ │ │ │ ├── d3.service.ts
│ │ │ │ │ └── models
│ │ │ │ │ │ ├── color.ts
│ │ │ │ │ │ ├── link.ts
│ │ │ │ │ │ ├── node.ts
│ │ │ │ │ │ └── subway-map.ts
│ │ │ │ ├── enter-login-prompt
│ │ │ │ │ ├── enter-login-prompt.component.html
│ │ │ │ │ ├── enter-login-prompt.component.scss
│ │ │ │ │ ├── enter-login-prompt.component.spec.ts
│ │ │ │ │ └── enter-login-prompt.component.ts
│ │ │ │ ├── external-file-viewer
│ │ │ │ │ ├── external-file-viewer.component.html
│ │ │ │ │ ├── external-file-viewer.component.scss
│ │ │ │ │ ├── external-file-viewer.component.spec.ts
│ │ │ │ │ └── external-file-viewer.component.ts
│ │ │ │ ├── file-counts
│ │ │ │ │ ├── file-counts.component.html
│ │ │ │ │ ├── file-counts.component.scss
│ │ │ │ │ ├── file-counts.component.spec.ts
│ │ │ │ │ └── file-counts.component.ts
│ │ │ │ ├── file-view-panel
│ │ │ │ │ ├── file-view-panel.component.html
│ │ │ │ │ ├── file-view-panel.component.scss
│ │ │ │ │ ├── file-view-panel.component.spec.ts
│ │ │ │ │ └── file-view-panel.component.ts
│ │ │ │ ├── force-push-prompt
│ │ │ │ │ ├── force-push-prompt.component.html
│ │ │ │ │ ├── force-push-prompt.component.scss
│ │ │ │ │ ├── force-push-prompt.component.spec.ts
│ │ │ │ │ └── force-push-prompt.component.ts
│ │ │ │ ├── git-view
│ │ │ │ │ ├── git-view.component.css
│ │ │ │ │ ├── git-view.component.html
│ │ │ │ │ ├── git-view.component.spec.ts
│ │ │ │ │ └── git-view.component.ts
│ │ │ │ ├── map-separator
│ │ │ │ │ ├── map-separator.component.html
│ │ │ │ │ ├── map-separator.component.scss
│ │ │ │ │ ├── map-separator.component.spec.ts
│ │ │ │ │ └── map-separator.component.ts
│ │ │ │ ├── mocks
│ │ │ │ │ ├── mock-appveyor-ci-service.ts
│ │ │ │ │ ├── mock-ci-integration-service.ts
│ │ │ │ │ ├── mock-commit-change-service.ts
│ │ │ │ │ ├── mock-commit-selection-service.ts
│ │ │ │ │ ├── mock-context-menu-service.ts
│ │ │ │ │ ├── mock-credential-service.ts
│ │ │ │ │ ├── mock-d3-service.ts
│ │ │ │ │ ├── mock-history-service.ts
│ │ │ │ │ ├── mock-hotkeys-service.ts
│ │ │ │ │ ├── mock-jira-service.ts
│ │ │ │ │ ├── mock-layout-service.ts
│ │ │ │ │ ├── mock-repo-service.ts
│ │ │ │ │ └── mock-submodule-service.ts
│ │ │ │ ├── open-repo-panel
│ │ │ │ │ ├── open-repo-panel.component.html
│ │ │ │ │ ├── open-repo-panel.component.scss
│ │ │ │ │ ├── open-repo-panel.component.spec.ts
│ │ │ │ │ └── open-repo-panel.component.ts
│ │ │ │ ├── prompt
│ │ │ │ │ ├── prompt-container.directive.ts
│ │ │ │ │ ├── prompt.component.html
│ │ │ │ │ ├── prompt.component.scss
│ │ │ │ │ ├── prompt.component.spec.ts
│ │ │ │ │ └── prompt.component.ts
│ │ │ │ ├── prototypes
│ │ │ │ │ ├── branch.ts
│ │ │ │ │ ├── commit.ts
│ │ │ │ │ └── file-detail.ts
│ │ │ │ ├── services
│ │ │ │ │ ├── appveyor-ci.service.spec.ts
│ │ │ │ │ ├── appveyor-ci.service.ts
│ │ │ │ │ ├── ci-integration.service.spec.ts
│ │ │ │ │ ├── ci-integration.service.ts
│ │ │ │ │ ├── commit-change.service.spec.ts
│ │ │ │ │ ├── commit-change.service.ts
│ │ │ │ │ ├── commit-selection.service.spec.ts
│ │ │ │ │ ├── commit-selection.service.ts
│ │ │ │ │ ├── credentials.service.spec.ts
│ │ │ │ │ ├── credentials.service.ts
│ │ │ │ │ ├── history.service.spec.ts
│ │ │ │ │ ├── history.service.ts
│ │ │ │ │ ├── layout.service.spec.ts
│ │ │ │ │ ├── layout.service.ts
│ │ │ │ │ ├── repo.service.spec.ts
│ │ │ │ │ ├── repo.service.ts
│ │ │ │ │ ├── submodules.service.spec.ts
│ │ │ │ │ └── submodules.service.ts
│ │ │ │ ├── ssh-password-prompt
│ │ │ │ │ ├── ssh-password-prompt.component.html
│ │ │ │ │ ├── ssh-password-prompt.component.scss
│ │ │ │ │ ├── ssh-password-prompt.component.spec.ts
│ │ │ │ │ └── ssh-password-prompt.component.ts
│ │ │ │ ├── status-bar
│ │ │ │ │ ├── status-bar.component.html
│ │ │ │ │ ├── status-bar.component.scss
│ │ │ │ │ ├── status-bar.component.spec.ts
│ │ │ │ │ └── status-bar.component.ts
│ │ │ │ ├── submodule-details-panel
│ │ │ │ │ ├── submodule-details-panel.component.html
│ │ │ │ │ ├── submodule-details-panel.component.scss
│ │ │ │ │ ├── submodule-details-panel.component.spec.ts
│ │ │ │ │ └── submodule-details-panel.component.ts
│ │ │ │ ├── subway-station-annot
│ │ │ │ │ ├── subway-station-annot.component.html
│ │ │ │ │ ├── subway-station-annot.component.scss
│ │ │ │ │ ├── subway-station-annot.component.spec.ts
│ │ │ │ │ └── subway-station-annot.component.ts
│ │ │ │ ├── subway-stations
│ │ │ │ │ ├── subway-stations.component.html
│ │ │ │ │ ├── subway-stations.component.scss
│ │ │ │ │ ├── subway-stations.component.spec.ts
│ │ │ │ │ └── subway-stations.component.ts
│ │ │ │ ├── subway
│ │ │ │ │ ├── subway.component.css
│ │ │ │ │ ├── subway.component.html
│ │ │ │ │ ├── subway.component.spec.ts
│ │ │ │ │ └── subway.component.ts
│ │ │ │ ├── tag-prompt
│ │ │ │ │ ├── tag-prompt.component.html
│ │ │ │ │ ├── tag-prompt.component.scss
│ │ │ │ │ ├── tag-prompt.component.spec.ts
│ │ │ │ │ └── tag-prompt.component.ts
│ │ │ │ └── visuals
│ │ │ │ │ ├── shared
│ │ │ │ │ ├── link-visual
│ │ │ │ │ │ ├── link-visual.component.html
│ │ │ │ │ │ ├── link-visual.component.scss
│ │ │ │ │ │ ├── link-visual.component.spec.ts
│ │ │ │ │ │ └── link-visual.component.ts
│ │ │ │ │ └── node-visual
│ │ │ │ │ │ ├── node-visual.component.html
│ │ │ │ │ │ ├── node-visual.component.scss
│ │ │ │ │ │ ├── node-visual.component.spec.ts
│ │ │ │ │ │ └── node-visual.component.ts
│ │ │ │ │ └── subway
│ │ │ │ │ └── subway-map-visual
│ │ │ │ │ ├── subway-map-visual.component.html
│ │ │ │ │ ├── subway-map-visual.component.scss
│ │ │ │ │ ├── subway-map-visual.component.spec.ts
│ │ │ │ │ └── subway-map-visual.component.ts
│ │ │ ├── infrastructure
│ │ │ │ ├── about-page
│ │ │ │ │ ├── about-page.component.html
│ │ │ │ │ ├── about-page.component.scss
│ │ │ │ │ ├── about-page.component.spec.ts
│ │ │ │ │ └── about-page.component.ts
│ │ │ │ ├── cache.service.spec.ts
│ │ │ │ ├── cache.service.ts
│ │ │ │ ├── electron.service.spec.ts
│ │ │ │ ├── electron.service.ts
│ │ │ │ ├── icheck
│ │ │ │ │ ├── icheck.component.html
│ │ │ │ │ ├── icheck.component.scss
│ │ │ │ │ ├── icheck.component.spec.ts
│ │ │ │ │ └── icheck.component.ts
│ │ │ │ ├── infrastructure.module.ts
│ │ │ │ ├── loading-screen
│ │ │ │ │ ├── loading-screen.component.css
│ │ │ │ │ ├── loading-screen.component.html
│ │ │ │ │ ├── loading-screen.component.spec.ts
│ │ │ │ │ └── loading-screen.component.ts
│ │ │ │ ├── loading-service.service.spec.ts
│ │ │ │ ├── loading-service.service.ts
│ │ │ │ ├── mocks
│ │ │ │ │ ├── mock-electron-service.ts
│ │ │ │ │ ├── mock-loading-service.ts
│ │ │ │ │ ├── mock-prompt-injector-service.ts
│ │ │ │ │ ├── mock-status-bar-service.ts
│ │ │ │ │ └── mock-updater-service.ts
│ │ │ │ ├── prompt-injector.service.spec.ts
│ │ │ │ ├── prompt-injector.service.ts
│ │ │ │ ├── prompt.ts
│ │ │ │ ├── release-note
│ │ │ │ │ ├── release-note.component.html
│ │ │ │ │ ├── release-note.component.scss
│ │ │ │ │ ├── release-note.component.spec.ts
│ │ │ │ │ └── release-note.component.ts
│ │ │ │ ├── spinner
│ │ │ │ │ ├── spinner.component.html
│ │ │ │ │ ├── spinner.component.scss
│ │ │ │ │ ├── spinner.component.spec.ts
│ │ │ │ │ └── spinner.component.ts
│ │ │ │ ├── status-bar.service.spec.ts
│ │ │ │ ├── status-bar.service.ts
│ │ │ │ ├── updater.service.spec.ts
│ │ │ │ └── updater.service.ts
│ │ │ ├── jira
│ │ │ │ ├── add-comment-prompt
│ │ │ │ │ ├── add-comment-prompt.component.html
│ │ │ │ │ ├── add-comment-prompt.component.scss
│ │ │ │ │ ├── add-comment-prompt.component.spec.ts
│ │ │ │ │ └── add-comment-prompt.component.ts
│ │ │ │ ├── jira-detail
│ │ │ │ │ ├── jira-detail.component.html
│ │ │ │ │ ├── jira-detail.component.scss
│ │ │ │ │ ├── jira-detail.component.spec.ts
│ │ │ │ │ └── jira-detail.component.ts
│ │ │ │ ├── jira-rich-text
│ │ │ │ │ ├── jira-rich-text.component.html
│ │ │ │ │ ├── jira-rich-text.component.scss
│ │ │ │ │ ├── jira-rich-text.component.spec.ts
│ │ │ │ │ └── jira-rich-text.component.ts
│ │ │ │ ├── jira.module.ts
│ │ │ │ ├── key-selector
│ │ │ │ │ ├── key-selector.component.html
│ │ │ │ │ ├── key-selector.component.scss
│ │ │ │ │ ├── key-selector.component.spec.ts
│ │ │ │ │ └── key-selector.component.ts
│ │ │ │ ├── models
│ │ │ │ │ ├── comment.ts
│ │ │ │ │ ├── commit-message.ts
│ │ │ │ │ ├── issue-type.ts
│ │ │ │ │ ├── issue.ts
│ │ │ │ │ ├── keyed-item.ts
│ │ │ │ │ ├── priority.ts
│ │ │ │ │ ├── profile.ts
│ │ │ │ │ ├── resolution.ts
│ │ │ │ │ ├── status.ts
│ │ │ │ │ ├── subtask.ts
│ │ │ │ │ └── transition.ts
│ │ │ │ ├── profile-selector
│ │ │ │ │ ├── profile-filter.ts
│ │ │ │ │ ├── profile-selector.component.html
│ │ │ │ │ ├── profile-selector.component.scss
│ │ │ │ │ ├── profile-selector.component.spec.ts
│ │ │ │ │ └── profile-selector.component.ts
│ │ │ │ ├── resolution-control
│ │ │ │ │ ├── resolution-control.component.html
│ │ │ │ │ ├── resolution-control.component.scss
│ │ │ │ │ ├── resolution-control.component.spec.ts
│ │ │ │ │ └── resolution-control.component.ts
│ │ │ │ ├── resolution-selector
│ │ │ │ │ ├── resolution-selector.component.html
│ │ │ │ │ ├── resolution-selector.component.scss
│ │ │ │ │ ├── resolution-selector.component.spec.ts
│ │ │ │ │ └── resolution-selector.component.ts
│ │ │ │ ├── services
│ │ │ │ │ ├── jira-integration.service.spec.ts
│ │ │ │ │ ├── jira-integration.service.ts
│ │ │ │ │ └── jira-issue-link-guard.ts
│ │ │ │ ├── subtask-prompt
│ │ │ │ │ ├── subtask-prompt.component.html
│ │ │ │ │ ├── subtask-prompt.component.scss
│ │ │ │ │ ├── subtask-prompt.component.spec.ts
│ │ │ │ │ └── subtask-prompt.component.ts
│ │ │ │ ├── title-editor
│ │ │ │ │ ├── title-editor.component.html
│ │ │ │ │ ├── title-editor.component.scss
│ │ │ │ │ ├── title-editor.component.spec.ts
│ │ │ │ │ └── title-editor.component.ts
│ │ │ │ └── transition-control
│ │ │ │ │ ├── transition-control.component.html
│ │ │ │ │ ├── transition-control.component.scss
│ │ │ │ │ ├── transition-control.component.spec.ts
│ │ │ │ │ └── transition-control.component.ts
│ │ │ └── settings
│ │ │ │ ├── auth-settings
│ │ │ │ ├── auth-settings.component.html
│ │ │ │ ├── auth-settings.component.scss
│ │ │ │ ├── auth-settings.component.spec.ts
│ │ │ │ └── auth-settings.component.ts
│ │ │ │ ├── ci-settings
│ │ │ │ ├── ci-settings.component.html
│ │ │ │ ├── ci-settings.component.scss
│ │ │ │ ├── ci-settings.component.spec.ts
│ │ │ │ └── ci-settings.component.ts
│ │ │ │ ├── general-settings
│ │ │ │ ├── general-settings.component.html
│ │ │ │ ├── general-settings.component.scss
│ │ │ │ ├── general-settings.component.spec.ts
│ │ │ │ └── general-settings.component.ts
│ │ │ │ ├── jira-settings
│ │ │ │ ├── jira-settings.component.html
│ │ │ │ ├── jira-settings.component.scss
│ │ │ │ ├── jira-settings.component.spec.ts
│ │ │ │ └── jira-settings.component.ts
│ │ │ │ ├── mocks
│ │ │ │ └── mock-settings-service.ts
│ │ │ │ ├── profile-settings
│ │ │ │ ├── profile-settings.component.html
│ │ │ │ ├── profile-settings.component.scss
│ │ │ │ ├── profile-settings.component.spec.ts
│ │ │ │ └── profile-settings.component.ts
│ │ │ │ ├── prototypes
│ │ │ │ └── settings-component.ts
│ │ │ │ ├── repo-profile
│ │ │ │ ├── repo-profile.component.html
│ │ │ │ ├── repo-profile.component.scss
│ │ │ │ ├── repo-profile.component.spec.ts
│ │ │ │ └── repo-profile.component.ts
│ │ │ │ ├── services
│ │ │ │ ├── settings.service.spec.ts
│ │ │ │ └── settings.service.ts
│ │ │ │ ├── settings-nav
│ │ │ │ ├── settings-nav.component.html
│ │ │ │ ├── settings-nav.component.scss
│ │ │ │ ├── settings-nav.component.spec.ts
│ │ │ │ └── settings-nav.component.ts
│ │ │ │ ├── settings-page
│ │ │ │ ├── settings-page.component.html
│ │ │ │ ├── settings-page.component.scss
│ │ │ │ ├── settings-page.component.spec.ts
│ │ │ │ └── settings-page.component.ts
│ │ │ │ ├── settings.module.ts
│ │ │ │ └── updater
│ │ │ │ ├── updater.component.html
│ │ │ │ ├── updater.component.scss
│ │ │ │ ├── updater.component.spec.ts
│ │ │ │ └── updater.component.ts
│ │ ├── assets
│ │ │ ├── .gitkeep
│ │ │ ├── fonts
│ │ │ │ ├── LICENSE.txt
│ │ │ │ ├── Lato-Black.ttf
│ │ │ │ ├── Lato-BlackItalic.ttf
│ │ │ │ ├── Lato-Bold.ttf
│ │ │ │ ├── Lato-BoldItalic.ttf
│ │ │ │ ├── Lato-Hairline.ttf
│ │ │ │ ├── Lato-HairlineItalic.ttf
│ │ │ │ ├── Lato-Italic.ttf
│ │ │ │ ├── Lato-Light.ttf
│ │ │ │ ├── Lato-LightItalic.ttf
│ │ │ │ ├── Lato-Regular.ttf
│ │ │ │ ├── NotoSans-Bold.ttf
│ │ │ │ ├── NotoSans-BoldItalic.ttf
│ │ │ │ ├── NotoSans-Italic.ttf
│ │ │ │ ├── NotoSans-Regular.ttf
│ │ │ │ ├── Roboto-Black.ttf
│ │ │ │ ├── Roboto-BlackItalic.ttf
│ │ │ │ ├── Roboto-Bold.ttf
│ │ │ │ ├── Roboto-BoldItalic.ttf
│ │ │ │ ├── Roboto-Italic.ttf
│ │ │ │ ├── Roboto-Light.ttf
│ │ │ │ ├── Roboto-LightItalic.ttf
│ │ │ │ ├── Roboto-Medium.ttf
│ │ │ │ ├── Roboto-MediumItalic.ttf
│ │ │ │ ├── Roboto-Regular.ttf
│ │ │ │ ├── Roboto-Thin.ttf
│ │ │ │ ├── Roboto-ThinItalic.ttf
│ │ │ │ ├── icomoon.eot
│ │ │ │ ├── icomoon.svg
│ │ │ │ ├── icomoon.ttf
│ │ │ │ └── icomoon.woff
│ │ │ ├── icheck
│ │ │ │ ├── blue.css
│ │ │ │ ├── blue.png
│ │ │ │ └── blue@2x.png
│ │ │ ├── icons.css
│ │ │ └── release-note
│ │ │ │ ├── MG0.2.0-1.gif
│ │ │ │ ├── MG0.2.0-2.gif
│ │ │ │ ├── MG0.2.0-3.gif
│ │ │ │ ├── MG0.2.0-4.gif
│ │ │ │ ├── MG0.3.0-1.gif
│ │ │ │ ├── MG0.3.0-2.gif
│ │ │ │ ├── MG0.3.0-3.gif
│ │ │ │ ├── MG0.4.0-1.gif
│ │ │ │ ├── MG0.4.0-2.gif
│ │ │ │ └── MG0.4.0-3.gif
│ │ ├── bootstrap.min.css
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.scss
│ │ ├── test.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.spec.json
│ │ └── typings.d.ts
│ ├── tsconfig.json
│ ├── tslint.json
│ └── yarn.lock
├── git
│ ├── auto-fetch.js
│ ├── external-file-view.js
│ ├── file-watcher.js
│ ├── repo-command-handler.js
│ ├── repo-helpers.js
│ ├── repo-history.js
│ ├── repo.js
│ └── submodules.js
├── infrastructure
│ ├── auto-updater.js
│ ├── cache.js
│ ├── handler-helper.js
│ ├── release-note.js
│ ├── secure.js
│ ├── settings.js
│ └── shell.js
├── jira-integration
│ └── jira.js
├── main.js
└── visual
│ ├── .DS_Store
│ ├── Icon-1024.png
│ ├── Icon-128.png
│ ├── Icon-16.png
│ ├── Icon-256.png
│ ├── Icon-32.png
│ ├── Icon-48.png
│ ├── Icon-512.png
│ ├── Icon-64.png
│ ├── Icon.ai
│ ├── Icon16.ai
│ ├── Icon48.ai
│ ├── MacOS
│ ├── .DS_Store
│ ├── metrogit.icns
│ └── metrogit.iconset
│ │ ├── .DS_Store
│ │ ├── icon_128x128.png
│ │ ├── icon_128x128@2x.png
│ │ ├── icon_16x16.png
│ │ ├── icon_16x16@2x.png
│ │ ├── icon_256x256.png
│ │ ├── icon_256x256@2x.png
│ │ ├── icon_32x32.png
│ │ ├── icon_32x32@2x.png
│ │ ├── icon_512x512.png
│ │ └── icon_512x512@2x.png
│ ├── _variables.scss
│ ├── appveyor.svg
│ ├── bootstrap.min.css
│ ├── icon.ico
│ └── station-dot.svg
├── build.ps1
├── build.sh
├── build
├── .DS_Store
├── icon.icns
├── icon.ico
└── license_en.txt
├── dev-app-update.yml
├── install-dep.sh
├── misc
├── metrogit.gif
├── metrogit1.PNG
├── metrogit2.PNG
├── metrogit3.PNG
├── metrogit4.PNG
└── metrogit5.PNG
├── package-lock.json
├── package.json
├── publish-linux.sh
├── publish.ps1
├── test.sh
└── yarn.lock
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/.DS_Store
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1. Go to '...'
13 | 2. Click on '....'
14 | 3. Scroll down to '....'
15 | 4. See error
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Additional context**
24 | Add any other context about the problem here.
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
13 | **Additional context**
14 | Add any other context or screenshots about the feature request here.
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | *.log
4 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | runtime = electron
2 | target = 1.8.4
3 | target_arch = x64
4 | disturl = https://atom.io/download/electron
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "8"
4 | install:
5 | - "yarn install"
6 | - "cd app/frontend"
7 | - "yarn install"
8 | - "cd ../.."
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Electron: Main",
11 | "protocol": "inspector",
12 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
13 | "runtimeArgs": [
14 | "--remote-debugging-port=9223",
15 | "."
16 | ],
17 | "windows": {
18 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
19 | }
20 | },
21 | {
22 | "name": "Electron: Renderer",
23 | "type": "chrome",
24 | "request": "attach",
25 | "port": 9223,
26 | "webRoot": "${workspaceFolder}",
27 | "timeout": 30000
28 | }
29 | ],
30 | "compounds": [
31 | {
32 | "name": "Electron: All",
33 | "configurations": [
34 | "Electron: Main",
35 | "Electron: Renderer"
36 | ]
37 | }
38 | ]
39 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Ming-Hung (Michael) Lu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/.DS_Store
--------------------------------------------------------------------------------
/app/frontend/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/app/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /dist-server
6 | /tmp
7 | /out-tsc
8 |
9 | # dependencies
10 | /node_modules
11 |
12 | # IDEs and editors
13 | /.idea
14 | .project
15 | .classpath
16 | .c9/
17 | *.launch
18 | .settings/
19 | *.sublime-workspace
20 |
21 | # IDE - VSCode
22 | .vscode/*
23 | !.vscode/settings.json
24 | !.vscode/tasks.json
25 | !.vscode/launch.json
26 | !.vscode/extensions.json
27 |
28 | # misc
29 | /.sass-cache
30 | /connect.lock
31 | /coverage
32 | /libpeerconnection.log
33 | npm-debug.log
34 | yarn-error.log
35 | testem.log
36 | /typings
37 |
38 | # e2e
39 | /e2e/*.js
40 | /e2e/*.map
41 |
42 | # System Files
43 | .DS_Store
44 | Thumbs.db
45 |
--------------------------------------------------------------------------------
/app/frontend/README.md:
--------------------------------------------------------------------------------
1 | # Frontend
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.7.3.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
28 |
--------------------------------------------------------------------------------
/app/frontend/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 |
3 | describe('frontend App', () => {
4 | let page: AppPage;
5 |
6 | beforeEach(() => {
7 | page = new AppPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('Welcome to app!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/app/frontend/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/app/frontend/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "baseUrl": "./",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": [
9 | "jasmine",
10 | "jasminewd2",
11 | "node"
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/frontend/karma-ci.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular/cli'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular/cli/plugins/karma'),
14 |
15 | ],
16 | client:{
17 | clearContext: false // leave Jasmine Spec Runner output visible in browser
18 | },
19 | coverageIstanbulReporter: {
20 | reports: [ 'html', 'lcovonly' ],
21 | fixWebpackSourcePaths: true
22 | },
23 | angularCli: {
24 | environment: 'dev'
25 | },
26 | customLaunchers: {
27 | ChromeHeadless: {
28 | base: 'Chrome',
29 | flags: [
30 | '--headless',
31 | '--disable-gpu',
32 | '--no-sandbox',
33 | '--remote-debugging-port=9222',
34 | ]
35 | }
36 | },
37 | port: 9876,
38 | colors: true,
39 | logLevel: config.LOG_INFO,
40 | autoWatch: true,
41 | browsers: ['ChromeHeadless'],
42 | singleRun: true,
43 | reporters: ['progress', 'kjhtml'],
44 | });
45 | };
46 |
--------------------------------------------------------------------------------
/app/frontend/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular/cli'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular/cli/plugins/karma'),
14 |
15 | ],
16 | client:{
17 | clearContext: false // leave Jasmine Spec Runner output visible in browser
18 | },
19 | coverageIstanbulReporter: {
20 | reports: [ 'html', 'lcovonly' ],
21 | fixWebpackSourcePaths: true
22 | },
23 | angularCli: {
24 | environment: 'dev'
25 | },
26 | reporters: ['progress', 'kjhtml'],
27 | port: 9876,
28 | colors: true,
29 | logLevel: config.LOG_INFO,
30 | autoWatch: true,
31 | browsers: ['Chrome'],
32 | singleRun: false
33 | });
34 | };
35 |
--------------------------------------------------------------------------------
/app/frontend/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './e2e/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 50000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | require('ts-node').register({
24 | project: 'e2e/tsconfig.e2e.json'
25 | });
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/app/frontend/src/app/app.component.css:
--------------------------------------------------------------------------------
1 | #parent-container{
2 | overflow: hidden;
3 | min-height: 100vh;
4 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/action-toolbar/action-toolbar.component.scss:
--------------------------------------------------------------------------------
1 | $item-height: 55px;
2 | $badge-size: 20px;
3 |
4 | :host{
5 | width: 100%;
6 | }
7 | #action-button-container{
8 | border-bottom: solid 1px #222;
9 | width: 100%;
10 | min-height: $item-height;
11 | cursor: pointer;
12 | .action-button{
13 | width: 80px;
14 | height: $item-height;
15 | padding: 5px 10px;
16 | font-size: 35px;
17 | text-align: center;
18 | position: relative;
19 | app-spinner{
20 | position: absolute;
21 | top: 10px;
22 | left: 20px;
23 | }
24 | &:hover{
25 | background: rgba(0, 0, 0, 0.2);
26 | color: var(--blue);
27 | }
28 | .badge{
29 | position: absolute;
30 | border-radius: 50%;
31 | background: rgb(5, 142, 217);
32 | width: $badge-size;
33 | height: $badge-size;
34 | top: 8px;
35 | right: 13px;
36 | font-size: 12px;
37 | padding: 0.25em 0;
38 | line-height: 14px;
39 | color: #FFF !important;
40 | }
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/branch-item/branch-item.component.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
{{item.display}}
8 |
9 |
10 |
11 |
12 |
13 | Delete Branch
14 |
15 |
16 |
17 |
18 | Delete Tag
19 |
20 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/branch-item/branch-item.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | width: 100%;
3 | }
4 |
5 | .branch-folder,
6 | .branch {
7 | width: 100%;
8 | padding-top: 2px;
9 | padding-bottom: 2px;
10 | height: 30px;
11 | overflow: hidden;
12 | text-overflow: ellipsis;
13 | white-space: nowrap;
14 | position: relative;
15 | cursor: pointer;
16 | .color-marker {
17 | position: absolute;
18 | height: 100%;
19 | width: 5px;
20 | left: 0;
21 | opacity: 0;
22 | }
23 | }
24 |
25 | .branch:hover .color-marker {
26 | opacity: 1;
27 | }
28 |
29 | .branch-folder {
30 | &.toggled {
31 | height: auto;
32 | }
33 | .expand {
34 | margin-top: 5px;
35 | margin-right: 5px;
36 | float: right;
37 | }
38 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/branch-list/branch-list.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/branch-list/branch-list.component.scss:
--------------------------------------------------------------------------------
1 | :host{
2 | width: 100%;
3 | height: 100%;
4 |
5 | }
6 |
7 | .branch-list-container{
8 | width: 100%;
9 | height: 100%;
10 | padding-left: 10px;
11 |
12 | background: rgba(0,0,0,0.2);
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/ci-console-output/ci-console-output.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
Loading output...
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/ci-console-output/ci-console-output.component.scss:
--------------------------------------------------------------------------------
1 | .console-output-container {
2 | width: 100%;
3 | max-height: 310px;
4 | overflow: hidden;
5 | .output {
6 | font-family: 'Consolas';
7 | background: rgba(0, 0, 0, 1);
8 | max-height: 300px;
9 | overflow-wrap: break-word;
10 | overflow-y: auto;
11 | }
12 | .loading-container{
13 | width: 100%;
14 | height: 300px;
15 | }
16 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/ci-console-output/ci-console-output.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { CiConsoleOutputComponent } from './ci-console-output.component';
4 | import { InfrastructureModule } from '../../infrastructure/infrastructure.module';
5 |
6 | describe('CiConsoleOutputComponent', () => {
7 | let component: CiConsoleOutputComponent;
8 | let fixture: ComponentFixture;
9 |
10 | beforeEach(async(() => {
11 | TestBed.configureTestingModule({
12 | declarations: [ CiConsoleOutputComponent ],
13 | imports: [
14 | InfrastructureModule
15 | ]
16 | })
17 | .compileComponents();
18 | }));
19 |
20 | beforeEach(() => {
21 | fixture = TestBed.createComponent(CiConsoleOutputComponent);
22 | component = fixture.componentInstance;
23 | fixture.detectChanges();
24 | });
25 |
26 | it('should create', () => {
27 | expect(component).toBeTruthy();
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/ci-console-output/ci-console-output.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input } from '@angular/core';
2 | import { SafeHtml, DomSanitizer } from '@angular/platform-browser';
3 |
4 | @Component({
5 | selector: 'app-ci-console-output',
6 | templateUrl: './ci-console-output.component.html',
7 | styleUrls: ['./ci-console-output.component.scss']
8 | })
9 | export class CiConsoleOutputComponent implements OnInit {
10 |
11 | @Input() loading = false;
12 | @Input() set text(txt: string) {
13 | let temp = txt;
14 | temp = temp.replace(/\n/g, '
');
15 | this.display = this.sanitizer.bypassSecurityTrustHtml(temp);
16 | }
17 | private display: SafeHtml;
18 | constructor(
19 | private sanitizer: DomSanitizer
20 | ) { }
21 |
22 | ngOnInit() {
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/commit-detail-ci/commit-detail-ci.component.scss:
--------------------------------------------------------------------------------
1 | .commit-detail-info-container {
2 | height: 100%;
3 | }
4 |
5 | .full-width {
6 | width: 100%;
7 | }
8 |
9 | .overall-container {
10 | .overall-ci {
11 | width: 50px;
12 | height: 50px;
13 | border-radius: 50%;
14 | .feather {
15 | width: 42px;
16 | height: 42px;
17 | margin-top: 4px;
18 | margin-left: 4px;
19 | }
20 | }
21 | h5 {
22 | line-height: 50px;
23 | }
24 | }
25 |
26 | .bg-gray {
27 | background: var(--gray) !important;
28 | }
29 |
30 | .selectable {
31 | user-select: initial;
32 | }
33 |
34 | .external-link {
35 | cursor: pointer;
36 | &:hover {
37 | color: var(--blue);
38 | }
39 | }
40 |
41 | .ci-status-container {
42 | overflow: hidden;
43 | .status-header {
44 | cursor: pointer;
45 | .logo-container {
46 | height: 30px;
47 | }
48 | h5 {
49 | margin: 0;
50 | line-height: 30px;
51 | }
52 | background: rgba(0, 0, 0, 0.2);
53 | }
54 | .status-content {
55 | background: rgba(0, 0, 0, 0.2);
56 | max-height: 0;
57 | }
58 | &.toggled .status-content {
59 |
60 | max-height: 400px;
61 | }
62 |
63 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/commit-file-list/commit-file-list.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | width: 100%;
3 | }
4 |
5 | .file-details-container {
6 | min-height: 0;
7 | height: 100%;
8 | }
9 |
10 | .modified-file-list {
11 | background: rgba(0, 0, 0, 0.2);
12 | overflow-y: auto;
13 | overflow-x: hidden;
14 | min-height: 0;
15 | user-select: initial;
16 | .modified-file-entry {
17 | white-space: nowrap;
18 | user-select: initial;
19 | cursor: pointer;
20 | position: relative;
21 | .btn-sm {
22 | height: 24px;
23 | font-size: 12px;
24 | padding: 2px;
25 | }
26 | &:hover {
27 | background: rgba(0, 0, 0, 0.2);
28 | .action-button {
29 | display: block;
30 | }
31 | }
32 | .action-button {
33 | display: none;
34 | position: absolute;
35 | right: 3px;
36 | top: 3px;
37 | line-height: 20px;
38 | }
39 | }
40 | }
41 |
42 | .action-button {
43 | cursor: pointer;
44 | font-size: 12px;
45 | background: var(--gray);
46 | border-radius: 3px;
47 | padding: 3px;
48 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/commit-file-list/commit-file-list.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { CommitFileListComponent } from './commit-file-list.component';
4 | import { FileListFilter } from './file-list-filter';
5 |
6 | describe('CommitFileListComponent', () => {
7 | let component: CommitFileListComponent;
8 | let fixture: ComponentFixture;
9 |
10 | beforeEach(async(() => {
11 | TestBed.configureTestingModule({
12 | declarations: [ CommitFileListComponent, FileListFilter ]
13 | })
14 | .compileComponents();
15 | }));
16 |
17 | beforeEach(() => {
18 | fixture = TestBed.createComponent(CommitFileListComponent);
19 | component = fixture.componentInstance;
20 | fixture.detectChanges();
21 | });
22 |
23 | it('should create', () => {
24 | expect(component).toBeTruthy();
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/commit-file-list/file-list-filter.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 |
3 | @Pipe({
4 | name: 'fileListFilter',
5 | pure: false
6 | })
7 | export class FileListFilter implements PipeTransform {
8 | transform(items: any[], filter: FileListFilterMask): any {
9 | if (!items || !filter) {
10 | return items;
11 | }
12 | return items.filter(item => {
13 | if (item.isAdded && filter.added) {
14 | return true;
15 | } else if (item.isDeleted && filter.deleted) {
16 | return true;
17 | } else if (item.isModified && filter.modified) {
18 | return true;
19 | } else if (item.isRenamed && filter.renamed) {
20 | return true;
21 | } else {
22 | return false;
23 | }
24 | });
25 | }
26 | }
27 |
28 | export class FileListFilterMask {
29 | added: boolean;
30 | deleted: boolean;
31 | modified: boolean;
32 | renamed: boolean;
33 | constructor(add, del, modif, rena) {
34 | this.added = add;
35 | this.deleted = del;
36 | this.modified = modif;
37 | this.renamed = rena;
38 | }
39 | showAll(): boolean {
40 | return this.added && this.deleted && this.modified && this.renamed;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/committer-card/committer-card.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{authorIcn}}
4 |
5 |
6 |
{{_author}}
7 | {{_email}}
8 | at
9 | {{timeStr}}
10 |
11 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/committer-card/committer-card.component.scss:
--------------------------------------------------------------------------------
1 | .committer-info-container {
2 | width: 100%;
3 | }
4 | .committer-badge {
5 | flex-shrink: 0;
6 | width: 70px;
7 | height: 70px;
8 | border-radius: 50%;
9 | line-height: 70px;
10 | text-align: center;
11 | font-size: 36px;
12 | box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
13 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/committer-card/committer-card.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { CommitterCardComponent } from './committer-card.component';
4 | import { D3Service } from '../d3/d3.service';
5 | import { MockD3 } from '../mocks/mock-d3-service';
6 |
7 | describe('CommitterCardComponent', () => {
8 | let component: CommitterCardComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(async(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [CommitterCardComponent],
14 | providers: [
15 | { provide: D3Service, useClass: MockD3 }
16 | ]
17 | })
18 | .compileComponents();
19 | }));
20 |
21 | beforeEach(() => {
22 | fixture = TestBed.createComponent(CommitterCardComponent);
23 | component = fixture.componentInstance;
24 | fixture.detectChanges();
25 | });
26 |
27 | it('should create', () => {
28 | expect(component).toBeTruthy();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/committer-card/committer-card.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input } from '@angular/core';
2 | import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
3 | import { D3Service } from '../d3/d3.service';
4 | import * as moment from 'moment';
5 |
6 | @Component({
7 | selector: 'app-committer-card',
8 | templateUrl: './committer-card.component.html',
9 | styleUrls: ['./committer-card.component.scss']
10 | })
11 | export class CommitterCardComponent implements OnInit {
12 |
13 | @Input() set author(a) {
14 | this.getCommitter(a);
15 | this._author = a;
16 | }
17 | @Input() set email(em) {
18 | this.getBadgeColor(em);
19 | this._email = em;
20 | }
21 | @Input() set time(t) {
22 | this.getDateTime(t);
23 | }
24 | private authorIcn = "";
25 | private badgeColor: SafeStyle;
26 | private timeStr = "";
27 | private _email = "";
28 | private _author = "";
29 | constructor(
30 | private sanitize: DomSanitizer,
31 | private d3: D3Service,
32 | ) { }
33 |
34 | ngOnInit() {
35 | }
36 | getBadgeColor(email) {
37 | this.badgeColor = this.sanitize.bypassSecurityTrustStyle(`${this.d3.getColorByAuthor(email)}`);
38 | }
39 | getDateTime(time) {
40 | this.timeStr = moment(time).format('MM/DD/YYYY hh:mm a');
41 | }
42 | getCommitter(comitter) {
43 | this.authorIcn = this.d3.getAuthor(comitter);
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/create-branch-prompt/create-branch-prompt.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/create-branch-prompt/create-branch-prompt.component.scss:
--------------------------------------------------------------------------------
1 | .login-form{
2 | width: 300px;
3 | height: 100%;
4 |
5 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/create-branch-prompt/create-branch-prompt.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { CreateBranchPromptComponent } from './create-branch-prompt.component';
4 | import { FormsModule } from '../../../../node_modules/@angular/forms';
5 |
6 | describe('CreateBranchPromptComponent', () => {
7 | let component: CreateBranchPromptComponent;
8 | let fixture: ComponentFixture;
9 |
10 | beforeEach(async(() => {
11 | TestBed.configureTestingModule({
12 | declarations: [ CreateBranchPromptComponent ],
13 | imports: [
14 | FormsModule
15 | ]
16 | })
17 | .compileComponents();
18 | }));
19 |
20 | beforeEach(() => {
21 | fixture = TestBed.createComponent(CreateBranchPromptComponent);
22 | component = fixture.componentInstance;
23 | fixture.detectChanges();
24 | });
25 |
26 | it('should create', () => {
27 | expect(component).toBeTruthy();
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/create-branch-prompt/create-branch-prompt.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, EventEmitter } from '@angular/core';
2 | import { Prompt } from '../../infrastructure/prompt';
3 |
4 | @Component({
5 | selector: 'app-create-branch-prompt',
6 | templateUrl: './create-branch-prompt.component.html',
7 | styleUrls: ['./create-branch-prompt.component.scss']
8 | })
9 | export class CreateBranchPromptComponent implements OnInit, Prompt {
10 |
11 | toClose = new EventEmitter();
12 | branchName = "";
13 | onEnter = new EventEmitter();
14 | constructor() { }
15 |
16 | ngOnInit() {
17 | }
18 | close() {
19 | this.toClose.emit();
20 | }
21 | enter() {
22 | this.onEnter.emit(this.branchName);
23 | this.close();
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/d3/d3.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { D3Service } from './d3.service';
4 | import { MockCIIntegration } from '../mocks/mock-ci-integration-service';
5 | import { CiIntegrationService } from '../services/ci-integration.service';
6 |
7 | describe('D3Service', () => {
8 | beforeEach(() => {
9 | TestBed.configureTestingModule({
10 | providers: [
11 | D3Service,
12 | { provide: CiIntegrationService, useClass: MockCIIntegration }
13 | ]
14 | });
15 | });
16 |
17 | it('should be created', inject([D3Service], (service: D3Service) => {
18 | expect(service).toBeTruthy();
19 | }));
20 | });
21 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/d3/models/color.ts:
--------------------------------------------------------------------------------
1 | export class Color {
2 | r: number;
3 | g: number;
4 | b: number;
5 | static parseHex(inString: string): Color {
6 | let color = new Color(0, 0, 0);
7 | color.setHex(inString);
8 | return color;
9 | }
10 |
11 | constructor(r, g, b) {
12 | this.r = r;
13 | this.g = g;
14 | this.b = b;
15 | }
16 | setHex(value: string): void {
17 | let parsed = value.split('#');
18 | if (parsed.length > 1 && parsed[1].length === 6) {
19 | let segment1 = parsed[1].substring(0, 2);
20 | let segment2 = parsed[1].substring(2, 4);
21 | let segment3 = parsed[1].substring(4, 6);
22 | this.r = parseInt(segment1, 16);
23 | this.g = parseInt(segment2, 16);
24 | this.b = parseInt(segment3, 16);
25 | }
26 | }
27 | get stringValue() {
28 | return `rgba(${this.r},${this.g},${this.b},1)`;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/d3/models/link.ts:
--------------------------------------------------------------------------------
1 | import { Node } from './node';
2 | import { Color } from './color';
3 |
4 | export class Link {
5 |
6 | color?: Color;
7 | merge = false;
8 | source: Node;
9 | target: Node;
10 |
11 | constructor(source, target) {
12 | this.source = source;
13 | this.target = target;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/d3/models/node.ts:
--------------------------------------------------------------------------------
1 | import { Commit } from "../../prototypes/commit";
2 | import { Color } from "./color";
3 |
4 | export class Node {
5 |
6 | static height = 35;
7 |
8 | commit?: Commit;
9 | x?: number;
10 | y?: number;
11 | color?: Color;
12 | secondColor?: Color;
13 | id: string;
14 | processed = false;
15 | x_order = 0;
16 |
17 | constructor(id) {
18 | this.id = id;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/enter-login-prompt/enter-login-prompt.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/enter-login-prompt/enter-login-prompt.component.scss:
--------------------------------------------------------------------------------
1 | .login-form{
2 | width: 300px;
3 | height: 100%;
4 |
5 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/enter-login-prompt/enter-login-prompt.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { EnterLoginPromptComponent } from './enter-login-prompt.component';
4 | import { FormsModule } from '../../../../node_modules/@angular/forms';
5 |
6 | describe('EnterLoginPromptComponent', () => {
7 | let component: EnterLoginPromptComponent;
8 | let fixture: ComponentFixture;
9 |
10 | beforeEach(async(() => {
11 | TestBed.configureTestingModule({
12 | declarations: [ EnterLoginPromptComponent ],
13 | imports: [
14 | FormsModule
15 | ]
16 | })
17 | .compileComponents();
18 | }));
19 |
20 | beforeEach(() => {
21 | fixture = TestBed.createComponent(EnterLoginPromptComponent);
22 | component = fixture.componentInstance;
23 | fixture.detectChanges();
24 | });
25 |
26 | it('should create', () => {
27 | expect(component).toBeTruthy();
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/enter-login-prompt/enter-login-prompt.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, EventEmitter } from '@angular/core';
2 | import { Prompt } from '../../infrastructure/prompt';
3 |
4 |
5 | @Component({
6 | selector: 'app-enter-login-prompt',
7 | templateUrl: './enter-login-prompt.component.html',
8 | styleUrls: ['./enter-login-prompt.component.scss']
9 | })
10 | export class EnterLoginPromptComponent implements OnInit, Prompt {
11 |
12 | toClose = new EventEmitter();
13 | username = "";
14 | password = "";
15 | onEnter = new EventEmitter<{username: string, password: string}>();
16 | constructor() { }
17 |
18 | ngOnInit() {
19 | }
20 | close() {
21 | this.toClose.emit();
22 | }
23 | enter() {
24 | this.onEnter.emit({username: this.username, password: this.password});
25 | this.close();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/external-file-viewer/external-file-viewer.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{path}} @ {{getTitle()}}
5 | {{fileDetail.summary.added}} +
6 | {{fileDetail.summary.removed}} -
7 |
8 |
9 |
10 |
11 |
12 |
{{oldPath}} has no {{fileDetail.commit === 'workdir' ? 'unstaged' : 'staged'}} changes
13 |
14 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/external-file-viewer/external-file-viewer.component.scss:
--------------------------------------------------------------------------------
1 | .file-panel{
2 | background: var(--gray-dark);
3 | width: 100vw;
4 | height: 100%;
5 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/file-counts/file-counts.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{modified}}
4 |
5 | {{newCount}}
6 |
7 | {{deleted}}
8 |
9 | {{renamed}}
10 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/file-counts/file-counts.component.scss:
--------------------------------------------------------------------------------
1 | .commit-summary{
2 | cursor: pointer;
3 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/file-counts/file-counts.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { FileCountsComponent } from './file-counts.component';
4 | import { LayoutService } from '../services/layout.service';
5 | import { MockLayout } from '../mocks/mock-layout-service';
6 | import { NgbModule } from '../../../../node_modules/@ng-bootstrap/ng-bootstrap';
7 |
8 | describe('FileCountsComponent', () => {
9 | let component: FileCountsComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | declarations: [ FileCountsComponent ],
15 | imports: [
16 | NgbModule.forRoot(),
17 | ],
18 | providers: [
19 | {provide: LayoutService, useClass: MockLayout}
20 | ]
21 | })
22 | .compileComponents();
23 | }));
24 |
25 | beforeEach(() => {
26 | fixture = TestBed.createComponent(FileCountsComponent);
27 | component = fixture.componentInstance;
28 | fixture.detectChanges();
29 | });
30 |
31 | it('should create', () => {
32 | expect(component).toBeTruthy();
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/file-counts/file-counts.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
2 | import { LayoutService } from '../services/layout.service';
3 |
4 | @Component({
5 | selector: 'app-file-counts',
6 | templateUrl: './file-counts.component.html',
7 | styleUrls: ['./file-counts.component.scss']
8 | })
9 | export class FileCountsComponent implements OnInit {
10 |
11 | @Input() modified = 0;
12 | @Input() newCount = 0;
13 | @Input() deleted = 0;
14 | @Input() renamed = 0;
15 | @Output() modifiedClicked = new EventEmitter();
16 | @Output() newClicked = new EventEmitter();
17 | @Output() deletedClicked = new EventEmitter();
18 | @Output() renamedClicked = new EventEmitter();
19 | private tooltip = true;
20 | constructor(
21 | private layout: LayoutService
22 | ) {
23 | layout.tooltipChanged.subscribe(tp => {
24 | this.tooltip = tp;
25 | });
26 | this.tooltip = layout.tooltipEnabled;
27 | }
28 |
29 | ngOnInit() {
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/force-push-prompt/force-push-prompt.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/force-push-prompt/force-push-prompt.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/app/core/force-push-prompt/force-push-prompt.component.scss
--------------------------------------------------------------------------------
/app/frontend/src/app/core/force-push-prompt/force-push-prompt.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ForcePushPromptComponent } from './force-push-prompt.component';
4 |
5 | describe('ForcePushPromptComponent', () => {
6 | let component: ForcePushPromptComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ ForcePushPromptComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ForcePushPromptComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/force-push-prompt/force-push-prompt.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, EventEmitter } from '@angular/core';
2 | import { Prompt } from '../../infrastructure/prompt';
3 |
4 | @Component({
5 | selector: 'app-force-push-prompt',
6 | templateUrl: './force-push-prompt.component.html',
7 | styleUrls: ['./force-push-prompt.component.scss']
8 | })
9 | export class ForcePushPromptComponent implements OnInit, Prompt {
10 |
11 | toClose = new EventEmitter();
12 | onResult = new EventEmitter();
13 | constructor() { }
14 |
15 | ngOnInit() {
16 | }
17 |
18 | enter() {
19 | this.toClose.emit();
20 | this.onResult.emit(true);
21 | }
22 | close() {
23 | this.toClose.emit();
24 | this.onResult.emit(false);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/git-view/git-view.component.css:
--------------------------------------------------------------------------------
1 | :host{
2 | width: 100%;
3 | }
4 | #outer-container{
5 | min-height: 100vh;
6 | padding: 0;
7 | position: relative;
8 | }
9 | #main-container{
10 | flex: 1;
11 | height: 100%;
12 | min-width: 100vh;
13 | border-bottom: solid 1px #222;
14 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.4);
15 | z-index: 10;
16 | position: relative;
17 | }
18 | #footer-container{
19 | height: 30px;
20 | min-width: 100vh;
21 | background-color: #34495e;
22 | z-index: 9;
23 | }
24 | #inner-container{
25 | width: 100%;
26 | }
27 |
28 | .open-repo-prompt{
29 | position: absolute;
30 | left: 35px;
31 | top: 5px;
32 | }
33 | .hidden{
34 | opacity: 0;
35 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/git-view/git-view.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Click the folder icon to open a repository
9 |
10 |
11 |
12 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/git-view/git-view.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { RepoService } from '../services/repo.service';
3 | import { Commit } from '../prototypes/commit';
4 | import { LoadingService } from '../../infrastructure/loading-service.service';
5 |
6 | @Component({
7 | selector: 'app-git-view',
8 | templateUrl: './git-view.component.html',
9 | styleUrls: ['./git-view.component.css']
10 | })
11 | export class GitViewComponent implements OnInit {
12 |
13 | private repoName: string = null;
14 | private commits: Commit[] = [];
15 | constructor(
16 | private repoService: RepoService,
17 | private loading: LoadingService
18 | ) {
19 | }
20 |
21 | ngOnInit() {
22 | let that = this;
23 | this.repoService.repoChange.subscribe(newRepoName => {
24 | this.loading.enableLoading();
25 | setTimeout(() => {
26 | that.repoName = newRepoName;
27 | that.loading.disableLoading();
28 | });
29 | });
30 | this.repoService.commitsChange.subscribe(commits => {
31 | this.commits = commits;
32 | });
33 | if (this.repoService.hasRepository) {
34 | this.loading.enableLoading();
35 | setTimeout(() => {
36 | that.repoName = that.repoService.repoName;
37 | that.commits = that.repoService.getCommitsWithWIP();
38 | that.loading.disableLoading();
39 | });
40 | }
41 |
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/map-separator/map-separator.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{sep.display}}
4 |
5 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/map-separator/map-separator.component.scss:
--------------------------------------------------------------------------------
1 | .separator-container{
2 | width:100%;
3 | top: 7px;
4 | position: absolute;
5 | z-index: -1;
6 | .separator{
7 | opacity: 0.6;
8 | text-align: left;
9 | border-top: solid 1px var(--gray);
10 | color: var(--gray);
11 | padding-left: 35px;
12 | position: absolute;
13 | width: 100%;
14 | line-height: 10px;
15 | small{
16 | font-size: 12px;
17 | }
18 | }
19 | }
20 | .hidden{
21 | display: none;
22 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/map-separator/map-separator.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { MapSeparatorComponent } from './map-separator.component';
4 |
5 | describe('MapSeparatorComponent', () => {
6 | let component: MapSeparatorComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ MapSeparatorComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(MapSeparatorComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/mocks/mock-appveyor-ci-service.ts:
--------------------------------------------------------------------------------
1 | import { Output, EventEmitter } from "../../../../node_modules/@angular/core";
2 |
3 | export class MockAppVeyor {
4 | @Output() buildsUpdated = new EventEmitter();
5 | @Output() enabledChanged = new EventEmitter();
6 | @Output() logRetrieved = new EventEmitter<{ build: string, output: string }>();
7 |
8 | buildResults;
9 | enabled;
10 | constructor(
11 | ) {
12 |
13 | }
14 |
15 | init() {
16 |
17 | }
18 |
19 | openAppveyor(commit) {
20 | }
21 |
22 | getBuildLog(commit) {
23 | }
24 |
25 | rebuildAppveyor(commit) {
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/mocks/mock-ci-integration-service.ts:
--------------------------------------------------------------------------------
1 | import { Output, EventEmitter } from "../../../../node_modules/@angular/core";
2 |
3 | export class MockCIIntegration {
4 | @Output() buildsUpdated = new EventEmitter();
5 | @Output() enabledChanged = new EventEmitter();
6 | buildResults: any;
7 |
8 | enabled = false;
9 | constructor(
10 | ) {
11 | }
12 |
13 | init() {
14 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/mocks/mock-commit-change-service.ts:
--------------------------------------------------------------------------------
1 | import { Output, EventEmitter } from "@angular/core";
2 |
3 | export class MockCommitChange {
4 |
5 | @Output() messageChange = new EventEmitter();
6 | @Output() detailChange = new EventEmitter();
7 | @Output() stashed = new EventEmitter();
8 | @Output() popped = new EventEmitter();
9 | @Output() commitingChange = new EventEmitter();
10 | defaultKey = "";
11 | set newCommitMessage(msg) {
12 | }
13 | get newCommitMessage() {
14 | return "";
15 | }
16 | set newCommitDetail(msg) {
17 | }
18 | get newCommitDetail() {
19 | return null;
20 | }
21 | constructor(
22 | ) {
23 | }
24 |
25 | init() {
26 |
27 | }
28 | stage(paths): void {
29 | }
30 | stageLines(path, lines) {
31 | }
32 | unstage(paths): void {
33 | }
34 | unstageLines(path, lines) {
35 | }
36 | commit(paths): void {
37 | }
38 | commitStaged(): void {
39 | }
40 | stash(): void {
41 | }
42 | pop(index = -1): void {
43 | }
44 | apply(index = -1): void {
45 | }
46 | deleteStash(index): void {
47 | }
48 | discardAll(): void {
49 | }
50 | tryCommit(): void {
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/mocks/mock-commit-selection-service.ts:
--------------------------------------------------------------------------------
1 | import { Output, EventEmitter } from "@angular/core";
2 | import { CommitDetail, WIPCommit } from "../prototypes/commit";
3 | import { FileDetail } from "../prototypes/file-detail";
4 |
5 | export class MockCommitSelection {
6 | @Output() selectionChange = new EventEmitter();
7 | @Output() selectingChange = new EventEmitter();
8 | @Output() selectedFileChange = new EventEmitter();
9 | @Output() fileDetailChanged = new EventEmitter();
10 | @Output() gettingFileDetail = new EventEmitter();
11 | selectedCommit: CommitDetail | WIPCommit;
12 | constructor(
13 | ) {
14 | }
15 |
16 | selectFileDetail(file, sha = null, fullFile = false) {
17 | }
18 | subscribeLiveFileUpdate(file, commit, fullFile) {
19 | }
20 | select(commit) {
21 | }
22 | openExternalFileView(file, sha = null) {
23 | }
24 | reset(commit, mode): void {
25 | }
26 | createTag(commit): void {
27 | }
28 | deleteTag(name): void {
29 | }
30 | deleteBranch(name): void {
31 | }
32 | deleteRemoteBranch(name): void {
33 | }
34 | unsubscribeFileUpdate(): void {
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/mocks/mock-context-menu-service.ts:
--------------------------------------------------------------------------------
1 | export class MockContextMenuService {
2 | show = {
3 | next(a: any) {
4 |
5 | },
6 | subscribe() {
7 |
8 | }
9 | };
10 | }
11 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/mocks/mock-credential-service.ts:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from "../../../../node_modules/@angular/core";
2 |
3 | export class MockCredential {
4 | username = "";
5 | password = "";
6 | email = "";
7 | name = "";
8 | credentialChange = new EventEmitter<{ username: string, password: string }>();
9 | constructor(
10 | ) {
11 | }
12 |
13 | init() {
14 |
15 | }
16 |
17 | promptUserUpdateCredential() {
18 | }
19 |
20 | promptUserEnterSSHPassword() {
21 | }
22 |
23 | notifyCredentialChange() {
24 | }
25 |
26 | updateCredentials(username, password) {
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/mocks/mock-d3-service.ts:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from "@angular/core";
2 |
3 | export class MockD3 {
4 | mapChange = new EventEmitter();
5 |
6 | scrollTo(commit) {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/mocks/mock-history-service.ts:
--------------------------------------------------------------------------------
1 | import { Output, EventEmitter } from "../../../../node_modules/@angular/core";
2 |
3 | export class MockHistory {
4 | @Output() historyChange = new EventEmitter();
5 | repos = [];
6 | constructor(
7 | ) {
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/mocks/mock-hotkeys-service.ts:
--------------------------------------------------------------------------------
1 | export class MockHotkeys {
2 | add(hotkey: any, a, desc) {
3 |
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/mocks/mock-layout-service.ts:
--------------------------------------------------------------------------------
1 | import { Output, EventEmitter } from "@angular/core";
2 |
3 | export class MockLayout {
4 |
5 | isLocalShown = true;
6 | isRemoteShown = true;
7 | isTagsShown = true;
8 | isDetailPanelOpen = false;
9 | isSubmoduleShown = true;
10 | set tooltipEnabled(tp) { }
11 | get tooltipEnabled() { return true; }
12 |
13 | set isNavToggled(val) { }
14 | get isNavToggled() { return true; }
15 | set isFilePanelOpen(val) { }
16 | get isFilePanelOpen() { return true; }
17 |
18 | @Output() filePanelChanged = new EventEmitter();
19 | @Output() navPanelChanged = new EventEmitter();
20 | @Output() tooltipChanged = new EventEmitter();
21 | constructor(
22 | ) {
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/mocks/mock-submodule-service.ts:
--------------------------------------------------------------------------------
1 | import { Output, EventEmitter } from "@angular/core";
2 |
3 | export class MockSubmodule {
4 | @Output() submoduleChanged = new EventEmitter();
5 | @Output() submoduleSelected = new EventEmitter();
6 | @Output() submoduleDetailChanged = new EventEmitter();
7 | submodules;
8 | selectedSubmodule = "";
9 | submoduleDetails;
10 | constructor(
11 | ) {
12 | }
13 | selectSubmodule(name) {
14 | }
15 | getSubmoduleDetails(name) {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/open-repo-panel/open-repo-panel.component.scss:
--------------------------------------------------------------------------------
1 | $primary-color: #FFF;
2 |
3 | :host{
4 | height: 100%;
5 | position: absolute;
6 | top: 0;
7 | background-color: var(--gray-dark);
8 | }
9 |
10 | .panel-container{
11 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.8);
12 | width: 0px;
13 | color: $primary-color;
14 | height: 100%;
15 | position: relative;
16 | overflow: hidden;
17 | &.toggled{
18 | width: 600px;
19 | }
20 |
21 | .close-btn-container{
22 | position: absolute;
23 | top: 10px;
24 | right: 10px;
25 | .close-btn{
26 | cursor: pointer;
27 | }
28 | i{
29 | font-size: 30px;
30 | }
31 | }
32 | }
33 | .recent-item{
34 | width: 100%;
35 | cursor: pointer;
36 | border-top: 1px solid #222;
37 | h5{
38 | margin: 0;
39 | }
40 | &:hover{
41 | background: rgba(0, 0, 0, 0.2);
42 | .action-button-container {
43 | opacity: 1;
44 | }
45 | }
46 | .action-button-container{
47 | opacity: 0;
48 | align-items: center;
49 | }
50 |
51 | }
52 | .full-width{
53 | width: 100%;
54 | }
55 | .flex-no-shrink{
56 | flex-shrink: 0;
57 | }
58 | .history-container{
59 | min-height: 0;
60 | overflow: auto;
61 | }
62 | .history{
63 | height: 100%;
64 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/prompt/prompt-container.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, ViewContainerRef } from '@angular/core';
2 |
3 | @Directive({
4 | // tslint:disable-next-line:directive-selector
5 | selector: 'app-prompt-container'
6 | })
7 | export class PromptContainerDirective {
8 |
9 | constructor(
10 | public viewContainerRef: ViewContainerRef
11 | ) { }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/prompt/prompt.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/prompt/prompt.component.scss:
--------------------------------------------------------------------------------
1 | :host{
2 | position: absolute;
3 | width: 100%;
4 | left: 0;
5 | top: -55px;
6 | z-index: 15;
7 | height: 55px;
8 | }
9 |
10 | :host.toggled{
11 | top: 0;
12 | }
13 |
14 | .prompt-container{
15 | width: 100%;
16 | height: 100%;
17 | background: var(--dark);
18 | border-radius: 0 0 5px 5px;
19 | border: solid 1px #222;
20 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.8);
21 | text-align: center;
22 | position: relative;
23 | .prompt-inner{
24 | margin: 0 auto;
25 | height: 100%;
26 | }
27 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/prompt/prompt.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { PromptComponent } from './prompt.component';
4 | import { NO_ERRORS_SCHEMA } from '../../../../node_modules/@angular/core';
5 | import { PromptInjectorService } from '../../infrastructure/prompt-injector.service';
6 | import { MockPromptInjector } from '../../infrastructure/mocks/mock-prompt-injector-service';
7 | import { InfrastructureModule } from '../../infrastructure/infrastructure.module';
8 | import { PromptContainerDirective } from './prompt-container.directive';
9 |
10 | describe('PromptComponent', () => {
11 | let component: PromptComponent;
12 | let fixture: ComponentFixture;
13 |
14 | beforeEach(async(() => {
15 | TestBed.configureTestingModule({
16 | declarations: [ PromptComponent, PromptContainerDirective ],
17 | providers: [
18 | {provide: PromptInjectorService, useClass: MockPromptInjector}
19 | ],
20 | })
21 | .compileComponents();
22 | }));
23 |
24 | beforeEach(() => {
25 | fixture = TestBed.createComponent(PromptComponent);
26 | component = fixture.componentInstance;
27 | fixture.detectChanges();
28 | });
29 |
30 | it('should create', () => {
31 | expect(component).toBeTruthy();
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/prompt/prompt.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, HostBinding, ViewChild, ComponentFactory, AfterViewInit } from '@angular/core';
2 | import { PromptContainerDirective } from './prompt-container.directive';
3 | import { PromptInjectorService } from '../../infrastructure/prompt-injector.service';
4 |
5 | @Component({
6 | selector: 'app-prompt',
7 | templateUrl: './prompt.component.html',
8 | styleUrls: ['./prompt.component.scss']
9 | })
10 | export class PromptComponent implements OnInit, AfterViewInit {
11 |
12 | @HostBinding('class') cls = "smooth";
13 | @HostBinding('class.toggled') toggled = false;
14 | @ViewChild(PromptContainerDirective) appPromptContainer: PromptContainerDirective;
15 |
16 | constructor(
17 | private prompt: PromptInjectorService
18 | ) {
19 | }
20 |
21 | ngOnInit() {
22 | }
23 |
24 | ngAfterViewInit() {
25 | this.prompt.init(this.appPromptContainer.viewContainerRef);
26 | this.prompt.componentChange.subscribe(prompt => {
27 | prompt.toClose.subscribe(() => {
28 | this.hide();
29 | });
30 | this.show();
31 | });
32 | }
33 |
34 | show() {
35 | this.toggled = true;
36 | }
37 |
38 | hide() {
39 | this.toggled = false;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/prototypes/branch.ts:
--------------------------------------------------------------------------------
1 | export interface Branch {
2 | name: string;
3 | fullName: string;
4 | shorthand: string;
5 | target: string;
6 | }
7 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/prototypes/file-detail.ts:
--------------------------------------------------------------------------------
1 |
2 | export interface FileDetail {
3 | commit: string;
4 | paths: string[];
5 | path: string;
6 | hunks: [
7 | {
8 | lines: [{
9 | op: string;
10 | content: string;
11 | oldLineno: number;
12 | newLineno: number;
13 | }]
14 | }
15 | ];
16 | summary: {
17 | added: number,
18 | removed: number,
19 | };
20 | }
21 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/services/appveyor-ci.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { AppveyorCiService } from './appveyor-ci.service';
4 | import { MockElectron } from '../../infrastructure/mocks/mock-electron-service';
5 | import { ElectronService } from '../../infrastructure/electron.service';
6 | import { SimpleNotificationsModule } from '../../../../node_modules/angular2-notifications';
7 | import { MockLoading } from '../../infrastructure/mocks/mock-loading-service';
8 | import { LoadingService } from '../../infrastructure/loading-service.service';
9 |
10 | describe('AppveyorCiService', () => {
11 | beforeEach(() => {
12 | TestBed.configureTestingModule({
13 | imports: [
14 | SimpleNotificationsModule.forRoot(),
15 | ],
16 | providers: [
17 | AppveyorCiService,
18 | { provide: ElectronService, useClass: MockElectron },
19 | { provide: LoadingService, useClass: MockLoading },
20 | ]
21 | });
22 | });
23 |
24 | it('should be created', inject([AppveyorCiService], (service: AppveyorCiService) => {
25 | expect(service).toBeTruthy();
26 | }));
27 | });
28 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/services/ci-integration.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { CiIntegrationService } from './ci-integration.service';
4 | import { ElectronService } from '../../infrastructure/electron.service';
5 | import { StatusBarService } from '../../infrastructure/status-bar.service';
6 | import { RepoService } from './repo.service';
7 | import { MockElectron } from '../../infrastructure/mocks/mock-electron-service';
8 | import { MockRepo } from '../mocks/mock-repo-service';
9 | import { MockStatusBar } from '../../infrastructure/mocks/mock-status-bar-service';
10 |
11 | describe('CiIntegrationService', () => {
12 | beforeEach(() => {
13 | TestBed.configureTestingModule({
14 | providers: [
15 | CiIntegrationService,
16 | {provide: ElectronService, useClass: MockElectron},
17 | {provide: StatusBarService, useClass: MockStatusBar},
18 | {provide: RepoService, useClass: MockRepo}
19 | ]
20 | });
21 | });
22 |
23 | it('should be created', inject([CiIntegrationService], (service: CiIntegrationService) => {
24 | expect(service).toBeTruthy();
25 | }));
26 | });
27 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/services/credentials.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { CredentialsService } from './credentials.service';
4 | import { ElectronService } from '../../infrastructure/electron.service';
5 | import { PromptInjectorService } from '../../infrastructure/prompt-injector.service';
6 | import { MockPromptInjector } from '../../infrastructure/mocks/mock-prompt-injector-service';
7 | import { SimpleNotificationsModule } from '../../../../node_modules/angular2-notifications';
8 | import { RouterTestingModule } from '../../../../node_modules/@angular/router/testing';
9 | import { MockElectron } from '../../infrastructure/mocks/mock-electron-service';
10 |
11 | describe('CredentialsService', () => {
12 | beforeEach(() => {
13 | TestBed.configureTestingModule({
14 | imports: [
15 | SimpleNotificationsModule.forRoot(),
16 | RouterTestingModule,
17 | ],
18 | providers: [
19 | CredentialsService,
20 | {provide: ElectronService, useClass: MockElectron},
21 | {provide: PromptInjectorService, useClass: MockPromptInjector},
22 | ]
23 | });
24 | });
25 |
26 | it('should be created', inject([CredentialsService], (service: CredentialsService) => {
27 | expect(service).toBeTruthy();
28 | }));
29 | });
30 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/services/history.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { HistoryService } from './history.service';
4 | import { ElectronService } from '../../infrastructure/electron.service';
5 | import { MockElectron } from '../../infrastructure/mocks/mock-electron-service';
6 |
7 | describe('HistoryService', () => {
8 | beforeEach(() => {
9 | TestBed.configureTestingModule({
10 | providers: [
11 | HistoryService,
12 | {provide: ElectronService, useClass: MockElectron}
13 | ]
14 | });
15 | });
16 |
17 | it('should be created', inject([HistoryService], (service: HistoryService) => {
18 | expect(service).toBeTruthy();
19 | }));
20 | });
21 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/services/history.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, EventEmitter, Output } from '@angular/core';
2 | import { ElectronService } from '../../infrastructure/electron.service';
3 |
4 | @Injectable()
5 | export class HistoryService {
6 |
7 | @Output() historyChange = new EventEmitter();
8 | repos = [];
9 | constructor(
10 | private electron: ElectronService
11 | ) {
12 | electron.onCD('Repo-HistoryChanged', (event, arg) => {
13 | this.repos = arg.history;
14 | this.historyChange.emit(this.repos);
15 | });
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/services/layout.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { LayoutService } from './layout.service';
4 | import { ElectronService } from '../../infrastructure/electron.service';
5 | import { MockElectron } from '../../infrastructure/mocks/mock-electron-service';
6 | import { HotkeysService } from '../../../../node_modules/angular2-hotkeys';
7 | import { MockHotkeys } from '../mocks/mock-hotkeys-service';
8 |
9 | describe('LayoutService', () => {
10 | beforeEach(() => {
11 | TestBed.configureTestingModule({
12 | providers: [
13 | LayoutService,
14 | {provide: ElectronService, useClass: MockElectron},
15 | {provide: HotkeysService, useClass: MockHotkeys}
16 | ]
17 | });
18 | });
19 |
20 | it('should be created', inject([LayoutService], (service: LayoutService) => {
21 | expect(service).toBeTruthy();
22 | }));
23 | });
24 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/services/submodules.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { SubmodulesService } from './submodules.service';
4 | import { ElectronService } from '../../infrastructure/electron.service';
5 | import { MockElectron } from '../../infrastructure/mocks/mock-electron-service';
6 |
7 | describe('SubmodulesService', () => {
8 | beforeEach(() => {
9 | TestBed.configureTestingModule({
10 | providers: [
11 | SubmodulesService,
12 | {provide: ElectronService, useClass: MockElectron}
13 | ]
14 | });
15 | });
16 |
17 | it('should be created', inject([SubmodulesService], (service: SubmodulesService) => {
18 | expect(service).toBeTruthy();
19 | }));
20 | });
21 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/services/submodules.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Output, EventEmitter } from '@angular/core';
2 | import { ElectronService } from '../../infrastructure/electron.service';
3 |
4 | @Injectable()
5 | export class SubmodulesService {
6 |
7 | @Output() submoduleChanged = new EventEmitter();
8 | @Output() submoduleSelected = new EventEmitter();
9 | @Output() submoduleDetailChanged = new EventEmitter();
10 | submodules;
11 | selectedSubmodule = "";
12 | submoduleDetails;
13 | constructor(
14 | private electron: ElectronService
15 | ) {
16 | this.electron.onCD('Repo-SubmoduleNamesRetrieved', (event, arg) => {
17 | this.submodules = arg.submodules;
18 | this.submoduleChanged.emit(this.submodules);
19 | });
20 | this.electron.onCD('Repo-SubmoduleDetailsRetrieved', (event, arg) => {
21 | this.submoduleDetails = arg.result;
22 | this.submoduleDetailChanged.emit(this.submoduleDetails);
23 | });
24 | }
25 |
26 | selectSubmodule(name) {
27 | this.selectedSubmodule = name;
28 | this.submoduleSelected.emit(name);
29 | }
30 |
31 | getSubmoduleDetails(name) {
32 | this.electron.ipcRenderer.send('Repo-GetSubmoduleDetails', {name: name});
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/ssh-password-prompt/ssh-password-prompt.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/ssh-password-prompt/ssh-password-prompt.component.scss:
--------------------------------------------------------------------------------
1 | .login-form{
2 | width: 300px;
3 | height: 100%;
4 |
5 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/ssh-password-prompt/ssh-password-prompt.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { SshPasswordPromptComponent } from './ssh-password-prompt.component';
4 | import { FormsModule } from '../../../../node_modules/@angular/forms';
5 |
6 | describe('SshPasswordPromptComponent', () => {
7 | let component: SshPasswordPromptComponent;
8 | let fixture: ComponentFixture;
9 |
10 | beforeEach(async(() => {
11 | TestBed.configureTestingModule({
12 | imports: [
13 | FormsModule,
14 | ],
15 | declarations: [ SshPasswordPromptComponent ]
16 | })
17 | .compileComponents();
18 | }));
19 |
20 | beforeEach(() => {
21 | fixture = TestBed.createComponent(SshPasswordPromptComponent);
22 | component = fixture.componentInstance;
23 | fixture.detectChanges();
24 | });
25 |
26 | it('should create', () => {
27 | expect(component).toBeTruthy();
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/ssh-password-prompt/ssh-password-prompt.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, EventEmitter } from '@angular/core';
2 | import { Prompt } from '../../infrastructure/prompt';
3 |
4 | @Component({
5 | selector: 'app-ssh-password-prompt',
6 | templateUrl: './ssh-password-prompt.component.html',
7 | styleUrls: ['./ssh-password-prompt.component.scss']
8 | })
9 | export class SshPasswordPromptComponent implements OnInit, Prompt {
10 |
11 | toClose = new EventEmitter();
12 | password = "";
13 | onEnter = new EventEmitter<{password: string}>();
14 | constructor() { }
15 |
16 | ngOnInit() {
17 | }
18 | close() {
19 | this.toClose.emit();
20 | }
21 | enter() {
22 | this.onEnter.emit({password: this.password});
23 | this.close();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/status-bar/status-bar.component.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
9 |
10 | {{message}}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/status-bar/status-bar.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | width: 100%;
3 | height: 100%;
4 | }
5 |
6 | .status-bar-container {
7 | width: 100%;
8 | height: 100%;
9 | position: relative;
10 | font-size: 16px;
11 | padding: 3px 10px;
12 | overflow: hidden;
13 | }
14 |
15 | .status-icon-container{
16 | width: 16px;
17 | transform-origin: 50%;
18 | text-align: center;
19 | }
20 | .status-message-container{
21 | width: 100%;
22 | }
23 |
24 | .message-container{
25 | position: absolute;
26 | top: 50px;
27 | width: 700px;
28 | transition: all 0.4s ease;
29 | }
30 |
31 | .show{
32 | top: 4px;
33 | }
34 |
35 | .loading-spinner {
36 | animation: rotating 0.9s linear infinite;
37 | }
38 |
39 | @keyframes rotating
40 | {
41 | from {
42 | -webkit-transform: rotate(0deg);
43 | -o-transform: rotate(0deg);
44 | transform: rotate(0deg);
45 | }
46 | to {
47 | -webkit-transform: rotate(360deg);
48 | -o-transform: rotate(360deg);
49 | transform: rotate(360deg);
50 | }
51 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/status-bar/status-bar.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { StatusBarComponent } from './status-bar.component';
4 | import { MockStatusBar } from '../../infrastructure/mocks/mock-status-bar-service';
5 | import { StatusBarService } from '../../infrastructure/status-bar.service';
6 |
7 | describe('StatusBarComponent', () => {
8 | let component: StatusBarComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(async(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [ StatusBarComponent ],
14 | providers: [
15 | {provide: StatusBarService, useClass: MockStatusBar}
16 | ]
17 | })
18 | .compileComponents();
19 | }));
20 |
21 | beforeEach(() => {
22 | fixture = TestBed.createComponent(StatusBarComponent);
23 | component = fixture.componentInstance;
24 | fixture.detectChanges();
25 | });
26 |
27 | it('should create', () => {
28 | expect(component).toBeTruthy();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/status-bar/status-bar.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { StatusBarService } from '../../infrastructure/status-bar.service';
3 |
4 | @Component({
5 | selector: 'app-status-bar',
6 | templateUrl: './status-bar.component.html',
7 | styleUrls: ['./status-bar.component.scss']
8 | })
9 | export class StatusBarComponent implements OnInit {
10 |
11 | private loading = false;
12 | private message = "";
13 | private type = "";
14 | private show = false;
15 | constructor(
16 | private sbService: StatusBarService
17 | ) {
18 | sbService.statusChange.subscribe(status => {
19 | if (status.show) {
20 | this.loading = status.loading;
21 | this.message = status.message;
22 | this.type = status.type;
23 | }
24 | this.show = status.show;
25 | });
26 | }
27 |
28 | ngOnInit() {
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/submodule-details-panel/submodule-details-panel.component.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
Submodule: {{_name}}
9 |
Head @ {{details.hid.substring(0, 6)}}
10 |
11 |
12 |
13 |
{{details.message}}
14 |
15 |
16 | {{details.detail}}
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/submodule-details-panel/submodule-details-panel.component.scss:
--------------------------------------------------------------------------------
1 | $primary-color: #FFF;
2 |
3 | :host{
4 | height: 100%;
5 | position: absolute;
6 | top: 0;
7 | background-color: var(--gray-dark);
8 | }
9 |
10 | .panel-container{
11 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.8);
12 | width: 0px;
13 | color: $primary-color;
14 | height: 100%;
15 | position: relative;
16 | overflow: hidden;
17 | &.toggled{
18 | width: 600px;
19 | }
20 |
21 | .close-btn-container{
22 | position: absolute;
23 | top: 10px;
24 | right: 10px;
25 | .close-btn{
26 | cursor: pointer;
27 | }
28 | i{
29 | font-size: 30px;
30 | }
31 | }
32 | .message-title-container {
33 | background: rgba(0, 0, 0, 0.2);
34 | border-bottom: solid 2px #222;
35 | h5 {
36 | margin: 0;
37 | }
38 | user-select: initial;
39 | }
40 | .message-container {
41 | height: 100%;
42 | }
43 | .message-content-container {
44 | background: rgba(0, 0, 0, 0.2);
45 | overflow-y: auto;
46 | user-select: initial;
47 | height: 100%;
48 | }
49 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/submodule-details-panel/submodule-details-panel.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { SubmoduleDetailsPanelComponent } from './submodule-details-panel.component';
4 | import { SubmodulesService } from '../services/submodules.service';
5 | import { MockSubmodule } from '../mocks/mock-submodule-service';
6 | import { NO_ERRORS_SCHEMA } from '../../../../node_modules/@angular/core';
7 |
8 | describe('SubmoduleDetailsPanelComponent', () => {
9 | let component: SubmoduleDetailsPanelComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | declarations: [ SubmoduleDetailsPanelComponent ],
15 | providers: [
16 | {provide: SubmodulesService, useClass: MockSubmodule}
17 | ],
18 | schemas: [NO_ERRORS_SCHEMA]
19 | })
20 | .compileComponents();
21 | }));
22 |
23 | beforeEach(() => {
24 | fixture = TestBed.createComponent(SubmoduleDetailsPanelComponent);
25 | component = fixture.componentInstance;
26 | fixture.detectChanges();
27 | });
28 |
29 | it('should create', () => {
30 | expect(component).toBeTruthy();
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/submodule-details-panel/submodule-details-panel.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input } from '@angular/core';
2 | import { SubmodulesService } from '../services/submodules.service';
3 |
4 | @Component({
5 | selector: 'app-submodule-details-panel',
6 | templateUrl: './submodule-details-panel.component.html',
7 | styleUrls: ['./submodule-details-panel.component.scss']
8 | })
9 | export class SubmoduleDetailsPanelComponent implements OnInit {
10 |
11 | @Input() toggled = false;
12 | @Input() set submoduleName(n) {
13 | this._name = n;
14 | this.submodules.getSubmoduleDetails(n);
15 | }
16 | private details;
17 | private _name = "";
18 | constructor(
19 | private submodules: SubmodulesService
20 | ) {
21 | submodules.submoduleDetailChanged.subscribe(result => {
22 | this.details = result;
23 | });
24 | }
25 |
26 | ngOnInit() {
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/subway/subway.component.css:
--------------------------------------------------------------------------------
1 | :host{
2 | width: 100%;
3 | height: 100%;
4 | position: relative;
5 | overflow-y: auto;
6 | overflow-x: hidden;
7 | }
8 |
9 | .hidden{
10 | display: none;
11 | }
12 |
13 | .subway-outer{
14 | height: 100%;
15 | position: absolute;
16 | width: 100%;
17 | }
18 |
19 | .subway-container{
20 | padding-left: 50px;
21 | border-bottom: solid 1px #222;
22 | box-shadow: 0 0 6px rgba(0, 0, 0, 0.4);
23 | position: relative;
24 | height: auto;
25 | }
26 |
27 | .eoh-container{
28 | color: #6c757d;
29 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/subway/subway.component.html:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/subway/subway.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input } from '@angular/core';
2 | import { Commit } from '../prototypes/commit';
3 | import { Node } from '../d3/models/node';
4 | import { Link } from '../d3/models/link';
5 | import { Color } from '../d3/models/color';
6 | import { RepoService } from '../services/repo.service';
7 |
8 | @Component({
9 | selector: 'app-subway',
10 | templateUrl: './subway.component.html',
11 | styleUrls: ['./subway.component.css']
12 | })
13 | export class SubwayComponent implements OnInit {
14 |
15 | @Input() commits: Commit[];
16 | hasRepo = false;
17 | constructor(private repo: RepoService) {
18 | this.hasRepo = repo.hasRepository;
19 | if (repo.hasRepository) {
20 | this.commits = repo.commits;
21 | }
22 | repo.repoChange.subscribe(name => {
23 | if (!name) {
24 | this.hasRepo = false;
25 | } else {
26 | this.hasRepo = true;
27 | }
28 | });
29 | }
30 |
31 | ngOnInit() {
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/tag-prompt/tag-prompt.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/tag-prompt/tag-prompt.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/app/core/tag-prompt/tag-prompt.component.scss
--------------------------------------------------------------------------------
/app/frontend/src/app/core/tag-prompt/tag-prompt.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { TagPromptComponent } from './tag-prompt.component';
4 | import { NO_ERRORS_SCHEMA } from '../../../../node_modules/@angular/core';
5 |
6 | describe('TagPromptComponent', () => {
7 | let component: TagPromptComponent;
8 | let fixture: ComponentFixture;
9 |
10 | beforeEach(async(() => {
11 | TestBed.configureTestingModule({
12 | declarations: [ TagPromptComponent ],
13 | schemas: [NO_ERRORS_SCHEMA]
14 | })
15 | .compileComponents();
16 | }));
17 |
18 | beforeEach(() => {
19 | fixture = TestBed.createComponent(TagPromptComponent);
20 | component = fixture.componentInstance;
21 | fixture.detectChanges();
22 | });
23 |
24 | it('should create', () => {
25 | expect(component).toBeTruthy();
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/tag-prompt/tag-prompt.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, EventEmitter } from '@angular/core';
2 | import { Prompt } from '../../infrastructure/prompt';
3 |
4 | @Component({
5 | selector: 'app-tag-prompt',
6 | templateUrl: './tag-prompt.component.html',
7 | styleUrls: ['./tag-prompt.component.scss']
8 | })
9 | export class TagPromptComponent implements OnInit, Prompt {
10 |
11 | toClose = new EventEmitter();
12 | toCreate = new EventEmitter<{sha: string, name: string}>();
13 | sha = "";
14 | private tagName = "";
15 | constructor() { }
16 |
17 | ngOnInit() {
18 | }
19 |
20 | enter() {
21 | this.toCreate.emit({sha: this.sha, name: this.tagName});
22 | this.toClose.emit();
23 | }
24 | close() {
25 | this.toClose.emit();
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/visuals/shared/link-visual/link-visual.component.scss:
--------------------------------------------------------------------------------
1 | .line {
2 | fill: none;
3 | stroke-width: 4;
4 | stroke-miterlimit: 10;
5 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/visuals/shared/link-visual/link-visual.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, OnInit, ChangeDetectorRef, AfterViewInit } from '@angular/core';
2 | import { Link } from '../../../d3/models/link';
3 |
4 | @Component({
5 | // tslint:disable-next-line:component-selector
6 | selector: '[linkVisual]',
7 | templateUrl: './link-visual.component.html',
8 | styleUrls: ['./link-visual.component.scss']
9 | })
10 | export class LinkVisualComponent implements OnInit, AfterViewInit {
11 |
12 | // tslint:disable-next-line:no-input-rename
13 | @Input('linkVisual') link: Link;
14 | linkDirection = -1;
15 | mergeDirection = 1;
16 | constructor(
17 | private cdr: ChangeDetectorRef
18 | ) {
19 |
20 | }
21 |
22 | ngAfterViewInit() {
23 | this.cdr.detach();
24 | }
25 | ngOnInit(): void {
26 | if (this.link.target.y > this.link.source.y && this.link.target.x > this.link.source.x) {
27 | this.linkDirection = 1;
28 | }
29 | if (!this.link.merge) {
30 | this.mergeDirection = -1;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/visuals/shared/node-visual/node-visual.component.scss:
--------------------------------------------------------------------------------
1 | :host{
2 | cursor: pointer;
3 | }
4 |
5 | :host:hover{
6 | .highlight{
7 | opacity: 0.3;
8 | }
9 | }
10 |
11 | .highlight.selected{
12 | opacity: 0.3;
13 | }
14 |
15 | .dot {
16 | fill: #FFFFFF;
17 | stroke-width: 4;
18 | stroke-miterlimit: 10;
19 | }
20 |
21 | .highlight{
22 | opacity: 0.1;
23 | border-right: solid 1px transparent;
24 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/core/visuals/subway/subway-map-visual/subway-map-visual.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/app/core/visuals/subway/subway-map-visual/subway-map-visual.component.scss:
--------------------------------------------------------------------------------
1 | :host{
2 | // width: 403px;
3 | flex-shrink: 0;
4 | }
5 | .dot {
6 | fill: #FFFFFF;
7 | stroke-width: 5;
8 | stroke-miterlimit: 10;
9 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/about-page/about-page.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | About
4 |
5 |
MetroGit 0.4.0
6 |
7 |
Love this app? I will greatly appreciate it if you can Buy Me A Tea !
8 |
License: MIT © Ming-Hung (Michael) Lu
9 |
10 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/about-page/about-page.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/app/infrastructure/about-page/about-page.component.scss
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/about-page/about-page.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { AboutPageComponent } from './about-page.component';
4 | import { ElectronService } from '../electron.service';
5 | import { MockElectron } from '../mocks/mock-electron-service';
6 |
7 | describe('AboutPageComponent', () => {
8 | let component: AboutPageComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(async(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [ AboutPageComponent ],
14 | providers: [
15 | {provide: ElectronService, useClass: MockElectron}
16 | ]
17 | })
18 | .compileComponents();
19 | }));
20 |
21 | beforeEach(() => {
22 | fixture = TestBed.createComponent(AboutPageComponent);
23 | component = fixture.componentInstance;
24 | fixture.detectChanges();
25 | });
26 |
27 | it('should create', () => {
28 | expect(component).toBeTruthy();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/about-page/about-page.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { ElectronService } from '../electron.service';
3 |
4 | @Component({
5 | selector: 'app-about-page',
6 | templateUrl: './about-page.component.html',
7 | styleUrls: ['./about-page.component.scss']
8 | })
9 | export class AboutPageComponent implements OnInit {
10 |
11 | constructor(
12 | private electron: ElectronService
13 | ) {
14 | }
15 |
16 | ngOnInit() {
17 | }
18 |
19 | goToRepo() {
20 | this.electron.ipcRenderer.send('Shell-Open', {url: 'https://github.com/Yamazaki93/MetroGit'});
21 | }
22 | goToBMC() {
23 | this.electron.ipcRenderer.send('Shell-Open', {url: 'https://www.buymeacoffee.com/mjCsGWDTS'});
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/cache.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { CacheService } from './cache.service';
4 | import { ElectronService } from './electron.service';
5 | import { MockElectron } from './mocks/mock-electron-service';
6 | import { StatusBarService } from './status-bar.service';
7 | import { MockStatusBar } from './mocks/mock-status-bar-service';
8 |
9 | describe('CacheService', () => {
10 | beforeEach(() => {
11 | TestBed.configureTestingModule({
12 | providers: [
13 | CacheService,
14 | {provide: ElectronService, useClass: MockElectron},
15 | {provide: StatusBarService, useClass: MockStatusBar}
16 | ],
17 | });
18 | });
19 |
20 | it('should be created', inject([CacheService], (service: CacheService) => {
21 | expect(service).toBeTruthy();
22 | }));
23 | });
24 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/cache.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { ElectronService } from './electron.service';
3 | import { StatusBarService } from './status-bar.service';
4 |
5 | @Injectable()
6 | export class CacheService {
7 |
8 | constructor(
9 | private electron: ElectronService,
10 | private statusBar: StatusBarService
11 | ) {
12 | this.electron.onCD('Cache-AutoCleanBegin', (event, arg) => {
13 | this.statusBar.enableLoading('Starting auto cache cleanup');
14 | });
15 | this.electron.onCD('Cache-AutoCleanSuccess', (event, arg) => {
16 | this.statusBar.flash('success', "Auto cache cleanup successful");
17 | });
18 | }
19 | init() {
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/electron.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { ElectronService } from './electron.service';
4 |
5 | // take care of declare var electron;
6 | (window as any).electron = undefined;
7 |
8 | describe('ElectronService', () => {
9 | beforeEach(() => {
10 | TestBed.configureTestingModule({
11 | providers: [ElectronService]
12 | });
13 | });
14 |
15 | it('should be created', inject([ElectronService], (service: ElectronService) => {
16 | expect(service).toBeTruthy();
17 | }));
18 | });
19 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/electron.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, ChangeDetectorRef, NgZone } from '@angular/core';
2 | declare var electron: any;
3 |
4 |
5 | @Injectable()
6 | export class ElectronService {
7 | private initialized = false;
8 | ipcRenderer = null;
9 | constructor(
10 | private zone: NgZone
11 | ) {
12 | if (electron) {
13 | this.ipcRenderer = electron.ipcRenderer;
14 | this.initialized = true;
15 | } else {
16 | console.warn('Electron not available');
17 | }
18 | }
19 |
20 | // safe subscribe method for angular change detection
21 | onCD(event: string, handler: Function) {
22 | if (this.available) {
23 | this.ipcRenderer.on(event, (ev, arg) => {
24 | this.zone.run(() => {
25 | handler(ev, arg);
26 | });
27 | });
28 | }
29 | }
30 | on(event: string, handler: Function) {
31 | if (this.available) {
32 | this.ipcRenderer.on(event, (ev, arg) => {
33 | handler(ev, arg);
34 | });
35 | }
36 | }
37 |
38 | openUrlExternal(url: string) {
39 | if (this.available) {
40 | this.ipcRenderer.send('Shell-Open', {url: url});
41 | }
42 | }
43 |
44 | get available(): boolean {
45 | return this.initialized;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/icheck/icheck.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/icheck/icheck.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | font-family: "Consolas", "Microsoft YaHei", Arial, arial, sans-serif;
3 | overflow: hidden;
4 | }
5 |
6 | :host > div > div {
7 | width: 24px;
8 | height: 24px;
9 | display: inline-block;
10 | vertical-align: middle;
11 | background: url('https://cdn.rawgit.com/fronteed/icheck/1.x/skins/square/blue.png') no-repeat left;
12 | background-position: 0 0;
13 | cursor: pointer;
14 | }
15 |
16 | :host > div > div:hover {
17 | background-position: -24px 0;
18 | }
19 |
20 | :host > div > div.disabled {
21 | background-position: -72px 0;
22 | cursor: default;
23 | }
24 |
25 | :host > div > div.checked {
26 | background-position: -48px 0;
27 | }
28 |
29 | :host > div > div.checked.disabled {
30 | background-position: -96px 0;
31 | }
32 |
33 | :host .label {
34 | display: inline-block;
35 | vertical-align: middle;
36 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/icheck/icheck.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { IcheckComponent } from './icheck.component';
4 |
5 | describe('IcheckComponent', () => {
6 | let component: IcheckComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ IcheckComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(IcheckComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/icheck/icheck.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-icheck',
5 | templateUrl: './icheck.component.html',
6 | styleUrls: ['./icheck.component.scss']
7 | })
8 | export class IcheckComponent implements OnInit {
9 |
10 | @Input() disabled = false;
11 | @Output() checkedChange = new EventEmitter();
12 |
13 | @Input()
14 | set checked(value) {
15 | this.checkedValue = value;
16 | }
17 | get checked() {
18 | return this.checkedValue;
19 | }
20 | private checkedValue = false;
21 | constructor() { }
22 |
23 | ngOnInit() {
24 | }
25 |
26 | onClick() {
27 | if (!this.disabled) {
28 | this.checked = !this.checked;
29 | this.checkedChange.emit(this.checkedValue);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/infrastructure.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { LoadingScreenComponent } from './loading-screen/loading-screen.component';
4 | import { SpinnerComponent } from './spinner/spinner.component';
5 | import { LoadingService } from './loading-service.service';
6 | import { ElectronService } from './electron.service';
7 | import { IcheckComponent } from './icheck/icheck.component';
8 | import { PromptInjectorService } from './prompt-injector.service';
9 | import { StatusBarService } from './status-bar.service';
10 | import { UpdaterService } from './updater.service';
11 | import { ReleaseNoteComponent } from './release-note/release-note.component';
12 | import { AboutPageComponent } from './about-page/about-page.component';
13 | import { CacheService } from './cache.service';
14 |
15 | @NgModule({
16 | imports: [
17 | CommonModule
18 | ],
19 | exports: [LoadingScreenComponent, SpinnerComponent, IcheckComponent],
20 | declarations: [LoadingScreenComponent, SpinnerComponent, IcheckComponent, ReleaseNoteComponent, AboutPageComponent],
21 | providers: [LoadingService, ElectronService, PromptInjectorService, StatusBarService, UpdaterService, CacheService]
22 | })
23 | export class InfrastructureModule { }
24 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/loading-screen/loading-screen.component.css:
--------------------------------------------------------------------------------
1 | :host{
2 | width: 100vw;
3 | height: 100vh;
4 | background: rgba(0, 0, 0, 0.8);
5 | position: absolute;
6 | top: 0;
7 | z-index: 9999;
8 | }
9 | .loading-container{
10 | height: 100vh;
11 | }
12 | .loading-text-container{
13 | width: 100vw;
14 | text-align: center
15 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/loading-screen/loading-screen.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{message}}
5 |
6 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/loading-screen/loading-screen.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, HostBinding, ViewChild } from '@angular/core';
2 | import { LoadingService } from '../loading-service.service';
3 | import { SpinnerComponent } from '../spinner/spinner.component';
4 |
5 | @Component({
6 | selector: 'app-loading-screen',
7 | templateUrl: './loading-screen.component.html',
8 | styleUrls: ['./loading-screen.component.css']
9 | })
10 | export class LoadingScreenComponent implements OnInit {
11 |
12 | message = "";
13 | @HostBinding('style.display') style = 'none';
14 | @ViewChild('spinner') spinner: SpinnerComponent;
15 | set enabled(newStatus: boolean) {
16 | this._enabled = newStatus;
17 | if (this._enabled) {
18 | this.style = 'block';
19 | } else {
20 | this.style = 'none';
21 | }
22 | this.spinner.enabled = this._enabled;
23 | }
24 |
25 | private _enabled = false;
26 | constructor(
27 | private loadingService: LoadingService
28 | ) {
29 | loadingService.change.subscribe(isBusy => {
30 | this.enabled = isBusy;
31 | });
32 | loadingService.messageChange.subscribe(message => {
33 | this.message = message;
34 | });
35 | }
36 |
37 | ngOnInit() {
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/mocks/mock-electron-service.ts:
--------------------------------------------------------------------------------
1 | export class MockElectron {
2 | private handlers: {
3 | msg: string
4 | cb: Function,
5 | }[];
6 | private messageSent: string[] = [];
7 |
8 | constructor() {
9 | this.handlers = [];
10 | }
11 |
12 | ipcRenderer = {
13 | send: (event, handler) => {
14 | this.messageSent.push(event);
15 | }
16 | };
17 |
18 | onCD(event: string, handler: Function) {
19 | this.handlers.push({
20 | msg: event,
21 | cb: handler,
22 | });
23 | }
24 | on(event: string, handler: Function) {
25 | this.handlers.push({
26 | msg: event,
27 | cb: handler,
28 | });
29 | }
30 | receiveEvent(event: string, arg: any) {
31 | this.handlers.forEach(h => {
32 | if (h.msg === event) {
33 | h.cb(undefined, arg);
34 | }
35 | });
36 | }
37 | messageWasSent(event: string) {
38 | return this.messageSent.indexOf(event) !== -1;
39 | }
40 | openUrlExternal(url: string) {
41 | }
42 |
43 | get available(): boolean {
44 | return true;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/mocks/mock-loading-service.ts:
--------------------------------------------------------------------------------
1 | import { EventEmitter, Output } from "../../../../node_modules/@angular/core";
2 |
3 | export class MockLoading {
4 | get isBusy(): boolean {
5 | return true;
6 | }
7 | @Output()
8 | messageChange: EventEmitter = new EventEmitter();
9 | change: EventEmitter = new EventEmitter();
10 |
11 | constructor(
12 | ) { }
13 | updateMessage(message) {
14 | }
15 | enableLoading(message = "Loading...") {
16 | }
17 | disableLoading() {
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/mocks/mock-prompt-injector-service.ts:
--------------------------------------------------------------------------------
1 | import { Output, EventEmitter, Type } from "../../../../node_modules/@angular/core";
2 | import { Prompt } from "../prompt";
3 |
4 | export class MockPromptInjector {
5 |
6 | @Output() componentChange = new EventEmitter();
7 | constructor(
8 | ) { }
9 |
10 | init() {
11 | }
12 |
13 | injectComponent(component: Type): T {
14 | return undefined;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/mocks/mock-status-bar-service.ts:
--------------------------------------------------------------------------------
1 | import { Output, EventEmitter } from "../../../../node_modules/@angular/core";
2 |
3 | export class MockStatusBar {
4 | @Output() statusChange = new EventEmitter();
5 | constructor() { }
6 |
7 | enableLoading(message) {
8 | }
9 | disableLoading() {
10 | }
11 | flash(type, message) {
12 | }
13 | private hide() {
14 | }
15 | }
16 |
17 | interface Status {
18 | loading: boolean;
19 | type: string;
20 | message: string;
21 | show: boolean;
22 | }
23 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/mocks/mock-updater-service.ts:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from "../../../../node_modules/@angular/core";
2 |
3 | export class MockUpdater {
4 | isUpdateAvailable = false;
5 | updateVersion = "";
6 | updateChecking: EventEmitter = new EventEmitter();
7 | updateAvailableChange: EventEmitter = new EventEmitter();
8 | constructor(
9 | ) {
10 |
11 | }
12 | checkUpdate() {
13 | }
14 | installUpdate() {
15 | }
16 | init() {
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/prompt-injector.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { PromptInjectorService } from './prompt-injector.service';
4 |
5 | describe('PromptInjectorService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [PromptInjectorService]
9 | });
10 | });
11 |
12 | it('should be created', inject([PromptInjectorService], (service: PromptInjectorService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/prompt-injector.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, ComponentFactoryResolver, ViewContainerRef, Type, Output, EventEmitter } from '@angular/core';
2 | import { Prompt } from './prompt';
3 |
4 | @Injectable()
5 | export class PromptInjectorService {
6 |
7 | @Output() componentChange = new EventEmitter();
8 | private vcf: ViewContainerRef;
9 | constructor(
10 | private cfr: ComponentFactoryResolver
11 | ) { }
12 |
13 | init(vcf: ViewContainerRef) {
14 | this.vcf = vcf;
15 | }
16 |
17 | injectComponent(component: Type): T {
18 | this.vcf.clear();
19 | let cf = this.cfr.resolveComponentFactory(component);
20 | let componentRef = this.vcf.createComponent(cf);
21 | this.componentChange.emit(componentRef.instance);
22 | return componentRef.instance;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/prompt.ts:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from "@angular/core";
2 |
3 | export interface Prompt {
4 | toClose: EventEmitter<{}>;
5 | }
6 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/release-note/release-note.component.scss:
--------------------------------------------------------------------------------
1 | h3{
2 | color: var(--blue);
3 | }
4 |
5 | img{
6 | width: 50vw;
7 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.8);
8 | margin: 10px 0;
9 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/release-note/release-note.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ReleaseNoteComponent } from './release-note.component';
4 |
5 | describe('ReleaseNoteComponent', () => {
6 | let component: ReleaseNoteComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ ReleaseNoteComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ReleaseNoteComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/release-note/release-note.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-release-note',
5 | templateUrl: './release-note.component.html',
6 | styleUrls: ['./release-note.component.scss']
7 | })
8 | export class ReleaseNoteComponent implements OnInit {
9 |
10 | constructor() { }
11 |
12 | ngOnInit() {
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/spinner/spinner.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { SpinnerComponent } from './spinner.component';
4 |
5 | describe('SpinnerComponent', () => {
6 | let component: SpinnerComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ SpinnerComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(SpinnerComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/spinner/spinner.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input, HostBinding } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-spinner',
5 | templateUrl: './spinner.component.html',
6 | styleUrls: ['./spinner.component.scss'],
7 | })
8 | export class SpinnerComponent implements OnInit {
9 | @Input() enabled = false;
10 | @Input() size = '40';
11 | @HostBinding('class') class = 'd-flex justify-content-center align-items-center';
12 | constructor() { }
13 |
14 | ngOnInit() {
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/status-bar.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { StatusBarService } from './status-bar.service';
4 |
5 | describe('StatusBarService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [StatusBarService]
9 | });
10 | });
11 |
12 | it('should be created', inject([StatusBarService], (service: StatusBarService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/status-bar.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, EventEmitter, Output } from '@angular/core';
2 |
3 | @Injectable()
4 | export class StatusBarService {
5 |
6 | @Output() statusChange = new EventEmitter();
7 | private existingTimeout = null;
8 | constructor() { }
9 |
10 | enableLoading(message) {
11 | this.statusChange.emit({loading: true, type: 'loading', message: message, show: true});
12 | }
13 | disableLoading() {
14 | this.hide();
15 | }
16 | flash(type, message) {
17 | this.statusChange.emit({loading: false, type: type, message: message, show: true});
18 | let that = this;
19 | if (this.existingTimeout) {
20 | clearTimeout(this.existingTimeout);
21 | }
22 | let timeout = 7000;
23 | if (type === 'warning' || type === 'danger') {
24 | timeout = 30 * 1000;
25 | }
26 | this.existingTimeout = setTimeout(() => {
27 | that.hide();
28 | }, timeout);
29 | }
30 | private hide() {
31 | this.statusChange.emit({loading: false, type: null, message: null, show: false});
32 | }
33 | }
34 |
35 | interface Status {
36 | loading: boolean;
37 | type: string;
38 | message: string;
39 | show: boolean;
40 | }
41 |
--------------------------------------------------------------------------------
/app/frontend/src/app/infrastructure/updater.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { UpdaterService } from './updater.service';
4 | import { ElectronService } from './electron.service';
5 | import { MockElectron } from './mocks/mock-electron-service';
6 | import { StatusBarService } from './status-bar.service';
7 | import { MockStatusBar } from './mocks/mock-status-bar-service';
8 | import { SimpleNotificationsModule } from '../../../node_modules/angular2-notifications';
9 |
10 | describe('UpdaterService', () => {
11 | beforeEach(() => {
12 | TestBed.configureTestingModule({
13 | imports: [
14 | SimpleNotificationsModule.forRoot()
15 | ],
16 | providers: [
17 | UpdaterService,
18 | {provide: ElectronService, useClass: MockElectron},
19 | {provide: StatusBarService, useClass: MockStatusBar},
20 | ]
21 | });
22 | });
23 |
24 | it('should be created', inject([UpdaterService], (service: UpdaterService) => {
25 | expect(service).toBeTruthy();
26 | }));
27 | });
28 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/add-comment-prompt/add-comment-prompt.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/add-comment-prompt/add-comment-prompt.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/app/jira/add-comment-prompt/add-comment-prompt.component.scss
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/add-comment-prompt/add-comment-prompt.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { AddCommentPromptComponent } from './add-comment-prompt.component';
4 | import { JiraIntegrationService } from '../services/jira-integration.service';
5 | import { MockJira } from '../../core/mocks/mock-jira-service';
6 | import { FormsModule } from '../../../../node_modules/@angular/forms';
7 |
8 | describe('AddCommentPromptComponent', () => {
9 | let component: AddCommentPromptComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | imports: [
15 | FormsModule
16 | ],
17 | declarations: [ AddCommentPromptComponent ],
18 | providers: [
19 | {provide: JiraIntegrationService, useClass: MockJira}
20 | ]
21 | })
22 | .compileComponents();
23 | }));
24 |
25 | beforeEach(() => {
26 | fixture = TestBed.createComponent(AddCommentPromptComponent);
27 | component = fixture.componentInstance;
28 | fixture.detectChanges();
29 | });
30 |
31 | it('should create', () => {
32 | expect(component).toBeTruthy();
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/add-comment-prompt/add-comment-prompt.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';
2 | import { JiraIntegrationService } from '../services/jira-integration.service';
3 | import { Prompt } from '../../infrastructure/prompt';
4 |
5 | @Component({
6 | selector: 'app-add-comment-prompt',
7 | templateUrl: './add-comment-prompt.component.html',
8 | styleUrls: ['./add-comment-prompt.component.scss']
9 | })
10 | export class AddCommentPromptComponent implements OnInit, Prompt {
11 |
12 | toClose: EventEmitter<{}> = new EventEmitter();
13 | key: string;
14 |
15 | @Output() canceling = new EventEmitter();
16 | private comment = "";
17 | constructor(
18 | private jira: JiraIntegrationService
19 | ) { }
20 |
21 | ngOnInit() {
22 | }
23 |
24 | enter() {
25 | this.jira.addComment(this.key, this.comment);
26 | this.toClose.emit();
27 | }
28 |
29 | close() {
30 | this.toClose.emit();
31 | this.canceling.emit();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/jira-rich-text/jira-rich-text.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/jira-rich-text/jira-rich-text.component.scss:
--------------------------------------------------------------------------------
1 | .rich-text-container {
2 | width: 100%;
3 | max-height: 200px;
4 | overflow-y: auto;
5 | overflow-x: hidden;
6 | background: rgba(0, 0, 0, 0.2);
7 | user-select: initial;
8 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/jira-rich-text/jira-rich-text.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { JiraRichTextComponent } from './jira-rich-text.component';
4 | import { JiraIntegrationService } from '../services/jira-integration.service';
5 | import { MockJira } from '../../core/mocks/mock-jira-service';
6 |
7 | describe('JiraRichTextComponent', () => {
8 | let component: JiraRichTextComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(async(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [ JiraRichTextComponent ],
14 | providers: [
15 | {provide: JiraIntegrationService, useClass: MockJira}
16 | ]
17 | })
18 | .compileComponents();
19 | }));
20 |
21 | beforeEach(() => {
22 | fixture = TestBed.createComponent(JiraRichTextComponent);
23 | component = fixture.componentInstance;
24 | fixture.detectChanges();
25 | });
26 |
27 | it('should create', () => {
28 | expect(component).toBeTruthy();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/key-selector/key-selector.component.scss:
--------------------------------------------------------------------------------
1 | .key-selector-container{
2 | cursor: pointer;
3 | position: relative;
4 | .form-control{
5 | padding: 0 5px;
6 | }
7 | .edit-actions{
8 | position: absolute;
9 | padding: 3px;
10 | border-radius: 3px;
11 | background: rgba(0, 0, 0, 0.2);
12 | right: 0;
13 | top: 25px;
14 | }
15 | .issues{
16 | z-index: 1;
17 | position: absolute;
18 | display: none;
19 | top: 25px;
20 | width: 100%;
21 | background: #FFF;
22 | border-radius: 3px;
23 | border: solid 1px var(--gray);
24 | color: var(--gray-dark);
25 | overflow: hidden;
26 | &.toggled{
27 | display: block;
28 | }
29 | .issue{
30 | width: 100%;
31 | &:hover{
32 | background: rgba(0, 0, 0, 0.2);
33 | }
34 | }
35 | .issue-text{
36 | white-space: nowrap;
37 | overflow: hidden;
38 | text-overflow: ellipsis;
39 | }
40 | }
41 | }
42 |
43 | .flex-no-shrink{
44 | flex-shrink: 0;
45 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/key-selector/key-selector.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { KeySelectorComponent } from './key-selector.component';
4 | import { JiraIntegrationService } from '../services/jira-integration.service';
5 | import { MockJira } from '../../core/mocks/mock-jira-service';
6 | import { FormsModule } from '../../../../node_modules/@angular/forms';
7 | import { NO_ERRORS_SCHEMA } from '../../../../node_modules/@angular/core';
8 |
9 | describe('KeySelectorComponent', () => {
10 | let component: KeySelectorComponent;
11 | let fixture: ComponentFixture;
12 |
13 | beforeEach(async(() => {
14 | TestBed.configureTestingModule({
15 | imports: [
16 | FormsModule
17 | ],
18 | declarations: [KeySelectorComponent],
19 | providers: [
20 | { provide: JiraIntegrationService, useClass: MockJira }
21 | ],
22 | schemas: [NO_ERRORS_SCHEMA]
23 | })
24 | .compileComponents();
25 | }));
26 |
27 | beforeEach(() => {
28 | fixture = TestBed.createComponent(KeySelectorComponent);
29 | component = fixture.componentInstance;
30 | fixture.detectChanges();
31 | });
32 |
33 | it('should create', () => {
34 | expect(component).toBeTruthy();
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/models/comment.ts:
--------------------------------------------------------------------------------
1 | import { Profile } from "./profile";
2 |
3 | export interface Comment {
4 | author: Profile;
5 | body: string;
6 | updateAuthor: Profile;
7 | created: Date;
8 | updated: Date;
9 |
10 | updatedString?: string;
11 | }
12 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/models/commit-message.ts:
--------------------------------------------------------------------------------
1 | export interface CommitMessage {
2 | message: string;
3 | detail: string;
4 | }
5 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/models/issue-type.ts:
--------------------------------------------------------------------------------
1 | export interface IssueType {
2 | name: string;
3 | iconUrl: string;
4 | subtask: boolean;
5 | id: string;
6 | safeIconUrl?: string;
7 | }
8 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/models/keyed-item.ts:
--------------------------------------------------------------------------------
1 | export interface KeyedItem {
2 | key: string;
3 | id: string;
4 | self: string;
5 | }
6 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/models/priority.ts:
--------------------------------------------------------------------------------
1 | export interface Priority {
2 | iconUrl: string;
3 | name: string;
4 | safeIconUrl?: string;
5 | }
6 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/models/profile.ts:
--------------------------------------------------------------------------------
1 | import { SafeUrl } from "@angular/platform-browser";
2 |
3 | export interface Profile {
4 | emailAddress: string;
5 | avatarUrls: {
6 | "48x48": string;
7 | "24x24": string;
8 | "16x16": string;
9 | "32x32": string;
10 | };
11 | name: string;
12 | key: string;
13 | displayName: string;
14 | active: boolean;
15 | safeAvatarUrl?: SafeUrl;
16 | }
17 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/models/resolution.ts:
--------------------------------------------------------------------------------
1 | export interface Resolution {
2 | self: string;
3 | description: string;
4 | iconUrl: string;
5 | name: string;
6 | }
7 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/models/status.ts:
--------------------------------------------------------------------------------
1 | export interface Status {
2 | name: string;
3 | statusCategory: {
4 | key: string;
5 | colorName: string;
6 | name: string;
7 | };
8 | }
9 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/models/subtask.ts:
--------------------------------------------------------------------------------
1 | import { KeyedItem } from "./keyed-item";
2 | import { IssueType } from "./issue-type";
3 | import { Priority } from "./priority";
4 | import { Status } from "./status";
5 | import { SafeResourceUrl } from "@angular/platform-browser";
6 | import { Transition } from "./transition";
7 |
8 | export interface Subtask extends KeyedItem {
9 | fields: {
10 | summary: string,
11 | status: Status;
12 | priority: Priority;
13 | issuetype: IssueType;
14 | safePriorityIconUrl?: SafeResourceUrl;
15 | };
16 | transitions: Transition[];
17 | editmeta: {
18 | fields: any;
19 | };
20 | }
21 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/models/transition.ts:
--------------------------------------------------------------------------------
1 | import { Status } from "./status";
2 |
3 | export interface Transition {
4 | id: string;
5 | name: string;
6 | to: Status;
7 | hasScreen: boolean;
8 | isGlobal: boolean;
9 | isInitial: boolean;
10 | isConditional: boolean;
11 | fields: any;
12 | }
13 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/profile-selector/profile-filter.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 |
3 | @Pipe({
4 | name: 'profileFilter',
5 | pure: false
6 | })
7 | export class ProfileFilterPipe implements PipeTransform {
8 | transform(items: any[], filter: { name: string }): any {
9 | if (!items || !filter) {
10 | return items;
11 | }
12 |
13 | return items.filter(item => item.displayName.toUpperCase().indexOf(filter.name.toUpperCase()) !== -1 || item.key.toUpperCase().indexOf(filter.name.toUpperCase()) !== -1);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/profile-selector/profile-selector.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
![]()
4 |
{{profile.displayName}}
5 |
6 |
7 |
8 |
10 |
11 |
14 |
15 |
16 |
17 |
![]()
18 |
19 |
20 | {{user.displayName}}
21 | @{{user.key}}
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/profile-selector/profile-selector.component.scss:
--------------------------------------------------------------------------------
1 | .profile-container {
2 | position: relative;
3 | .name-container {
4 | overflow: hidden;
5 | white-space: nowrap;
6 | text-overflow: ellipsis;
7 | }
8 | cursor: pointer;
9 | .profiles {
10 | z-index: 1;
11 | min-width: 130px;
12 | display: none;
13 | position: absolute;
14 | top: 35px;
15 | background: #FFF;
16 | border-radius: 5px;
17 | right: 0;
18 | min-width: 100%;
19 | min-height: 20px;
20 | max-height: 300px;
21 | overflow-y: auto;
22 | border: solid 2px var(--gray);
23 | &.toggled {
24 | display: block;
25 | }
26 | .user {
27 | color: var(--gray-dark);
28 | cursor: pointer;
29 | img {
30 | border-radius: 50%;
31 | }
32 | &:hover {
33 | background: rgba(0, 0, 0, 0.2);
34 | }
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/profile-selector/profile-selector.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ProfileSelectorComponent } from './profile-selector.component';
4 | import { JiraIntegrationService } from '../services/jira-integration.service';
5 | import { MockJira } from '../../core/mocks/mock-jira-service';
6 | import { FormsModule } from '../../../../node_modules/@angular/forms';
7 | import { NO_ERRORS_SCHEMA } from '../../../../node_modules/@angular/core';
8 |
9 | describe('ProfileSelectorComponent', () => {
10 | let component: ProfileSelectorComponent;
11 | let fixture: ComponentFixture;
12 |
13 | beforeEach(async(() => {
14 | TestBed.configureTestingModule({
15 | imports: [
16 | FormsModule
17 | ],
18 | declarations: [ProfileSelectorComponent],
19 | providers: [
20 | { provide: JiraIntegrationService, useClass: MockJira }
21 | ],
22 | schemas: [NO_ERRORS_SCHEMA]
23 | })
24 | .compileComponents();
25 | }));
26 |
27 | beforeEach(() => {
28 | fixture = TestBed.createComponent(ProfileSelectorComponent);
29 | component = fixture.componentInstance;
30 | fixture.detectChanges();
31 | });
32 |
33 | it('should create', () => {
34 | expect(component).toBeTruthy();
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/resolution-control/resolution-control.component.html:
--------------------------------------------------------------------------------
1 |
2 |
{{resolution.name}}
3 |
4 |
5 | {{res.name}}
6 |
7 |
8 |
9 | This field is not editable
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/resolution-control/resolution-control.component.scss:
--------------------------------------------------------------------------------
1 | .resolution-container {
2 | position: relative;
3 | cursor: pointer;
4 | .transitions {
5 | z-index: 1;
6 | min-width: 130px;
7 | display: none;
8 | position: absolute;
9 | top: 35px;
10 | background: #FFF;
11 | border-radius: 5px;
12 | width: 100%;
13 | right: 0;
14 | min-height: 20px;
15 | border: solid 2px var(--gray);
16 | &.toggled {
17 | display: block;
18 | }
19 | .transition {
20 | cursor: pointer;
21 | &:hover {
22 | color: #FFF !important;
23 | background: var(--gray);
24 | }
25 | }
26 | }
27 | }
28 | a{
29 | &:hover{
30 | text-decoration: none;
31 | }
32 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/resolution-control/resolution-control.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';
2 | import { Resolution } from '../models/resolution';
3 | import { JiraIntegrationService } from '../services/jira-integration.service';
4 |
5 | @Component({
6 | selector: 'app-resolution-control',
7 | templateUrl: './resolution-control.component.html',
8 | styleUrls: ['./resolution-control.component.scss']
9 | })
10 | export class ResolutionControlComponent implements OnInit {
11 |
12 | @Input() resolution: Resolution;
13 | @Input() key: string;
14 | @Input() editable = true;
15 | @Output() resolutionSelected: EventEmitter = new EventEmitter();
16 | private resolutions: Resolution[];
17 | private toggled = false;
18 | constructor(
19 | private jira: JiraIntegrationService
20 | ) {
21 | this.resolutions = this.jira.resolutions;
22 | }
23 |
24 | ngOnInit() {
25 | }
26 |
27 | selectResolution(resolution: Resolution) {
28 | this.jira.updateIssue(this.key, { "resolution": { "name": resolution.name } });
29 | this.resolutionSelected.emit(resolution);
30 | }
31 | toggle() {
32 | if (this.editable) {
33 | this.toggled = !this.toggled;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/resolution-selector/resolution-selector.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/resolution-selector/resolution-selector.component.scss:
--------------------------------------------------------------------------------
1 | select {
2 | height: 30px;
3 |
4 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/resolution-selector/resolution-selector.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ResolutionSelectorComponent } from './resolution-selector.component';
4 | import { FormsModule } from '../../../../node_modules/@angular/forms';
5 | import { JiraIntegrationService } from '../services/jira-integration.service';
6 | import { MockJira } from '../../core/mocks/mock-jira-service';
7 | import { SimpleNotificationsModule } from '../../../../node_modules/angular2-notifications';
8 |
9 | describe('ResolutionSelectorComponent', () => {
10 | let component: ResolutionSelectorComponent;
11 | let fixture: ComponentFixture;
12 |
13 | beforeEach(async(() => {
14 | TestBed.configureTestingModule({
15 | imports: [
16 | FormsModule,
17 | ],
18 | declarations: [ ResolutionSelectorComponent ],
19 | providers: [
20 | {provide: JiraIntegrationService, useClass: MockJira}
21 | ]
22 | })
23 | .compileComponents();
24 | }));
25 |
26 | beforeEach(() => {
27 | fixture = TestBed.createComponent(ResolutionSelectorComponent);
28 | component = fixture.componentInstance;
29 | fixture.detectChanges();
30 | });
31 |
32 | it('should create', () => {
33 | expect(component).toBeTruthy();
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/resolution-selector/resolution-selector.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, EventEmitter } from '@angular/core';
2 | import { Prompt } from '../../infrastructure/prompt';
3 | import { JiraIntegrationService } from '../services/jira-integration.service';
4 | import { Resolution } from '../models/resolution';
5 |
6 | @Component({
7 | selector: 'app-resolution-selector',
8 | templateUrl: './resolution-selector.component.html',
9 | styleUrls: ['./resolution-selector.component.scss']
10 | })
11 | export class ResolutionSelectorComponent implements OnInit, Prompt {
12 |
13 | toClose = new EventEmitter();
14 | toEnter = new EventEmitter();
15 | key = "";
16 | required = false;
17 | private _resolution;
18 | private resolutions: Resolution[] = [];
19 | constructor(
20 | private jira: JiraIntegrationService
21 | ) {
22 | this.resolutions = this.jira.resolutions;
23 | }
24 |
25 | ngOnInit() {
26 | }
27 |
28 | enter() {
29 | this.toEnter.emit(this._resolution);
30 | this.toClose.emit();
31 | }
32 | continue() {
33 | this.toEnter.emit("");
34 | this.toClose.emit();
35 | }
36 | close() {
37 | this.toClose.emit();
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/services/jira-issue-link-guard.ts:
--------------------------------------------------------------------------------
1 | import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router";
2 | import { Injectable } from "@angular/core";
3 | import { JiraIntegrationService } from "./jira-integration.service";
4 |
5 | @Injectable()
6 | export class JIRAIssueGuard implements CanActivate {
7 | constructor(
8 | private jira: JiraIntegrationService,
9 | ) {
10 | }
11 | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
12 | this.jira.pushPrevious(route.params.previousKey);
13 | this.jira.navigateToIssue(route.params.key);
14 | return false;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/subtask-prompt/subtask-prompt.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/subtask-prompt/subtask-prompt.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/app/jira/subtask-prompt/subtask-prompt.component.scss
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/subtask-prompt/subtask-prompt.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { SubtaskPromptComponent } from './subtask-prompt.component';
4 | import { FormsModule } from '../../../../node_modules/@angular/forms';
5 |
6 | describe('SubtaskPromptComponent', () => {
7 | let component: SubtaskPromptComponent;
8 | let fixture: ComponentFixture;
9 |
10 | beforeEach(async(() => {
11 | TestBed.configureTestingModule({
12 | imports: [
13 | FormsModule
14 | ],
15 | declarations: [ SubtaskPromptComponent ]
16 | })
17 | .compileComponents();
18 | }));
19 |
20 | beforeEach(() => {
21 | fixture = TestBed.createComponent(SubtaskPromptComponent);
22 | component = fixture.componentInstance;
23 | fixture.detectChanges();
24 | });
25 |
26 | it('should create', () => {
27 | expect(component).toBeTruthy();
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/subtask-prompt/subtask-prompt.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, EventEmitter } from '@angular/core';
2 | import { Prompt } from '../../infrastructure/prompt';
3 |
4 | @Component({
5 | selector: 'app-subtask-prompt',
6 | templateUrl: './subtask-prompt.component.html',
7 | styleUrls: ['./subtask-prompt.component.scss']
8 | })
9 | export class SubtaskPromptComponent implements OnInit, Prompt {
10 |
11 | toClose = new EventEmitter();
12 | toEnter = new EventEmitter();
13 | toCancel = new EventEmitter();
14 | key = "";
15 | private name = "";
16 | constructor() { }
17 |
18 | ngOnInit() {
19 | }
20 |
21 | enter() {
22 | this.toEnter.emit(this.name);
23 | this.toClose.emit();
24 | }
25 |
26 | close() {
27 | this.toCancel.emit();
28 | this.toClose.emit();
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/title-editor/title-editor.component.html:
--------------------------------------------------------------------------------
1 |
2 |
{{text}}
3 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/title-editor/title-editor.component.scss:
--------------------------------------------------------------------------------
1 | .title-container {
2 | width: 100%;
3 | position: relative;
4 | border-radius: 5px;
5 | padding: 3px;
6 | cursor: pointer;
7 | h5 i {
8 | display: none;
9 | }
10 | &:hover {
11 | background: rgba(0, 0, 0, 0.2);
12 | h5 i {
13 | display: inline;
14 | }
15 | }
16 | .edit-actions {
17 | padding: 3px;
18 | background: var(--gray-dark);
19 | border-radius: 3px;
20 | right: 0;
21 | top: 38px;
22 | position: absolute;
23 | }
24 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/title-editor/title-editor.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { TitleEditorComponent } from './title-editor.component';
4 | import { FormsModule } from '../../../../node_modules/@angular/forms';
5 |
6 | describe('TitleEditorComponent', () => {
7 | let component: TitleEditorComponent;
8 | let fixture: ComponentFixture;
9 |
10 | beforeEach(async(() => {
11 | TestBed.configureTestingModule({
12 | imports: [
13 | FormsModule
14 | ],
15 | declarations: [ TitleEditorComponent ]
16 | })
17 | .compileComponents();
18 | }));
19 |
20 | beforeEach(() => {
21 | fixture = TestBed.createComponent(TitleEditorComponent);
22 | component = fixture.componentInstance;
23 | fixture.detectChanges();
24 | });
25 |
26 | it('should create', () => {
27 | expect(component).toBeTruthy();
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/title-editor/title-editor.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-title-editor',
5 | templateUrl: './title-editor.component.html',
6 | styleUrls: ['./title-editor.component.scss']
7 | })
8 | export class TitleEditorComponent implements OnInit {
9 |
10 | @Input()
11 | set text(txt: string) {
12 | this._text = txt;
13 | this.textChange.emit(this._text);
14 | }
15 | get text(): string {
16 | return this._text;
17 | }
18 | @Output() textChange = new EventEmitter();
19 | @Output() toConfirm = new EventEmitter();
20 | private _text = "";
21 | private editText = "";
22 | private editing = false;
23 | constructor() { }
24 |
25 | ngOnInit() {
26 | }
27 |
28 | toggleEdit() {
29 | this.editText = this._text;
30 | this.editing = true;
31 | }
32 | cancelEdit() {
33 | this.editing = false;
34 | }
35 | confirmEdit() {
36 | this.text = this.editText;
37 | this.toConfirm.emit();
38 | this.editing = false;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/transition-control/transition-control.component.html:
--------------------------------------------------------------------------------
1 |
4 | {{status.name}}
5 |
6 |
11 | {{trans.name}}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/frontend/src/app/jira/transition-control/transition-control.component.scss:
--------------------------------------------------------------------------------
1 | .status-container {
2 | position: relative;
3 | cursor: pointer;
4 | .transitions {
5 | z-index: 1;
6 | min-width: 130px;
7 | display: none;
8 | position: absolute;
9 | top: 35px;
10 | background: #FFF;
11 | border-radius: 5px;
12 | width: 100%;
13 | right: 0;
14 | min-height: 20px;
15 | border: solid 2px var(--gray);
16 | &.toggled {
17 | display: block;
18 | }
19 | .transition {
20 | cursor: pointer;
21 | &:hover {
22 | color: #FFF !important;
23 | background: var(--gray);
24 | }
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/auth-settings/auth-settings.component.scss:
--------------------------------------------------------------------------------
1 | .input-action-btn{
2 | cursor: pointer;
3 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/auth-settings/auth-settings.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { AuthSettingsComponent } from './auth-settings.component';
4 | import { SettingsService } from '../services/settings.service';
5 | import { MockSettings } from '../mocks/mock-settings-service';
6 | import { NgbModule } from '../../../../node_modules/@ng-bootstrap/ng-bootstrap';
7 |
8 | describe('AuthSettingsComponent', () => {
9 | let component: AuthSettingsComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | imports: [
15 | NgbModule.forRoot()
16 | ],
17 | declarations: [AuthSettingsComponent],
18 | providers: [
19 | { provide: SettingsService, useClass: MockSettings }
20 | ]
21 | })
22 | .compileComponents();
23 | }));
24 |
25 | beforeEach(() => {
26 | fixture = TestBed.createComponent(AuthSettingsComponent);
27 | component = fixture.componentInstance;
28 | fixture.detectChanges();
29 | });
30 |
31 | it('should create', () => {
32 | expect(component).toBeTruthy();
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/auth-settings/auth-settings.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { SettingsService } from '../services/settings.service';
3 | import { SettingsComponent } from '../prototypes/settings-component';
4 |
5 | @Component({
6 | selector: 'app-auth-settings',
7 | templateUrl: './auth-settings.component.html',
8 | styleUrls: ['./auth-settings.component.scss']
9 | })
10 | export class AuthSettingsComponent extends SettingsComponent {
11 |
12 | private keyPath = "";
13 | private pubPath = "";
14 | constructor(
15 | settings: SettingsService
16 | ) {
17 | super(settings);
18 | }
19 |
20 | getSettings() {
21 | this.keyPath = this.settings.getAppSetting('auth-keypath');
22 | this.pubPath = this.settings.getAppSetting('auth-pubpath');
23 | }
24 |
25 | browseKey() {
26 | this.keyPath = this.settings.browseFile();
27 | this.settings.setSetting('auth-keypath', this.keyPath);
28 | }
29 | browsePub() {
30 | this.pubPath = this.settings.browseFile();
31 | this.settings.setSetting('auth-pubpath', this.pubPath);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/ci-settings/ci-settings.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/app/settings/ci-settings/ci-settings.component.scss
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/ci-settings/ci-settings.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { CiSettingsComponent } from './ci-settings.component';
4 | import { SettingsService } from '../services/settings.service';
5 | import { MockSettings } from '../mocks/mock-settings-service';
6 | import { NO_ERRORS_SCHEMA } from '../../../../node_modules/@angular/core';
7 | import { ElectronService } from '../../infrastructure/electron.service';
8 | import { MockElectron } from '../../infrastructure/mocks/mock-electron-service';
9 |
10 | describe('CiSettingsComponent', () => {
11 | let component: CiSettingsComponent;
12 | let fixture: ComponentFixture;
13 |
14 | beforeEach(async(() => {
15 | TestBed.configureTestingModule({
16 | declarations: [CiSettingsComponent],
17 | providers: [
18 | { provide: SettingsService, useClass: MockSettings },
19 | { provide: ElectronService, useClass: MockElectron }
20 | ],
21 | schemas: [NO_ERRORS_SCHEMA]
22 | })
23 | .compileComponents();
24 | }));
25 |
26 | beforeEach(() => {
27 | fixture = TestBed.createComponent(CiSettingsComponent);
28 | component = fixture.componentInstance;
29 | fixture.detectChanges();
30 | });
31 |
32 | it('should create', () => {
33 | expect(component).toBeTruthy();
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/general-settings/general-settings.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/app/settings/general-settings/general-settings.component.scss
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/general-settings/general-settings.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { GeneralSettingsComponent } from './general-settings.component';
4 | import { FormsModule } from '../../../../node_modules/@angular/forms';
5 | import { NO_ERRORS_SCHEMA } from '../../../../node_modules/@angular/core';
6 | import { SettingsService } from '../services/settings.service';
7 | import { MockSettings } from '../mocks/mock-settings-service';
8 |
9 | describe('GeneralSettingsComponent', () => {
10 | let component: GeneralSettingsComponent;
11 | let fixture: ComponentFixture;
12 |
13 | beforeEach(async(() => {
14 | TestBed.configureTestingModule({
15 | imports: [
16 | FormsModule
17 | ],
18 | declarations: [GeneralSettingsComponent],
19 | providers: [
20 | { provide: SettingsService, useClass: MockSettings }
21 | ],
22 | schemas: [NO_ERRORS_SCHEMA]
23 | })
24 | .compileComponents();
25 | }));
26 |
27 | beforeEach(() => {
28 | fixture = TestBed.createComponent(GeneralSettingsComponent);
29 | component = fixture.componentInstance;
30 | fixture.detectChanges();
31 | });
32 |
33 | it('should create', () => {
34 | expect(component).toBeTruthy();
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/jira-settings/jira-settings.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/app/settings/jira-settings/jira-settings.component.scss
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/jira-settings/jira-settings.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { JiraSettingsComponent } from './jira-settings.component';
4 | import { SettingsService } from '../services/settings.service';
5 | import { MockSettings } from '../mocks/mock-settings-service';
6 | import { NO_ERRORS_SCHEMA } from '../../../../node_modules/@angular/core';
7 | import { ElectronService } from '../../infrastructure/electron.service';
8 | import { MockElectron } from '../../infrastructure/mocks/mock-electron-service';
9 |
10 | describe('JiraSettingsComponent', () => {
11 | let component: JiraSettingsComponent;
12 | let fixture: ComponentFixture;
13 |
14 | beforeEach(async(() => {
15 | TestBed.configureTestingModule({
16 | declarations: [JiraSettingsComponent],
17 | providers: [
18 | { provide: SettingsService, useClass: MockSettings },
19 | { provide: ElectronService, useClass: MockElectron }
20 | ],
21 | schemas: [NO_ERRORS_SCHEMA]
22 | })
23 | .compileComponents();
24 | }));
25 |
26 | beforeEach(() => {
27 | fixture = TestBed.createComponent(JiraSettingsComponent);
28 | component = fixture.componentInstance;
29 | fixture.detectChanges();
30 | });
31 |
32 | it('should create', () => {
33 | expect(component).toBeTruthy();
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/mocks/mock-settings-service.ts:
--------------------------------------------------------------------------------
1 | import { Output, EventEmitter } from "../../../../node_modules/@angular/core";
2 |
3 | export class MockSettings {
4 | @Output() settingsUpdated = new EventEmitter();
5 | settingsData = new Settings;
6 | constructor(
7 | ) {
8 | }
9 |
10 | init() {
11 | }
12 | setSetting(key, value) {
13 | }
14 | setRepoSetting(key, value) {
15 | }
16 | setSecureRepoSetting(key, value) {
17 | }
18 | browseFile(): string {
19 | return "";
20 | }
21 | getRepoSetting(key) {
22 | }
23 | getSecureRepoSetting(key) {
24 | }
25 | getAppSetting(key) {
26 | }
27 | clearSecureCache() {
28 | }
29 | }
30 |
31 | class Settings {
32 | app_settings: Map;
33 | repo_settings: Map;
34 | current_repo: RepoInfo = {
35 | id: '',
36 | name: null
37 | };
38 | }
39 |
40 | interface RepoInfo {
41 | id: string;
42 | name: string;
43 | }
44 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/profile-settings/profile-settings.component.html:
--------------------------------------------------------------------------------
1 |
2 | Profile
3 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/profile-settings/profile-settings.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/app/settings/profile-settings/profile-settings.component.scss
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/profile-settings/profile-settings.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ProfileSettingsComponent } from './profile-settings.component';
4 | import { SettingsService } from '../services/settings.service';
5 | import { MockSettings } from '../mocks/mock-settings-service';
6 | import { NO_ERRORS_SCHEMA } from '../../../../node_modules/@angular/core';
7 | import { FormsModule } from '../../../../node_modules/@angular/forms';
8 |
9 | describe('ProfileSettingsComponent', () => {
10 | let component: ProfileSettingsComponent;
11 | let fixture: ComponentFixture;
12 |
13 | beforeEach(async(() => {
14 | TestBed.configureTestingModule({
15 | declarations: [ ProfileSettingsComponent ],
16 | imports: [
17 | FormsModule
18 | ],
19 | providers: [
20 | { provide: SettingsService, useClass: MockSettings },
21 | ],
22 | schemas: [NO_ERRORS_SCHEMA]
23 | })
24 | .compileComponents();
25 | }));
26 |
27 | beforeEach(() => {
28 | fixture = TestBed.createComponent(ProfileSettingsComponent);
29 | component = fixture.componentInstance;
30 | fixture.detectChanges();
31 | });
32 |
33 | it('should create', () => {
34 | expect(component).toBeTruthy();
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/profile-settings/profile-settings.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { SettingsComponent } from '../prototypes/settings-component';
3 | import { SettingsService } from '../services/settings.service';
4 |
5 | @Component({
6 | selector: 'app-profile-settings',
7 | templateUrl: './profile-settings.component.html',
8 | styleUrls: ['./profile-settings.component.scss']
9 | })
10 | export class ProfileSettingsComponent extends SettingsComponent {
11 |
12 | private email = "";
13 | private name = "";
14 | getSettings() {
15 | this.email = this.settings.getAppSetting('profile-email');
16 | this.name = this.settings.getAppSetting('profile-name');
17 | }
18 | updateSettings() {
19 | this.settings.setSetting('profile-email', this.email);
20 | this.settings.setSetting('profile-name', this.name);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/prototypes/settings-component.ts:
--------------------------------------------------------------------------------
1 | import { SettingsService } from "../services/settings.service";
2 | import { OnInit, Component } from "@angular/core";
3 |
4 | @Component({})
5 | export abstract class SettingsComponent implements OnInit {
6 |
7 | constructor(
8 | protected settings: SettingsService
9 | ) {
10 | settings.settingsUpdated.subscribe(sett => {
11 | this.getSettings();
12 | });
13 | this.settings = settings;
14 | }
15 | abstract getSettings();
16 | ngOnInit(): void {
17 | this.getSettings();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/repo-profile/repo-profile.component.html:
--------------------------------------------------------------------------------
1 |
2 | Repository Profile
3 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/repo-profile/repo-profile.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/app/settings/repo-profile/repo-profile.component.scss
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/repo-profile/repo-profile.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { RepoProfileComponent } from './repo-profile.component';
4 | import { FormsModule } from '../../../../node_modules/@angular/forms';
5 | import { SettingsService } from '../services/settings.service';
6 | import { MockSettings } from '../mocks/mock-settings-service';
7 | import { NO_ERRORS_SCHEMA } from '../../../../node_modules/@angular/core';
8 |
9 | describe('RepoProfileComponent', () => {
10 | let component: RepoProfileComponent;
11 | let fixture: ComponentFixture;
12 |
13 | beforeEach(async(() => {
14 | TestBed.configureTestingModule({
15 | declarations: [ RepoProfileComponent ],
16 | imports: [
17 | FormsModule
18 | ],
19 | providers: [
20 | { provide: SettingsService, useClass: MockSettings },
21 | ],
22 | schemas: [NO_ERRORS_SCHEMA]
23 | })
24 | .compileComponents();
25 | }));
26 |
27 | beforeEach(() => {
28 | fixture = TestBed.createComponent(RepoProfileComponent);
29 | component = fixture.componentInstance;
30 | fixture.detectChanges();
31 | });
32 |
33 | it('should create', () => {
34 | expect(component).toBeTruthy();
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/repo-profile/repo-profile.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { SettingsComponent } from '../prototypes/settings-component';
3 |
4 | @Component({
5 | selector: 'app-repo-profile',
6 | templateUrl: './repo-profile.component.html',
7 | styleUrls: ['./repo-profile.component.scss']
8 | })
9 | export class RepoProfileComponent extends SettingsComponent {
10 |
11 | private repoProfile = false;
12 | private name = "";
13 | private email = "";
14 | getSettings() {
15 | this.repoProfile = this.settings.getRepoSetting('use-repo-profile');
16 | this.name = this.settings.getRepoSetting('profile-name');
17 | this.email = this.settings.getRepoSetting('profile-email');
18 | }
19 |
20 | updateEnableRepoProfile(enabled) {
21 | this.repoProfile = enabled;
22 | this.settings.setRepoSetting('use-repo-profile', enabled);
23 | if (!enabled) {
24 | this.setRepoProfile(undefined, undefined);
25 | } else {
26 | this.setRepoProfile(this.name, this.email);
27 | }
28 | }
29 |
30 | updateProfile() {
31 | this.setRepoProfile(this.name, this.email);
32 | }
33 |
34 | private setRepoProfile(name, email) {
35 | this.settings.setRepoSetting('profile-name', name);
36 | this.settings.setRepoSetting('profile-email', email);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/services/settings.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { SettingsService } from './settings.service';
4 | import { ElectronService } from '../../infrastructure/electron.service';
5 | import { MockElectron } from '../../infrastructure/mocks/mock-electron-service';
6 | import { SimpleNotificationsModule } from '../../../../node_modules/angular2-notifications';
7 |
8 | describe('SettingsService', () => {
9 | beforeEach(() => {
10 | TestBed.configureTestingModule({
11 | imports: [
12 | SimpleNotificationsModule.forRoot()
13 | ],
14 | providers: [
15 | SettingsService,
16 | { provide: ElectronService, useClass: MockElectron },
17 | ]
18 | });
19 | });
20 |
21 | it('should be created', inject([SettingsService], (service: SettingsService) => {
22 | expect(service).toBeTruthy();
23 | }));
24 | });
25 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/settings-nav/settings-nav.component.scss:
--------------------------------------------------------------------------------
1 | $primary-color: #FFF;
2 | $item-height: 46px;
3 | $width: 400px;
4 |
5 | :host{
6 | height: 100%;
7 | }
8 | .back-btn{
9 | cursor: pointer;
10 | }
11 |
12 | #setting-nav {
13 | position:relative;
14 | width: $width;
15 | height: 100%;
16 | flex-shrink: 0;
17 | background-color: var(--gray-dark);
18 | color: $primary-color;
19 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.8);
20 | overflow: visible;
21 | }
22 | .setting-option-container{
23 | width: 100%;
24 | padding: 10px 25px;
25 | height: $item-height;
26 | border-top: solid 1px #222;
27 | }
28 |
29 | .setting-separator{
30 | width: 100%;
31 | padding: 2px 10px;
32 | height: 26px;
33 | font-size: 14px;
34 | color: var(--gray);
35 |
36 | // box-shadow:
37 | // inset 0px 11px 8px -12px rgba(0, 0, 0, 0.8),
38 | // inset 0px -11px 8px -12px rgba(0, 0, 0, 0.8);
39 |
40 | background-color: rgba(0,0,0,0.2);
41 | border-top: solid 1px #222;
42 | }
43 |
44 | .action-button{
45 | cursor: pointer;
46 | &:hover{
47 | background: rgba(0, 0, 0, 0.2);
48 | }
49 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/settings-nav/settings-nav.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { SettingsNavComponent } from './settings-nav.component';
4 | import { RouterTestingModule } from '../../../../node_modules/@angular/router/testing';
5 | import { SettingsService } from '../services/settings.service';
6 | import { MockSettings } from '../mocks/mock-settings-service';
7 |
8 | describe('SettingsNavComponent', () => {
9 | let component: SettingsNavComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | imports: [
15 | RouterTestingModule
16 | ],
17 | declarations: [ SettingsNavComponent ],
18 | providers: [
19 | {provide: SettingsService, useClass: MockSettings}
20 | ]
21 | })
22 | .compileComponents();
23 | }));
24 |
25 | beforeEach(() => {
26 | fixture = TestBed.createComponent(SettingsNavComponent);
27 | component = fixture.componentInstance;
28 | fixture.detectChanges();
29 | });
30 |
31 | it('should create', () => {
32 | expect(component).toBeTruthy();
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/settings-nav/settings-nav.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Router } from '@angular/router';
3 | import { SettingsService } from '../services/settings.service';
4 |
5 | @Component({
6 | selector: 'app-settings-nav',
7 | templateUrl: './settings-nav.component.html',
8 | styleUrls: ['./settings-nav.component.scss']
9 | })
10 | export class SettingsNavComponent implements OnInit {
11 |
12 | private repoName: string = null;
13 | constructor(
14 | private route: Router,
15 | private settings: SettingsService
16 | ) {
17 | settings.settingsUpdated.subscribe(val => {
18 | this.repoName = val.current_repo.name;
19 | });
20 | if (this.settings.settingsData.current_repo) {
21 | this.repoName = settings.settingsData.current_repo.name;
22 | }
23 | }
24 |
25 | ngOnInit() {
26 | }
27 | goToGitView() {
28 | this.route.navigateByUrl('/');
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/settings-page/settings-page.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/settings-page/settings-page.component.scss:
--------------------------------------------------------------------------------
1 | :host{
2 | width: 100%;
3 | }
4 | .settings-container{
5 | width: 100%;
6 | height: 100%;
7 | }
8 |
9 | .setting-details-container{
10 | width: 100%;
11 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/settings-page/settings-page.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { SettingsPageComponent } from './settings-page.component';
4 | import { NO_ERRORS_SCHEMA } from '../../../../node_modules/@angular/core';
5 |
6 | describe('SettingsPageComponent', () => {
7 | let component: SettingsPageComponent;
8 | let fixture: ComponentFixture;
9 |
10 | beforeEach(async(() => {
11 | TestBed.configureTestingModule({
12 | declarations: [ SettingsPageComponent ],
13 | schemas: [NO_ERRORS_SCHEMA]
14 | })
15 | .compileComponents();
16 | }));
17 |
18 | beforeEach(() => {
19 | fixture = TestBed.createComponent(SettingsPageComponent);
20 | component = fixture.componentInstance;
21 | fixture.detectChanges();
22 | });
23 |
24 | it('should create', () => {
25 | expect(component).toBeTruthy();
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/settings-page/settings-page.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Router } from '@angular/router';
3 |
4 | @Component({
5 | selector: 'app-settings-page',
6 | templateUrl: './settings-page.component.html',
7 | styleUrls: ['./settings-page.component.scss']
8 | })
9 | export class SettingsPageComponent implements OnInit {
10 |
11 | constructor() { }
12 |
13 | ngOnInit() {
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/updater/updater.component.html:
--------------------------------------------------------------------------------
1 |
2 | Update
3 |
4 |
10 |
11 |
14 |
15 | Checking for update...
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/updater/updater.component.scss:
--------------------------------------------------------------------------------
1 | .loading-container {
2 | position: relative;
3 | width: 200px;
4 | text-align: center;
5 | }
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/updater/updater.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { UpdaterComponent } from './updater.component';
4 | import { UpdaterService } from '../../infrastructure/updater.service';
5 | import { MockUpdater } from '../../infrastructure/mocks/mock-updater-service';
6 | import { NO_ERRORS_SCHEMA } from '../../../../node_modules/@angular/core';
7 |
8 | describe('UpdaterComponent', () => {
9 | let component: UpdaterComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(async(() => {
13 | TestBed.configureTestingModule({
14 | declarations: [ UpdaterComponent ],
15 | providers: [
16 | {provide: UpdaterService, useClass: MockUpdater}
17 | ],
18 | schemas: [NO_ERRORS_SCHEMA]
19 | })
20 | .compileComponents();
21 | }));
22 |
23 | beforeEach(() => {
24 | fixture = TestBed.createComponent(UpdaterComponent);
25 | component = fixture.componentInstance;
26 | fixture.detectChanges();
27 | });
28 |
29 | it('should create', () => {
30 | expect(component).toBeTruthy();
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/app/frontend/src/app/settings/updater/updater.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { UpdaterService } from '../../infrastructure/updater.service';
3 |
4 | @Component({
5 | selector: 'app-updater',
6 | templateUrl: './updater.component.html',
7 | styleUrls: ['./updater.component.scss']
8 | })
9 | export class UpdaterComponent implements OnInit {
10 |
11 | checkingUpdate = false;
12 | updateAvailable = false;
13 | newVersion = "";
14 | constructor(
15 | private updater: UpdaterService
16 | ) {
17 | updater.updateChecking.subscribe(checking => {
18 | this.checkingUpdate = checking;
19 | });
20 | updater.updateAvailableChange.subscribe(ava => {
21 | this.updateAvailable = ava;
22 | if (ava) {
23 | this.newVersion = this.updater.updateVersion;
24 | }
25 | });
26 | this.updateAvailable = this.updater.isUpdateAvailable;
27 | this.newVersion = this.updater.updateVersion;
28 | }
29 |
30 | ngOnInit() {
31 | }
32 |
33 | checkUpdate() {
34 | this.updater.checkUpdate();
35 | }
36 | installUpdate() {
37 | this.updater.installUpdate();
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/app/frontend/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/.gitkeep
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Lato-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Lato-Black.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Lato-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Lato-BlackItalic.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Lato-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Lato-Bold.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Lato-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Lato-BoldItalic.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Lato-Hairline.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Lato-Hairline.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Lato-HairlineItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Lato-HairlineItalic.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Lato-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Lato-Italic.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Lato-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Lato-Light.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Lato-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Lato-LightItalic.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Lato-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Lato-Regular.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/NotoSans-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/NotoSans-Bold.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/NotoSans-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/NotoSans-BoldItalic.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/NotoSans-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/NotoSans-Italic.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/NotoSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/NotoSans-Regular.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Roboto-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Roboto-Black.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Roboto-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Roboto-BlackItalic.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Roboto-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Roboto-Bold.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Roboto-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Roboto-BoldItalic.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Roboto-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Roboto-Italic.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Roboto-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Roboto-Light.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Roboto-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Roboto-LightItalic.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Roboto-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Roboto-Medium.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Roboto-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Roboto-MediumItalic.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Roboto-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Roboto-Thin.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/Roboto-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/Roboto-ThinItalic.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/icomoon.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/icomoon.eot
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/icomoon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/icomoon.ttf
--------------------------------------------------------------------------------
/app/frontend/src/assets/fonts/icomoon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/fonts/icomoon.woff
--------------------------------------------------------------------------------
/app/frontend/src/assets/icheck/blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/icheck/blue.png
--------------------------------------------------------------------------------
/app/frontend/src/assets/icheck/blue@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/icheck/blue@2x.png
--------------------------------------------------------------------------------
/app/frontend/src/assets/release-note/MG0.2.0-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/release-note/MG0.2.0-1.gif
--------------------------------------------------------------------------------
/app/frontend/src/assets/release-note/MG0.2.0-2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/release-note/MG0.2.0-2.gif
--------------------------------------------------------------------------------
/app/frontend/src/assets/release-note/MG0.2.0-3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/release-note/MG0.2.0-3.gif
--------------------------------------------------------------------------------
/app/frontend/src/assets/release-note/MG0.2.0-4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/release-note/MG0.2.0-4.gif
--------------------------------------------------------------------------------
/app/frontend/src/assets/release-note/MG0.3.0-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/release-note/MG0.3.0-1.gif
--------------------------------------------------------------------------------
/app/frontend/src/assets/release-note/MG0.3.0-2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/release-note/MG0.3.0-2.gif
--------------------------------------------------------------------------------
/app/frontend/src/assets/release-note/MG0.3.0-3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/release-note/MG0.3.0-3.gif
--------------------------------------------------------------------------------
/app/frontend/src/assets/release-note/MG0.4.0-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/release-note/MG0.4.0-1.gif
--------------------------------------------------------------------------------
/app/frontend/src/assets/release-note/MG0.4.0-2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/release-note/MG0.4.0-2.gif
--------------------------------------------------------------------------------
/app/frontend/src/assets/release-note/MG0.4.0-3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/assets/release-note/MG0.4.0-3.gif
--------------------------------------------------------------------------------
/app/frontend/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/app/frontend/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `.angular-cli.json`.
5 |
6 | export const environment = {
7 | production: false
8 | };
9 |
--------------------------------------------------------------------------------
/app/frontend/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/frontend/src/favicon.ico
--------------------------------------------------------------------------------
/app/frontend/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Metro Git
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/frontend/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 | import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
7 | import 'angular2-notifications';
8 |
9 | if (environment.production) {
10 | enableProdMode();
11 | }
12 |
13 | platformBrowserDynamic().bootstrapModule(AppModule)
14 | .catch(err => console.log(err));
15 |
--------------------------------------------------------------------------------
/app/frontend/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/app/frontend/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "baseUrl": "./",
6 | "module": "es2015",
7 | "types": [
8 | "node"
9 | ]
10 | },
11 | "exclude": [
12 | "test.ts",
13 | "**/*.spec.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/app/frontend/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "baseUrl": "./",
6 | "module": "commonjs",
7 | "types": [
8 | "jasmine",
9 | "node"
10 | ]
11 | },
12 | "files": [
13 | "test.ts"
14 | ],
15 | "include": [
16 | "**/*.spec.ts",
17 | "**/*.d.ts"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/app/frontend/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | /* SystemJS module definition */
2 | declare var module: NodeModule;
3 | interface NodeModule {
4 | id: string;
5 | }
6 |
--------------------------------------------------------------------------------
/app/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist/out-tsc",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "target": "es5",
11 | "typeRoots": [
12 | "node_modules/@types"
13 | ],
14 | "lib": [
15 | "es2017",
16 | "dom"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/git/external-file-view.js:
--------------------------------------------------------------------------------
1 | const { ipcMain, BrowserWindow } = require('electron');
2 | const { requireArgParams } = require('../infrastructure/handler-helper');
3 | const url = require('url');
4 | const path = require('path');
5 | var fileWatch;
6 |
7 | ipcMain.on('Repo-OpenExternalFile', requireArgParams(open, ['file', 'commit']));
8 |
9 | function init(fw) {
10 | fileWatch = fw;
11 | }
12 |
13 | function open(event, arg) {
14 | let win = new BrowserWindow({});
15 | win.setMenu(null);
16 | let address = url.format({
17 | pathname: path.join(__dirname, '../frontend/dist/index.html'),
18 | hash: `/file/${arg.commit}`,
19 | protocol: 'file:',
20 | slashes: true,
21 | });
22 | win.webContents.once('did-finish-load', () => {
23 | fileWatch.getFileDetail(arg.file, arg.commit).then(result => {
24 | win.webContents.send('Repo-FileDetailRetrieved', result);
25 | })
26 | })
27 | win.loadURL(address);
28 | win.maximize();
29 | }
30 |
31 | module.exports = {
32 | init: init,
33 | }
--------------------------------------------------------------------------------
/app/git/repo-helpers.js:
--------------------------------------------------------------------------------
1 | function isSSH(url) {
2 | if (url.startsWith('http://') || url.startsWith('https://')) {
3 | return false;
4 | } else {
5 | return true;
6 | }
7 | }
8 |
9 | module.exports = {
10 | isSSH: isSSH
11 | }
--------------------------------------------------------------------------------
/app/git/repo-history.js:
--------------------------------------------------------------------------------
1 | const { ipcMain } = require('electron');
2 | const { requireArgParams } = require('../infrastructure/handler-helper');
3 |
4 | let settings;
5 | let window;
6 |
7 | ipcMain.on('Settings-Init', updateRepos);
8 | ipcMain.on('Repo-RemoveHistory', requireArgParams(removeHistory, ['workingDir']))
9 |
10 |
11 | function updateRepos(event, arg) {
12 | let repos = settings.getRepos();
13 | let repoHistory = repos.map(r => {
14 | return {
15 | name: r.name,
16 | path: r.workingDir
17 | }
18 | })
19 | window.webContents.send('Repo-HistoryChanged', {history: repoHistory});
20 | }
21 |
22 | function removeHistory(event, arg) {
23 | settings.removeRepo(arg.workingDir);
24 | updateRepos();
25 | }
26 |
27 | function init(sett, win){
28 | settings = sett;
29 | window = win;
30 | }
31 |
32 | module.exports = {
33 | init: init,
34 | updateRepos: updateRepos
35 | }
--------------------------------------------------------------------------------
/app/infrastructure/handler-helper.js:
--------------------------------------------------------------------------------
1 | function requireArgParams(wrapped, params){
2 | if(Array.isArray(params)) {
3 | return function() {
4 | let hasRequired = true;
5 | for(let i = 0; i < params.length; i++){
6 | if(arguments[1][params[i]] === undefined) {
7 | hasRequired = false;
8 | }
9 | }
10 | if(hasRequired) {
11 | return wrapped.apply(this, arguments);
12 | } else {
13 | return undefined;
14 | }
15 | }
16 | } else {
17 | return function(){
18 | return wrapped.apply(this, arguments);
19 | }
20 | }
21 | }
22 |
23 | module.exports = {
24 | requireArgParams: requireArgParams
25 | }
--------------------------------------------------------------------------------
/app/infrastructure/release-note.js:
--------------------------------------------------------------------------------
1 | const { ipcMain, BrowserWindow } = require('electron');
2 | const { requireArgParams } = require('../infrastructure/handler-helper');
3 | const url = require('url');
4 | const path = require('path');
5 | var settings;
6 |
7 | function init(sett, win) {
8 | settings = sett;
9 | if(!settings.get('show-release-note')) {
10 | settings.update('show-release-note', true);
11 | win.webContents.once('did-finish-load', () => {
12 | openReleaseNote();
13 | })
14 | }
15 | }
16 |
17 | function openReleaseNote(){
18 | let win = new BrowserWindow({});
19 | win.setMenu(null);
20 | let address = url.format({
21 | pathname: path.join(__dirname, '../frontend/dist/index.html'),
22 | hash: `/release-note`,
23 | protocol: 'file:',
24 | slashes: true,
25 | });
26 | win.loadURL(address);
27 | win.maximize();
28 | }
29 |
30 | function openAboutPage() {
31 | let win = new BrowserWindow({});
32 | win.setMenu(null);
33 | let address = url.format({
34 | pathname: path.join(__dirname, '../frontend/dist/index.html'),
35 | hash: `/about`,
36 | protocol: 'file:',
37 | slashes: true,
38 | });
39 | win.loadURL(address);
40 | win.maximize();
41 | }
42 |
43 | module.exports = {
44 | init: init,
45 | openReleaseNote: openReleaseNote,
46 | openAboutPage: openAboutPage
47 | }
--------------------------------------------------------------------------------
/app/infrastructure/shell.js:
--------------------------------------------------------------------------------
1 | const { shell, ipcMain } = require('electron');
2 |
3 | ipcMain.on('Shell-Open', (event, arg) => {
4 | openUrl(arg.url);
5 | });
6 |
7 | function init() {
8 |
9 | }
10 |
11 | function openUrl(url) {
12 | shell.openExternal(url);
13 | }
14 |
15 | module.exports = {
16 | init: init
17 | }
--------------------------------------------------------------------------------
/app/visual/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/.DS_Store
--------------------------------------------------------------------------------
/app/visual/Icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/Icon-1024.png
--------------------------------------------------------------------------------
/app/visual/Icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/Icon-128.png
--------------------------------------------------------------------------------
/app/visual/Icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/Icon-16.png
--------------------------------------------------------------------------------
/app/visual/Icon-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/Icon-256.png
--------------------------------------------------------------------------------
/app/visual/Icon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/Icon-32.png
--------------------------------------------------------------------------------
/app/visual/Icon-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/Icon-48.png
--------------------------------------------------------------------------------
/app/visual/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/Icon-512.png
--------------------------------------------------------------------------------
/app/visual/Icon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/Icon-64.png
--------------------------------------------------------------------------------
/app/visual/Icon.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/Icon.ai
--------------------------------------------------------------------------------
/app/visual/Icon16.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/Icon16.ai
--------------------------------------------------------------------------------
/app/visual/Icon48.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/Icon48.ai
--------------------------------------------------------------------------------
/app/visual/MacOS/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/MacOS/.DS_Store
--------------------------------------------------------------------------------
/app/visual/MacOS/metrogit.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/MacOS/metrogit.icns
--------------------------------------------------------------------------------
/app/visual/MacOS/metrogit.iconset/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/MacOS/metrogit.iconset/.DS_Store
--------------------------------------------------------------------------------
/app/visual/MacOS/metrogit.iconset/icon_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/MacOS/metrogit.iconset/icon_128x128.png
--------------------------------------------------------------------------------
/app/visual/MacOS/metrogit.iconset/icon_128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/MacOS/metrogit.iconset/icon_128x128@2x.png
--------------------------------------------------------------------------------
/app/visual/MacOS/metrogit.iconset/icon_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/MacOS/metrogit.iconset/icon_16x16.png
--------------------------------------------------------------------------------
/app/visual/MacOS/metrogit.iconset/icon_16x16@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/MacOS/metrogit.iconset/icon_16x16@2x.png
--------------------------------------------------------------------------------
/app/visual/MacOS/metrogit.iconset/icon_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/MacOS/metrogit.iconset/icon_256x256.png
--------------------------------------------------------------------------------
/app/visual/MacOS/metrogit.iconset/icon_256x256@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/MacOS/metrogit.iconset/icon_256x256@2x.png
--------------------------------------------------------------------------------
/app/visual/MacOS/metrogit.iconset/icon_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/MacOS/metrogit.iconset/icon_32x32.png
--------------------------------------------------------------------------------
/app/visual/MacOS/metrogit.iconset/icon_32x32@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/MacOS/metrogit.iconset/icon_32x32@2x.png
--------------------------------------------------------------------------------
/app/visual/MacOS/metrogit.iconset/icon_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/MacOS/metrogit.iconset/icon_512x512.png
--------------------------------------------------------------------------------
/app/visual/MacOS/metrogit.iconset/icon_512x512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/MacOS/metrogit.iconset/icon_512x512@2x.png
--------------------------------------------------------------------------------
/app/visual/_variables.scss:
--------------------------------------------------------------------------------
1 |
2 | $blue: #007bff;
3 | $cyan: #17a2b8;
4 | $gray-100: #f8f9fa;
5 | $light: $gray-100;
6 | $body-bg: #222f3d;
7 | $body-color: $light;
8 | $font-family-sans-serif: Lato, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
9 | $primary: $blue;
--------------------------------------------------------------------------------
/app/visual/appveyor.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/visual/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/app/visual/icon.ico
--------------------------------------------------------------------------------
/app/visual/station-dot.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | cd app/frontend
2 | ng build --prod --aot=false
3 | cd ../..
4 | yarn dist
5 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | cd app/frontend
2 | ng build --prod --aot=false
3 | cd ../..
4 | yarn dist
5 |
--------------------------------------------------------------------------------
/build/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/build/.DS_Store
--------------------------------------------------------------------------------
/build/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/build/icon.icns
--------------------------------------------------------------------------------
/build/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/build/icon.ico
--------------------------------------------------------------------------------
/build/license_en.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Ming-Hung (Michael) Lu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/dev-app-update.yml:
--------------------------------------------------------------------------------
1 | owner: Yamazaki93
2 | repo: MetroGit
3 | provider: github
4 |
--------------------------------------------------------------------------------
/install-dep.sh:
--------------------------------------------------------------------------------
1 | yarn install
2 | cd app/frontend
3 | yarn install
--------------------------------------------------------------------------------
/misc/metrogit.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/misc/metrogit.gif
--------------------------------------------------------------------------------
/misc/metrogit1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/misc/metrogit1.PNG
--------------------------------------------------------------------------------
/misc/metrogit2.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/misc/metrogit2.PNG
--------------------------------------------------------------------------------
/misc/metrogit3.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/misc/metrogit3.PNG
--------------------------------------------------------------------------------
/misc/metrogit4.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/misc/metrogit4.PNG
--------------------------------------------------------------------------------
/misc/metrogit5.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yamazaki93/MetroGit/d796975e4b2530557075f24cb7e1e18c26bcaf76/misc/metrogit5.PNG
--------------------------------------------------------------------------------
/publish-linux.sh:
--------------------------------------------------------------------------------
1 | cd app/frontend
2 | ng build --prod --aot=false
3 | cd ../..
4 |
5 | electron-builder build --linux --publish always
6 |
--------------------------------------------------------------------------------
/publish.ps1:
--------------------------------------------------------------------------------
1 | cd app/frontend
2 | ng build --prod --aot=false
3 | cd ../..
4 |
5 | electron-builder build --publish always
6 |
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | cd app/frontend
2 | ng test --config=karma-ci.conf.js
3 |
--------------------------------------------------------------------------------