├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ ├── main.yml │ ├── manual-all.yml │ ├── manual-one.yml │ ├── nightly.yml │ ├── notify.yml │ ├── release.yml │ └── scan-release.yml ├── .gitignore ├── .idea ├── .gitignore ├── flowkeeper-python.iml ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── vcs.xml └── watcherTasks.xml ├── LICENSE ├── README.md ├── TODO.md ├── desktop.bin ├── doc ├── actions.md ├── build-alpine.md ├── data-model.md ├── design.md ├── events.md ├── fk-pipeline.svg ├── fk-simple.png ├── pipeline.md ├── release.md └── strategies.md ├── flowkeeper-1024.png ├── requirements-test.txt ├── requirements.txt ├── res ├── CHANGELOG.txt ├── CREDITS.txt ├── LICENSE.txt ├── NotoSans.ttf ├── about.ui ├── core.ui ├── flowkeeper.ico ├── flowkeeper.png ├── icons │ ├── dark │ │ ├── 24x24 │ │ │ ├── conn-offline.svg │ │ │ ├── conn-online.svg │ │ │ ├── conn-unknown.svg │ │ │ ├── pomodoro-finished-planned.svg │ │ │ ├── pomodoro-finished-unplanned.svg │ │ │ ├── pomodoro-new-planned.svg │ │ │ ├── pomodoro-new-unplanned.svg │ │ │ ├── pomodoro-running-planned.svg │ │ │ ├── pomodoro-running-unplanned.svg │ │ │ ├── pomodoro-voided.svg │ │ │ ├── tool-add-pomodoro.svg │ │ │ ├── tool-add-prefilled.svg │ │ │ ├── tool-add.svg │ │ │ ├── tool-backlogs.svg │ │ │ ├── tool-complete-item.svg │ │ │ ├── tool-delete.svg │ │ │ ├── tool-filter-off.svg │ │ │ ├── tool-filter-on.svg │ │ │ ├── tool-finish-tracking.svg │ │ │ ├── tool-focus-complete.svg │ │ │ ├── tool-focus-next.svg │ │ │ ├── tool-interruption.svg │ │ │ ├── tool-left-close.svg │ │ │ ├── tool-left-open.svg │ │ │ ├── tool-next.svg │ │ │ ├── tool-note.svg │ │ │ ├── tool-pin.svg │ │ │ ├── tool-remove-pomodoro.svg │ │ │ ├── tool-rename.svg │ │ │ ├── tool-search.svg │ │ │ ├── tool-settings.svg │ │ │ ├── tool-show-all.svg │ │ │ ├── tool-show-timer-only.svg │ │ │ ├── tool-start-item.svg │ │ │ ├── tool-teams.svg │ │ │ ├── tool-unpin.svg │ │ │ └── tool-void.svg │ │ └── index.theme │ ├── info.png │ ├── light │ │ ├── 24x24 │ │ │ ├── conn-offline.svg │ │ │ ├── conn-online.svg │ │ │ ├── conn-unknown.svg │ │ │ ├── pomodoro-finished-planned.svg │ │ │ ├── pomodoro-finished-unplanned.svg │ │ │ ├── pomodoro-new-planned.svg │ │ │ ├── pomodoro-new-unplanned.svg │ │ │ ├── pomodoro-running-planned.svg │ │ │ ├── pomodoro-running-unplanned.svg │ │ │ ├── pomodoro-voided.svg │ │ │ ├── tool-add-pomodoro.svg │ │ │ ├── tool-add-prefilled.svg │ │ │ ├── tool-add.svg │ │ │ ├── tool-backlogs.svg │ │ │ ├── tool-complete-item.svg │ │ │ ├── tool-delete.svg │ │ │ ├── tool-filter-off.svg │ │ │ ├── tool-filter-on.svg │ │ │ ├── tool-finish-tracking.svg │ │ │ ├── tool-focus-complete.svg │ │ │ ├── tool-focus-next.svg │ │ │ ├── tool-interruption.svg │ │ │ ├── tool-left-close.svg │ │ │ ├── tool-left-open.svg │ │ │ ├── tool-next.svg │ │ │ ├── tool-note.svg │ │ │ ├── tool-pin.svg │ │ │ ├── tool-remove-pomodoro.svg │ │ │ ├── tool-rename.svg │ │ │ ├── tool-search.svg │ │ │ ├── tool-settings.svg │ │ │ ├── tool-show-all.svg │ │ │ ├── tool-show-timer-only.svg │ │ │ ├── tool-start-item.svg │ │ │ ├── tool-teams.svg │ │ │ ├── tool-unpin.svg │ │ │ └── tool-void.svg │ │ └── index.theme │ ├── mixed │ │ ├── 24x24 │ │ │ ├── conn-offline.svg │ │ │ ├── conn-online.svg │ │ │ ├── conn-unknown.svg │ │ │ ├── pomodoro-finished-planned.svg │ │ │ ├── pomodoro-finished-unplanned.svg │ │ │ ├── pomodoro-new-planned.svg │ │ │ ├── pomodoro-new-unplanned.svg │ │ │ ├── pomodoro-running-planned.svg │ │ │ ├── pomodoro-running-unplanned.svg │ │ │ ├── pomodoro-voided.svg │ │ │ ├── tool-add-pomodoro.svg │ │ │ ├── tool-add-prefilled.svg │ │ │ ├── tool-add.svg │ │ │ ├── tool-backlogs.svg │ │ │ ├── tool-complete-item.svg │ │ │ ├── tool-delete.svg │ │ │ ├── tool-filter-off.svg │ │ │ ├── tool-filter-on.svg │ │ │ ├── tool-finish-tracking.svg │ │ │ ├── tool-focus-complete.svg │ │ │ ├── tool-focus-next.svg │ │ │ ├── tool-interruption.svg │ │ │ ├── tool-left-close.svg │ │ │ ├── tool-left-open.svg │ │ │ ├── tool-note.svg │ │ │ ├── tool-pin.svg │ │ │ ├── tool-remove-pomodoro.svg │ │ │ ├── tool-rename.svg │ │ │ ├── tool-search.svg │ │ │ ├── tool-settings.svg │ │ │ ├── tool-show-all.svg │ │ │ ├── tool-show-timer-only.svg │ │ │ ├── tool-start-item.svg │ │ │ ├── tool-teams.svg │ │ │ ├── tool-unpin.svg │ │ │ └── tool-void.svg │ │ └── index.theme │ ├── triangle-down.svg │ ├── triangle-up.svg │ └── warning.png ├── img │ └── bg.jpg ├── sound │ ├── Madelene.m4a │ ├── bell.wav │ └── tick.wav ├── stats.ui ├── style-beach.json ├── style-dark.json ├── style-desert.json ├── style-highlight.json ├── style-light.json ├── style-lime.json ├── style-mixed.json ├── style-motel.json ├── style-purple.json ├── style-resort.json ├── style-template.qss ├── style-terra.json └── summary.ui ├── run-tests.sh ├── run.sh ├── scripts ├── README.md ├── bsd │ └── README.md ├── common │ ├── download-release.sh │ ├── generate-resources.sh │ ├── get-version.sh │ ├── pyinstaller │ │ ├── entitlements.plist │ │ ├── normal.spec │ │ └── portable.spec │ └── upload-release.sh ├── linux │ ├── appimage │ │ ├── install-appimage.sh │ │ └── package-appimage.sh │ ├── common │ │ ├── flowkeeper │ │ ├── flowkeeper.desktop │ │ └── org.flowkeeper.Flowkeeper.metainfo.xml │ ├── debian │ │ ├── debian-control │ │ ├── debian-control-min │ │ ├── package-deb-min.sh │ │ └── package-deb.sh │ ├── flatpak │ │ ├── org.flowkeeper.Flowkeeper.yaml │ │ └── python3-modules.yaml │ ├── obs │ │ └── obs.spec │ ├── package-nuitka.sh │ └── rpm │ │ └── package-rpm-min.sh ├── macos │ ├── create-dmg.sh │ ├── create-icons.sh │ ├── install-certificates.sh │ ├── install-create-dmg.sh │ ├── notarize-dmg.sh │ ├── package-macos-pkg.sh │ └── package-nuitka.sh └── windows │ ├── generate-resources-windows.sh │ ├── install-innosetup.sh │ ├── package-installer.sh │ ├── requirements-vt.txt │ ├── vtscan.py │ └── windows-installer.iss ├── src └── fk │ ├── __init__.py │ ├── core │ ├── __init__.py │ ├── abstract_cryptograph.py │ ├── abstract_data_container.py │ ├── abstract_data_item.py │ ├── abstract_event_emitter.py │ ├── abstract_event_source.py │ ├── abstract_filesystem_watcher.py │ ├── abstract_serializer.py │ ├── abstract_settings.py │ ├── abstract_strategy.py │ ├── abstract_timer.py │ ├── abstract_timer_display.py │ ├── backlog.py │ ├── backlog_strategies.py │ ├── ephemeral_event_source.py │ ├── event_source_factory.py │ ├── event_source_holder.py │ ├── events.py │ ├── fernet_cryptograph.py │ ├── file_event_source.py │ ├── import_export.py │ ├── integration_executor.py │ ├── interruption.py │ ├── mock_settings.py │ ├── no_cryptograph.py │ ├── pomodoro.py │ ├── pomodoro_strategies.py │ ├── sandbox.py │ ├── simple_serializer.py │ ├── strategy_factory.py │ ├── tag.py │ ├── tags.py │ ├── tenant.py │ ├── timer.py │ ├── timer_data.py │ ├── timer_strategies.py │ ├── user.py │ ├── user_strategies.py │ ├── workitem.py │ └── workitem_strategies.py │ ├── desktop │ ├── __init__.py │ ├── application.py │ ├── config_wizard.py │ ├── desktop.py │ ├── desktop_strategies.py │ ├── export_wizard.py │ ├── import_wizard.py │ ├── settings.py │ ├── stats_window.py │ ├── tutorial.py │ └── work_summary_window.py │ ├── e2e │ ├── __init__.py │ ├── abstract_e2e_test.py │ ├── all-tests.json │ ├── backlog_e2e.py │ ├── screenshot.py │ └── screenshots_e2e.py │ ├── qt │ ├── __init__.py │ ├── about_window.py │ ├── abstract_drop_model.py │ ├── abstract_tableview.py │ ├── actions.py │ ├── app_version.py │ ├── audio_player.py │ ├── backlog_model.py │ ├── backlog_tableview.py │ ├── backlog_widget.py │ ├── configurable_toolbar.py │ ├── connection_widget.py │ ├── flow_layout.py │ ├── focus_widget.py │ ├── heartbeat.py │ ├── info_overlay.py │ ├── oauth.py │ ├── pomodoro_delegate.py │ ├── progress_widget.py │ ├── qt_filesystem_watcher.py │ ├── qt_invoker.py │ ├── qt_settings.py │ ├── qt_timer.py │ ├── render │ │ ├── __init__.py │ │ ├── abstract_timer_renderer.py │ │ ├── classic_timer_renderer.py │ │ └── minimal_timer_renderer.py │ ├── resize_event_filter.py │ ├── search_completer.py │ ├── tags_widget.py │ ├── theme_change_event_filter.py │ ├── threaded_event_source.py │ ├── timer_widget.py │ ├── tray_icon.py │ ├── user_model.py │ ├── user_tableview.py │ ├── websocket_event_source.py │ ├── workitem_model.py │ ├── workitem_tableview.py │ ├── workitem_text_delegate.py │ └── workitem_widget.py │ ├── tests │ ├── __init__.py │ ├── abstract_test_case.py │ ├── data_generator.py │ ├── fixtures │ │ ├── random-dump.txt │ │ ├── random.txt │ │ └── test-tags.txt │ ├── test_backlogs.py │ ├── test_events.py │ ├── test_file_event_source.py │ ├── test_import_export.py │ ├── test_pomodoros.py │ ├── test_settings.py │ ├── test_tags.py │ ├── test_users.py │ ├── test_utils.py │ └── test_workitems.py │ └── tools │ ├── __init__.py │ ├── cli.py │ ├── minimal_actions.py │ ├── minimal_audio.py │ ├── minimal_auth.py │ ├── minimal_backlogs.py │ ├── minimal_common.py │ ├── minimal_focus.py │ ├── minimal_settings.py │ ├── minimal_timer_widget.py │ ├── minimal_tray.py │ ├── minimal_tutorial.py │ ├── minimal_update.py │ ├── minimal_users.py │ └── minimal_workitems.py └── ws-tests.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: flowkeeper-org 4 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Tests and checks 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | push: 8 | branches: 9 | - main 10 | 11 | permissions: 12 | checks: write 13 | 14 | jobs: 15 | run-tests: 16 | runs-on: ubuntu-22.04 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | - name: Setup Python 21 | uses: actions/setup-python@v5 22 | with: 23 | python-version: '3.11' 24 | - name: Create Virtual Environment 25 | run: | 26 | python3 -m venv venv 27 | source venv/bin/activate 28 | - name: Install dependencies 29 | run: | 30 | pip install -r requirements-test.txt 31 | - name: Generate resources 32 | run: | 33 | ./generate-resources.sh 34 | - name: Run unit tests for fk.core 35 | run: | 36 | PYTHONPATH=src coverage run -m xmlrunner -o test-results discover -v fk.tests 37 | - name: Publish test report 38 | uses: mikepenz/action-junit-report@v4 39 | with: 40 | report_paths: 'test-results/TEST-*.xml' 41 | include_passed: true 42 | - name: Upload code coverage to coveralls.io 43 | run: coveralls --service=github 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | - name: Trigger e2e tests (external) 47 | run: | 48 | curl --version 49 | 50 | remote-pipeline: 51 | runs-on: ubuntu-22.04 52 | needs: run-tests 53 | steps: 54 | - name: Trigger remote job 55 | env: 56 | REMOTE_URL: ${{ secrets.REMOTE_URL }} 57 | REMOTE_USER_TOKEN: ${{ secrets.REMOTE_USER_TOKEN }} 58 | REMOTE_JOB_TOKEN: ${{ secrets.REMOTE_MAIN_JOB_TOKEN }} 59 | run: | 60 | curl -I -u "github-trigger:$REMOTE_USER_TOKEN" "$REMOTE_URL/job/gh-main/build?token=$REMOTE_JOB_TOKEN" 2>/dev/null > /dev/null 61 | -------------------------------------------------------------------------------- /.github/workflows/manual-all.yml: -------------------------------------------------------------------------------- 1 | name: Manual build - all 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | call-build: 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | os: [ubuntu-24.04, ubuntu-22.04, ubuntu-24.04-arm, ubuntu-22.04-arm, macos-15, macos-14, macos-13, windows-2025, windows-2022, windows-2019] 12 | compiler: [nuitka, pyinstaller] 13 | exclude: 14 | - os: macos-13 15 | compiler: nuitka 16 | uses: flowkeeper-org/fk-desktop/.github/workflows/build.yml@rc-0.10.0 17 | with: 18 | os: ${{ matrix.os }} 19 | compiler: ${{ matrix.compiler }} 20 | secrets: inherit 21 | -------------------------------------------------------------------------------- /.github/workflows/manual-one.yml: -------------------------------------------------------------------------------- 1 | name: Manual build - one 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | os: 7 | description: 'Operating system' 8 | required: true 9 | default: 'ubuntu-24.04' 10 | type: choice 11 | options: 12 | - ubuntu-24.04 13 | - ubuntu-22.04 14 | - ubuntu-24.04-arm 15 | - ubuntu-22.04-arm 16 | - macos-15 17 | - macos-14 18 | - macos-13 19 | - windows-2025 20 | - windows-2022 21 | - windows-2019 22 | compiler: 23 | description: 'Compiler' 24 | required: true 25 | default: 'nuitka' 26 | type: choice 27 | options: 28 | - nuitka 29 | - pyinstaller 30 | 31 | jobs: 32 | call-build: 33 | uses: flowkeeper-org/fk-desktop/.github/workflows/build.yml@rc-0.10.0 34 | with: 35 | os: ${{ inputs.os }} 36 | compiler: ${{ inputs.compiler }} 37 | secrets: inherit 38 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | name: Nightly job 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * *" 6 | 7 | jobs: 8 | call-scan: 9 | uses: flowkeeper-org/fk-desktop/.github/workflows/scan-release.yml 10 | with: 11 | release: '' 12 | secrets: inherit 13 | permissions: 14 | contents: write 15 | -------------------------------------------------------------------------------- /.github/workflows/notify.yml: -------------------------------------------------------------------------------- 1 | name: Notify 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | text: 7 | required: true 8 | default: 'Hello, World!' 9 | type: string 10 | 11 | jobs: 12 | notify: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Post message 16 | uses: slackapi/slack-github-action@v2.0.0 17 | with: 18 | method: chat.postMessage 19 | token: ${{ secrets.SLACK_BOT_TOKEN }} 20 | payload: | 21 | channel: ${{ secrets.SLACK_CHANNEL_ID }} 22 | text: "${{ inputs.text }}" 23 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release new version 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | 8 | permissions: 9 | contents: write 10 | checks: write 11 | 12 | jobs: 13 | call-build: 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | os: [ubuntu-24.04, ubuntu-22.04, ubuntu-24.04-arm, ubuntu-22.04-arm, macos-15, macos-14, macos-13, windows-2025, windows-2022, windows-2019] 18 | compiler: [nuitka, pyinstaller] 19 | exclude: 20 | - os: macos-13 21 | compiler: nuitka 22 | uses: flowkeeper-org/fk-desktop/.github/workflows/build.yml@rc-0.10.0 23 | with: 24 | os: ${{ matrix.os }} 25 | compiler: ${{ matrix.compiler }} 26 | secrets: inherit 27 | 28 | release: 29 | needs: call-build 30 | strategy: 31 | fail-fast: false 32 | matrix: 33 | os: [ubuntu-24.04, ubuntu-22.04, ubuntu-24.04-arm, ubuntu-22.04-arm, macos-15, macos-14, macos-13, windows-2025, windows-2022, windows-2019] 34 | compiler: [nuitka, pyinstaller] 35 | exclude: 36 | - os: macos-13 37 | compiler: nuitka 38 | runs-on: ubuntu-latest 39 | steps: 40 | - name: Download artifacts 41 | uses: actions/download-artifact@v4 42 | with: 43 | name: dist-${{ matrix.os }}-${{ matrix.compiler }}-all 44 | path: . 45 | - name: Release 46 | uses: softprops/action-gh-release@v1 47 | with: 48 | files: "./*" 49 | -------------------------------------------------------------------------------- /.github/workflows/scan-release.yml: -------------------------------------------------------------------------------- 1 | name: Scan release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | release: 7 | description: 'Release to scan, e.g. v0.10.0. Empty means latest.' 8 | required: false 9 | default: '' 10 | type: string 11 | workflow_call: 12 | inputs: 13 | release: 14 | description: 'Release to scan, e.g. v0.10.0. Empty means latest.' 15 | required: true 16 | default: '' 17 | type: string 18 | secrets: 19 | VTCLI_APIKEY: 20 | required: true 21 | SLACK_BOT_TOKEN: 22 | required: true 23 | SLACK_CHANNEL_ID: 24 | required: true 25 | 26 | jobs: 27 | scan: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - name: Checkout repository 31 | uses: actions/checkout@v4 32 | 33 | - name: Download binaries 34 | shell: bash 35 | env: 36 | GH_TOKEN: ${{ github.token }} 37 | RELEASE: ${{ inputs.release }} 38 | run: scripts/common/download-release.sh 39 | 40 | - name: Setup Python 41 | uses: actions/setup-python@v5 42 | with: 43 | python-version: '3.11' 44 | 45 | - name: Scan binaries 46 | shell: bash 47 | env: 48 | VTCLI_APIKEY: ${{ secrets.VTCLI_APIKEY }} 49 | run: | 50 | pip install vt-py 51 | SCAN_STATUS=$(python3 scripts/windows/vtscan.py downloads/*.exe) 52 | echo "$SCAN_STATUS" 53 | { 54 | echo 'SCAN_STATUS<> $GITHUB_ENV 58 | 59 | - name: Save scan results as an artifact 60 | uses: actions/upload-artifact@v4 61 | with: 62 | name: vtscan-results 63 | path: | 64 | vtscan-results-all.json 65 | vtscan-results-warnings.json 66 | 67 | - name: Post message 68 | uses: slackapi/slack-github-action@v2.0.0 69 | with: 70 | method: chat.postMessage 71 | token: ${{ secrets.SLACK_BOT_TOKEN }} 72 | payload: | 73 | channel: ${{ secrets.SLACK_CHANNEL_ID }} 74 | text: "${{ env.SCAN_STATUS }}" 75 | 76 | upload: 77 | # Making it a standalone job to limit security its perimeter 78 | runs-on: ubuntu-latest 79 | needs: scan 80 | permissions: 81 | contents: write 82 | steps: 83 | - name: Checkout repository 84 | uses: actions/checkout@v4 85 | 86 | - name: Download artifacts 87 | uses: actions/download-artifact@v4 88 | with: 89 | name: vtscan-results 90 | path: . 91 | 92 | - name: Upload scan results back to release 93 | shell: bash 94 | env: 95 | GH_TOKEN: ${{ github.token }} 96 | RELEASE: ${{ inputs.release }} 97 | run: scripts/common/upload-release.sh vtscan-results-all.json vtscan-results-warnings.json 98 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv 2 | venv 3 | venv* 4 | __pycache__ 5 | build 6 | dist 7 | .coverage 8 | .DS_Store 9 | htmlcov 10 | src/fk/desktop/resources.py 11 | test-results 12 | Flowkeeper.dmg 13 | notary-key.txt 14 | flowkeeper.icns 15 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/flowkeeper-python.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/watcherTasks.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 24 | 25 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # To do before release 2 | 3 | 1. "Compress" doesn't support long breaks 4 | 2. Test if "Repair" and "Import" support everything correctly. Create unit tests for all of those. 5 | 3. Labels in Settings are not tall enough on Windows 6 | 4. Fonts -- backlogs use default font 7 | 5. Fonts -- status uses default font 8 | 6. Fonts -- focus mode uses default font. Default focus becomes the same after double-clicking. 9 | 7. Tray icon doesn't work after changing the flavor while running a pomodoro 10 | 8. Backlogs toggle icon doesn't change its color on theme change 11 | 9. Rows height doesn't recalculate on fonts change 12 | 13 | # Tests 14 | 15 | ## Windows binaries 16 | 17 | 1. No sound on Windows with Nuitka 18 | 2. "standalone" directory in ZIPs 19 | 3. Sign binaries in standalone ZIPs and repack 20 | 21 | ## Linux binaries 22 | 23 | ### AppImage 24 | 25 | ### Flatpak 26 | 27 | ## macOS binaries 28 | 29 | 1. On Ventura 13 / x86 and ARM, both Nuitka and PyInstaller -- no signature 30 | 2. "Too many values to unpack" when launching Settings, even after settings reset 31 | 3. No sound for Nuitka, same as Windows 32 | -------------------------------------------------------------------------------- /desktop.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/desktop.bin -------------------------------------------------------------------------------- /doc/actions.md: -------------------------------------------------------------------------------- 1 | # UI: Actions 2 | 3 | ## Application 4 | - ('application.settings', "Settings", 'F10', None, Application.show_settings_dialog) 5 | - ('application.quit', "Quit", 'Ctrl+Q', None, Application.quit_local) 6 | - ('application.import', "Import data...", 'Ctrl+I', None, Application.show_import_wizard) 7 | - ('application.export', "Export data...", 'Ctrl+E', None, Application.show_export_wizard) 8 | - ('application.about', "About", '', None, Application.show_about) 9 | 10 | ## BacklogTableView 11 | - ('backlogs_table.newBacklog', "New Backlog", 'Ctrl+N', None, BacklogTableView.create_backlog) 12 | - ('backlogs_table.renameBacklog', "Rename Backlog", 'Ctrl+R', None, BacklogTableView.rename_selected_backlog) 13 | - ('backlogs_table.deleteBacklog', "Delete Backlog", 'F8', None, BacklogTableView.delete_selected_backlog) 14 | - ('backlogs_table.newBacklogFromIncomplete', "New Backlog From Incomplete", 'Ctrl+M', "tool-add-prefilled", BacklogTableView.create_backlog_from_incomplete) 15 | 16 | ## WorkitemTableView 17 | - ('workitems_table.newItem', "New Item", 'Ins', None, WorkitemTableView.create_workitem) 18 | - ('workitems_table.renameItem', "Rename Item", 'F6', None, WorkitemTableView.rename_selected_workitem) 19 | - ('workitems_table.deleteItem', "Delete Item", 'Del', None, WorkitemTableView.delete_selected_workitem) 20 | - ('workitems_table.startItem', "Start Item", 'Ctrl+S', 'tool-next', WorkitemTableView.start_selected_workitem) 21 | - ('workitems_table.completeItem', "Complete Item", 'Ctrl+P', 'tool-complete', WorkitemTableView.complete_selected_workitem) 22 | - ('workitems_table.addPomodoro', "Add Pomodoro", 'Ctrl++', None, WorkitemTableView.add_pomodoro) 23 | - ('workitems_table.removePomodoro', "Remove Pomodoro", 'Ctrl+-', None, WorkitemTableView.remove_pomodoro) 24 | - ('workitems_table.hideCompleted', "Hide Completed Items", '', None, WorkitemTableView._toggle_hide_completed_workitems, True, True) 25 | 26 | ## FocusWidget 27 | - ('focus.voidPomodoro', "Void Pomodoro", 'Ctrl+V', "tool-void", FocusWidget._void_pomodoro) 28 | - ('focus.nextPomodoro', "Next Pomodoro", None, "tool-next", FocusWidget._next_pomodoro) 29 | - ('focus.completeItem', "Complete Item", None, "tool-complete", FocusWidget._complete_item) 30 | 31 | ## MainWindow 32 | - ('window.focusMode', "Focus Mode", None, "tool-show-timer-only", MainWindow.toggle_focus_mode) 33 | - ('window.showMainWindow', "Show Main Window", None, "tool-show-timer-only", MainWindow.show_window) 34 | - ('window.showBacklogs', "Backlogs", 'Ctrl+B', 'tool-backlogs', MainWindow.show_about) 35 | - ('window.showUsers', "Team", 'Ctrl+T', 'tool-teams', MainWindow.toggle_users) 36 | - ('window.showSearch', "Search...", 'Ctrl+F', '', MainWindow.show_search) 37 | -------------------------------------------------------------------------------- /doc/build-alpine.md: -------------------------------------------------------------------------------- 1 | # Building for Alpine Linux 2 | 3 | Flowkeeper's CI pipeline runs PyInstaller on Ubuntu and thus generates binaries which rely on glibc. 4 | Alpine is based on musl, so you'd get "symbol not found" errors in runtime if you try to run any of the 5 | "official" binaries. 6 | 7 | You can still use Flowkeeper with Alpine. We tested it with the edge release + Xfce. Instructions: 8 | 9 | 1. Install `py3-pyside6` package via `apk`. This is the only tricky bit. We couldn't install PySide6 10 | via pip from inside the venv, as we'd normally do. 11 | 2. Clone this repo and create a Python Virtual Environment, *which uses system packages*: 12 | `python3 -m venv venv --system-site-packages` 13 | 3. The rest of the steps are the same as for any other Linux OS 14 | 15 | -------------------------------------------------------------------------------- /doc/data-model.md: -------------------------------------------------------------------------------- 1 | # Data model 2 | 3 | Flowkeeper data model is strictly hierarchical: 4 | 5 | - Tenant: AbstractDataContainer 6 | - User: AbstractDataContainer 7 | - Backlog: AbstractDataContainer 8 | - Workitem: AbstractDataContainer 9 | - Pomodoro: AbstractDataItem 10 | 11 | `AbstractDataContainer` acts as a `dict`, and `AbstractDataItem` represents a domain object with 12 | `uid`, `parent`, `create_date` and `last_modified_date`. 13 | 14 | Due to its tree nature, sharing backlogs and workitems should be implemented via symlinks. 15 | -------------------------------------------------------------------------------- /doc/fk-simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/doc/fk-simple.png -------------------------------------------------------------------------------- /doc/pipeline.md: -------------------------------------------------------------------------------- 1 | ## CI/CD Pipeline 2 | 3 | ![Schematics of the Flowkeeper CI/CD pipeline](fk-pipeline.svg "Flowkeeper CI/CD pipeline") 4 | -------------------------------------------------------------------------------- /flowkeeper-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/flowkeeper-1024.png -------------------------------------------------------------------------------- /requirements-test.txt: -------------------------------------------------------------------------------- 1 | -r requirements.txt 2 | coverage 3 | coveralls 4 | assertpy 5 | unittest-xml-reporting 6 | pillow 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Stay at 6.7.0 due to a macOS bug in 6.7.1 and 6.7.2, which says "No QtMultimedia backends found". 2 | # 6.7.3 has another regression. 3 | # PySide6==6.8.1 4 | PySide6 5 | semantic-version 6 | cryptography 7 | keyring 8 | -------------------------------------------------------------------------------- /res/NotoSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/res/NotoSans.ttf -------------------------------------------------------------------------------- /res/flowkeeper.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/res/flowkeeper.ico -------------------------------------------------------------------------------- /res/flowkeeper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/res/flowkeeper.png -------------------------------------------------------------------------------- /res/icons/dark/24x24/conn-offline.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/conn-online.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/conn-unknown.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/pomodoro-finished-planned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/pomodoro-finished-unplanned.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/pomodoro-new-planned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/pomodoro-new-unplanned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/pomodoro-running-planned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/pomodoro-running-unplanned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/pomodoro-voided.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-add-pomodoro.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-add-prefilled.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-add.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-backlogs.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-complete-item.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-filter-off.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-filter-on.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-finish-tracking.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-focus-complete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-focus-next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-interruption.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-left-close.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-left-open.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-next.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-note.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-pin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-remove-pomodoro.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-rename.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-settings.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-show-all.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-show-timer-only.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-start-item.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-teams.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-unpin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/dark/24x24/tool-void.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/dark/index.theme: -------------------------------------------------------------------------------- 1 | [Icon Theme] 2 | Name=dark 3 | Inherits=hicolor 4 | Directories=24x24 5 | 6 | [24x24] 7 | Size=24 8 | -------------------------------------------------------------------------------- /res/icons/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/res/icons/info.png -------------------------------------------------------------------------------- /res/icons/light/24x24/conn-offline.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/light/24x24/conn-online.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/light/24x24/conn-unknown.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/light/24x24/pomodoro-finished-planned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/pomodoro-finished-unplanned.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/light/24x24/pomodoro-new-planned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/pomodoro-new-unplanned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/pomodoro-running-planned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/pomodoro-running-unplanned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/pomodoro-voided.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-add-pomodoro.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-add-prefilled.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-add.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-backlogs.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-complete-item.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-filter-off.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-filter-on.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-finish-tracking.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-focus-complete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-focus-next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-interruption.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-left-close.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-left-open.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-next.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-note.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-pin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-remove-pomodoro.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-rename.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-settings.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-show-all.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-show-timer-only.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-start-item.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-teams.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-unpin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/24x24/tool-void.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/light/index.theme: -------------------------------------------------------------------------------- 1 | [Icon Theme] 2 | Name=light 3 | Inherits=hicolor 4 | Directories=24x24 5 | 6 | [24x24] 7 | Size=24 8 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/conn-offline.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/conn-online.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/conn-unknown.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/pomodoro-finished-planned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/pomodoro-finished-unplanned.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/pomodoro-new-planned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/pomodoro-new-unplanned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/pomodoro-running-planned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/pomodoro-running-unplanned.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/pomodoro-voided.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-add-pomodoro.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-add-prefilled.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-add.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-backlogs.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-complete-item.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-filter-off.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-filter-on.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-finish-tracking.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-focus-complete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-focus-next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-interruption.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-left-close.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-left-open.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-note.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-pin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-remove-pomodoro.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-rename.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-settings.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-show-all.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-show-timer-only.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-start-item.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-teams.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-unpin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icons/mixed/24x24/tool-void.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/icons/mixed/index.theme: -------------------------------------------------------------------------------- 1 | [Icon Theme] 2 | Name=mixed 3 | Inherits=hicolor 4 | Directories=24x24 5 | 6 | [24x24] 7 | Size=24 8 | -------------------------------------------------------------------------------- /res/icons/triangle-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 56 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /res/icons/triangle-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 56 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /res/icons/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/res/icons/warning.png -------------------------------------------------------------------------------- /res/img/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/res/img/bg.jpg -------------------------------------------------------------------------------- /res/sound/Madelene.m4a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/res/sound/Madelene.m4a -------------------------------------------------------------------------------- /res/sound/bell.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/res/sound/bell.wav -------------------------------------------------------------------------------- /res/sound/tick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/res/sound/tick.wav -------------------------------------------------------------------------------- /res/style-beach.json: -------------------------------------------------------------------------------- 1 | { 2 | "ICON_THEME": "mixed", 3 | 4 | "FOCUS_TEXT_COLOR": "#ffffff", 5 | "FOCUS_BG_COLOR": "#F038FF", 6 | "FOCUS_BORDER_COLOR": "#F038FF", 7 | 8 | "BORDER_COLOR": "#EEEEEE", 9 | 10 | "TABLE_TEXT_COLOR": "#000000", 11 | "PRIMARY_BG_COLOR": "#E2EF70", 12 | "SECONDARY_BG_COLOR": "#70E4EF", 13 | "SELECTION_BG_COLOR": "#FFFFFF", 14 | 15 | "TOOLBAR_BG_COLOR": "#3772FF", 16 | "TOOLBAR_CHECKED_BG_COLOR": "#3772FF" 17 | } 18 | -------------------------------------------------------------------------------- /res/style-dark.json: -------------------------------------------------------------------------------- 1 | { 2 | "ICON_THEME": "dark", 3 | 4 | "FOCUS_TEXT_COLOR": "#ffffff", 5 | "FOCUS_BG_COLOR": "#27282e", 6 | "FOCUS_BORDER_COLOR": "#000000", 7 | 8 | "BORDER_COLOR": "#000000", 9 | 10 | "TABLE_TEXT_COLOR": "#ced0d6", 11 | "PRIMARY_BG_COLOR": "#1e1f22", 12 | "SECONDARY_BG_COLOR": "#2b2d30", 13 | "SELECTION_BG_COLOR": "#43454a", 14 | 15 | "TOOLBAR_BG_COLOR": "#44484C", 16 | "TOOLBAR_CHECKED_BG_COLOR": "#64686C" 17 | } 18 | -------------------------------------------------------------------------------- /res/style-desert.json: -------------------------------------------------------------------------------- 1 | { 2 | "ICON_THEME": "mixed", 3 | 4 | "FOCUS_TEXT_COLOR": "#ffffff", 5 | "FOCUS_BG_COLOR": "#D52941", 6 | "FOCUS_BORDER_COLOR": "#FCD581", 7 | 8 | "BORDER_COLOR": "#FCD581", 9 | 10 | "TABLE_TEXT_COLOR": "#000000", 11 | "PRIMARY_BG_COLOR": "#ffffff", 12 | "SECONDARY_BG_COLOR": "#FFF8E8", 13 | "SELECTION_BG_COLOR": "#FCD581", 14 | 15 | "TOOLBAR_BG_COLOR": "#FCD581", 16 | "TOOLBAR_CHECKED_BG_COLOR": "#990D35" 17 | } 18 | -------------------------------------------------------------------------------- /res/style-highlight.json: -------------------------------------------------------------------------------- 1 | { 2 | "ICON_THEME": "dark", 3 | 4 | "FOCUS_TEXT_COLOR": "#FFFFFF", 5 | "FOCUS_BG_COLOR": "#EF6306", 6 | "FOCUS_BORDER_COLOR": "#000000", 7 | 8 | "BORDER_COLOR": "#000000", 9 | 10 | "TABLE_TEXT_COLOR": "#FFFFFF", 11 | "PRIMARY_BG_COLOR": "#1e1f22", 12 | "SECONDARY_BG_COLOR": "#2b2d30", 13 | "SELECTION_BG_COLOR": "#EF6306", 14 | 15 | "TOOLBAR_BG_COLOR": "#44484C", 16 | "TOOLBAR_CHECKED_BG_COLOR": "#EF6306" 17 | } 18 | -------------------------------------------------------------------------------- /res/style-light.json: -------------------------------------------------------------------------------- 1 | { 2 | "ICON_THEME": "light", 3 | 4 | "FOCUS_TEXT_COLOR": "#000000", 5 | "FOCUS_BG_COLOR": "#f7f8fa", 6 | "FOCUS_BORDER_COLOR": "#d7d8da", 7 | 8 | "BORDER_COLOR": "#d7d8da", 9 | 10 | "TABLE_TEXT_COLOR": "#000000", 11 | "PRIMARY_BG_COLOR": "#ffffff", 12 | "SECONDARY_BG_COLOR": "#f7f8fa", 13 | "SELECTION_BG_COLOR": "#cfdefc", 14 | 15 | "TOOLBAR_BG_COLOR": "#f7f8fa", 16 | "TOOLBAR_CHECKED_BG_COLOR": "#dfe1e5" 17 | } 18 | -------------------------------------------------------------------------------- /res/style-lime.json: -------------------------------------------------------------------------------- 1 | { 2 | "ICON_THEME": "light", 3 | 4 | "FOCUS_TEXT_COLOR": "#000000", 5 | "FOCUS_BG_COLOR": "#E0FF4F", 6 | "FOCUS_BORDER_COLOR": "#A7CC00", 7 | 8 | "BORDER_COLOR": "#d7d8da", 9 | 10 | "TABLE_TEXT_COLOR": "#000000", 11 | "PRIMARY_BG_COLOR": "#FEFFFE", 12 | "SECONDARY_BG_COLOR": "#E0ECF5", 13 | "SELECTION_BG_COLOR": "#A3C6E1", 14 | 15 | "TOOLBAR_BG_COLOR": "#FF6663", 16 | "TOOLBAR_CHECKED_BG_COLOR": "#E0FF4F" 17 | } 18 | -------------------------------------------------------------------------------- /res/style-mixed.json: -------------------------------------------------------------------------------- 1 | { 2 | "ICON_THEME": "mixed", 3 | 4 | "FOCUS_TEXT_COLOR": "#ffffff", 5 | "FOCUS_BG_COLOR": "#27282e", 6 | "FOCUS_BORDER_COLOR": "#535553", 7 | 8 | "BORDER_COLOR": "#d7d8da", 9 | 10 | "TABLE_TEXT_COLOR": "#000000", 11 | "PRIMARY_BG_COLOR": "#ffffff", 12 | "SECONDARY_BG_COLOR": "#f7f8fa", 13 | "SELECTION_BG_COLOR": "#cfdefc", 14 | 15 | "TOOLBAR_BG_COLOR": "#44484C", 16 | "TOOLBAR_CHECKED_BG_COLOR": "#64686C" 17 | } 18 | -------------------------------------------------------------------------------- /res/style-motel.json: -------------------------------------------------------------------------------- 1 | { 2 | "ICON_THEME": "mixed", 3 | 4 | "FOCUS_TEXT_COLOR": "#FFFFFF", 5 | "FOCUS_BG_COLOR": "#343233", 6 | "FOCUS_BORDER_COLOR": "#343233", 7 | 8 | "BORDER_COLOR": "#FFFFFF", 9 | 10 | "TABLE_TEXT_COLOR": "#000000", 11 | "PRIMARY_BG_COLOR": "#FFEEF2", 12 | "SECONDARY_BG_COLOR": "#FFE4F3", 13 | "SELECTION_BG_COLOR": "#FFC8FB", 14 | 15 | "TOOLBAR_BG_COLOR": "#FF92C2", 16 | "TOOLBAR_CHECKED_BG_COLOR": "#343233" 17 | } 18 | -------------------------------------------------------------------------------- /res/style-purple.json: -------------------------------------------------------------------------------- 1 | { 2 | "ICON_THEME": "dark", 3 | 4 | "FOCUS_TEXT_COLOR": "#E9EDE9", 5 | "FOCUS_BG_COLOR": "#2D2327", 6 | "FOCUS_BORDER_COLOR": "#45364B", 7 | 8 | "BORDER_COLOR": "#45364B", 9 | 10 | "TABLE_TEXT_COLOR": "#E9EDE9", 11 | "PRIMARY_BG_COLOR": "#413347", 12 | "SECONDARY_BG_COLOR": "#5A4063", 13 | "SELECTION_BG_COLOR": "#2D2327", 14 | 15 | "TOOLBAR_BG_COLOR": "#606880", 16 | "TOOLBAR_CHECKED_BG_COLOR": "#2D2327" 17 | } 18 | -------------------------------------------------------------------------------- /res/style-resort.json: -------------------------------------------------------------------------------- 1 | { 2 | "ICON_THEME": "mixed", 3 | 4 | "FOCUS_TEXT_COLOR": "#FFFFFF", 5 | "FOCUS_BG_COLOR": "#F5AB00", 6 | "FOCUS_BORDER_COLOR": "#F5AB00", 7 | 8 | "BORDER_COLOR": "#FFFFFF", 9 | 10 | "TABLE_TEXT_COLOR": "#000000", 11 | "PRIMARY_BG_COLOR": "#FFFFFF", 12 | "SECONDARY_BG_COLOR": "#F7EDDE", 13 | "SELECTION_BG_COLOR": "#2DC7FF", 14 | 15 | "TOOLBAR_BG_COLOR": "#2DC7FF", 16 | "TOOLBAR_CHECKED_BG_COLOR": "#0081AF" 17 | } 18 | -------------------------------------------------------------------------------- /res/style-terra.json: -------------------------------------------------------------------------------- 1 | { 2 | "ICON_THEME": "dark", 3 | 4 | "FOCUS_TEXT_COLOR": "#ffffff", 5 | "FOCUS_BG_COLOR": "#2D381A", 6 | "FOCUS_BORDER_COLOR": "#2D381A", 7 | 8 | "BORDER_COLOR": "#2D381A", 9 | 10 | "TABLE_TEXT_COLOR": "#ffffff", 11 | "PRIMARY_BG_COLOR": "#5F8349", 12 | "SECONDARY_BG_COLOR": "#426B69", 13 | "SELECTION_BG_COLOR": "#2D381A", 14 | 15 | "TOOLBAR_BG_COLOR": "#2A4849", 16 | "TOOLBAR_CHECKED_BG_COLOR": "#2D381A" 17 | } 18 | -------------------------------------------------------------------------------- /res/summary.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | WorkSummaryWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 600 10 | 500 11 | 12 | 13 | 14 | Work Summary 15 | 16 | 17 | 18 | 0 19 | 20 | 21 | 0 22 | 23 | 24 | 0 25 | 26 | 27 | 0 28 | 29 | 30 | 0 31 | 32 | 33 | 34 | 35 | 10 36 | 37 | 38 | 15 39 | 40 | 41 | 15 42 | 43 | 44 | 15 45 | 46 | 47 | 15 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Include time spent 59 | 60 | 61 | 62 | 63 | 64 | 65 | Include information about backlogs 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | QDialogButtonBox::Close|QDialogButtonBox::Save 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | source venv/bin/activate 24 | PYTHONPATH=src python -m coverage run -m unittest discover -v fk.tests 25 | python -m coverage html # --include="src/fk/core/*" 26 | 27 | # Simple test run 28 | # PYTHONPATH=src python -m unittest discover -v fk.tests 29 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | source venv/bin/activate 22 | PYTHONPATH=src python -m fk.desktop.desktop "$@" 23 | 24 | -------------------------------------------------------------------------------- /scripts/bsd/README.md: -------------------------------------------------------------------------------- 1 | # Building for FreeBSD 2 | 3 | We successfully built and tested Flowkeeper on FreeBSD: 4 | 5 | ``` 6 | pkg install python3 git devel/pyside6 devel/pyside6-tools py311-keyring py311-semantic-version 7 | git clone https://github.com/flowkeeper-org/fk-desktop.git 8 | cd fk-desktop/res 9 | /usr/local/bin/pyside6/rcc --project -o resources.qrc 10 | /usr/local/bin/pyside6/rcc -g python resources.qrc -o ../src/fk/desktop/resources.py 11 | cd .. 12 | PYTHONPATH=src python3.11 -m fk.desktop.desktop 13 | ``` 14 | 15 | Tested with: 16 | 17 | - KDE 5.27.11 on X11 18 | - FreeBSD 14.1 RELEASE 19 | - Flowkeeper v0.10.0 20 | - Python 3.11.11 21 | - Qt 6.8.2 (xcb) 22 | -------------------------------------------------------------------------------- /scripts/common/download-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | REPO="flowkeeper-org/fk-desktop" 24 | OUTPUT="downloads" 25 | 26 | mkdir -p "$OUTPUT" 27 | 28 | if [ -z "$RELEASE" ]; then 29 | echo "Fetching the latest release..." 30 | echo Getting the latest release... 31 | RELEASE=$(gh release list --repo "$REPO" --json tagName,isPrerelease --jq ".[] | select(.isPrerelease) | .tagName") 32 | else 33 | echo "Release variable is specified" 34 | fi 35 | 36 | echo "Downloading binaries for release $RELEASE to $(pwd)" 37 | 38 | gh release download "$RELEASE" --clobber --dir "$OUTPUT" --pattern "*.exe" --repo "$REPO" 39 | gh release download "$RELEASE" --clobber --dir "$OUTPUT" --pattern "*-windows-*-standalone.zip" --repo "$REPO" 40 | echo "Done. Downloaded:" 41 | ls -al "$OUTPUT" 42 | 43 | echo "Unpacking standalone ZIPs" 44 | for f in "$OUTPUT"/*.zip; do 45 | echo "Unpacking $f..." 46 | unzip -p "$f" "Flowkeeper.exe" > "${f/zip/exe}" 47 | done 48 | 49 | echo "Cleanup..." 50 | rm -rf "$OUTPUT"/*.zip 51 | 52 | echo "Done. Extracted all:" 53 | ls -al "$OUTPUT" 54 | -------------------------------------------------------------------------------- /scripts/common/generate-resources.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | if [[ "$OSTYPE" == "msys" ]]; then 24 | alias "pyside6-rcc=$(pwd)/venv/Lib/site-packages/PySide6/rcc" 25 | elif [[ "$OSTYPE" == "darwin"* ]]; then 26 | scripts/macos/create-icons.sh 27 | echo "Generated icns file for macOS" 28 | ls -al 29 | fi 30 | 31 | cd res 32 | qrc="resources.qrc" 33 | pyside6-rcc --project -o "$qrc" 34 | pyside6-rcc -g python "$qrc" -o "../src/fk/desktop/resources.py" 35 | rm "$qrc" 36 | -------------------------------------------------------------------------------- /scripts/common/get-version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | VERSION_REGEX='^### v(.+) \(.*$' 22 | VERSION_LINE=$(head --lines=1 res/CHANGELOG.txt) 23 | if [[ $VERSION_LINE =~ $VERSION_REGEX ]]; then 24 | echo "${BASH_REMATCH[1]}" 25 | else 26 | echo "N/A" 27 | fi 28 | -------------------------------------------------------------------------------- /scripts/common/pyinstaller/entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-unsigned-executable-memory 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /scripts/common/pyinstaller/normal.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | # Flowkeeper - Pomodoro timer for power users and teams 3 | # Copyright (c) 2023 Constantine Kulak 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | import argparse 19 | 20 | parser = argparse.ArgumentParser() 21 | parser.add_argument("--sign", action="store_true") 22 | options = parser.parse_args() 23 | 24 | a = Analysis( 25 | ['../../../src/fk/desktop/desktop.py'], 26 | pathex=[], 27 | binaries=[], 28 | datas=[], 29 | hiddenimports=[], 30 | hookspath=[], 31 | hooksconfig={}, 32 | runtime_hooks=[], 33 | excludes=[], 34 | noarchive=False, 35 | ) 36 | pyz = PYZ(a.pure) 37 | 38 | exe = EXE( 39 | pyz, 40 | a.scripts, 41 | [], 42 | exclude_binaries=True, 43 | name='Flowkeeper', 44 | debug=False, 45 | bootloader_ignore_signals=False, 46 | strip=False, 47 | upx=True, 48 | console=False, 49 | disable_windowed_traceback=False, 50 | argv_emulation=False, 51 | target_arch=None, 52 | codesign_identity=('Developer ID Application: Constantine Kulak (ELWZ9S676C)' if options.sign else None), 53 | entitlements_file=('/Users/runner/work/fk-desktop/fk-desktop/scripts/common/pyinstaller/entitlements.plist' if options.sign else None), 54 | icon=['../../../res/flowkeeper.ico'], 55 | ) 56 | coll = COLLECT( 57 | exe, 58 | a.binaries, 59 | a.datas, 60 | strip=False, 61 | upx=True, 62 | upx_exclude=[], 63 | name='flowkeeper', 64 | ) 65 | app = BUNDLE( 66 | coll, 67 | name='Flowkeeper.app', 68 | icon='../../../flowkeeper.icns', 69 | bundle_identifier='org.flowkeeper.Flowkeeper', 70 | ) -------------------------------------------------------------------------------- /scripts/common/pyinstaller/portable.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | # Flowkeeper - Pomodoro timer for power users and teams 3 | # Copyright (c) 2023 Constantine Kulak 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | from PyInstaller.utils.hooks import collect_all 19 | 20 | datas = [] 21 | binaries = [] 22 | hiddenimports = [] 23 | tmp_ret = collect_all('fk') 24 | datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2] 25 | 26 | 27 | a = Analysis( 28 | ['../../../src/fk/desktop/desktop.py'], 29 | pathex=['../../../src'], 30 | binaries=binaries, 31 | datas=datas, 32 | hiddenimports=hiddenimports, 33 | hookspath=[], 34 | hooksconfig={}, 35 | runtime_hooks=[], 36 | excludes=[], 37 | noarchive=True, 38 | ) 39 | pyz = PYZ(a.pure) 40 | 41 | exe = EXE( 42 | pyz, 43 | a.scripts, 44 | a.binaries, 45 | a.datas, 46 | [('v', None, 'OPTION')], 47 | name='Flowkeeper', 48 | debug=True, 49 | bootloader_ignore_signals=False, 50 | strip=False, 51 | upx=True, 52 | upx_exclude=[], 53 | runtime_tmpdir=None, 54 | console=False, 55 | disable_windowed_traceback=False, 56 | argv_emulation=False, 57 | target_arch=None, 58 | codesign_identity=None, 59 | entitlements_file=None, 60 | icon=['../../../res/flowkeeper.ico'], 61 | ) 62 | -------------------------------------------------------------------------------- /scripts/common/upload-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | REPO="flowkeeper-org/fk-desktop" 24 | 25 | if [ -z "$RELEASE" ]; then 26 | echo "Fetching the latest release..." 27 | echo Getting the latest release... 28 | RELEASE=$(gh release list --repo "$REPO" --json tagName,isPrerelease --jq ".[] | select(.isPrerelease) | .tagName") 29 | else 30 | echo "Release variable is specified" 31 | fi 32 | 33 | echo "Uploading binaries for release $RELEASE" 34 | 35 | for f in "$@"; do 36 | echo "Uploading $f..." 37 | gh release upload --clobber --repo "$REPO" "$RELEASE" "$f" 38 | echo "Done" 39 | done 40 | 41 | echo "Done. Uploaded all." 42 | -------------------------------------------------------------------------------- /scripts/linux/appimage/install-appimage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | # AppImage installer 24 | sudo wget "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$(uname -m).AppImage" -O /usr/local/bin/appimagetool 25 | sudo chmod +x /usr/local/bin/appimagetool 26 | -------------------------------------------------------------------------------- /scripts/linux/appimage/package-appimage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | # In the next version(s) think of installing it in /opt/Flowkeeper instead 24 | 25 | # 1. Prepare temp folder 26 | cd build 27 | rm -rf AppDir 28 | mkdir AppDir 29 | echo "1. Prepared temp folder" 30 | 31 | # 2. Copy application files 32 | mkdir -p AppDir/usr/lib/flowkeeper 33 | mkdir -p AppDir/usr/share/icons/hicolor/512x512/flowkeeper 34 | mkdir -p AppDir/usr/share/metainfo 35 | cp -r ../dist/standalone/* AppDir/usr/lib/flowkeeper/ 36 | cp ../res/flowkeeper.png AppDir/usr/share/icons/hicolor/512x512/flowkeeper/ 37 | cp ../scripts/linux/common/org.flowkeeper.Flowkeeper.metainfo.xml AppDir/usr/share/metainfo/org.flowkeeper.Flowkeeper.appdata.xml 38 | echo "2. Copied application files" 39 | 40 | # 3. Create a desktop shortcut 41 | export FK_AUTOSTART_ARGS="" 42 | < ../scripts/linux/common/flowkeeper.desktop envsubst > AppDir/org.flowkeeper.Flowkeeper.desktop 43 | echo "3. Created a desktop shortcut:" 44 | cat AppDir/org.flowkeeper.Flowkeeper.desktop 45 | 46 | # 4. Create AppRun symlink 47 | cd AppDir 48 | ln -s ./usr/lib/flowkeeper/Flowkeeper ./AppRun 49 | cd .. 50 | echo "4. Created AppRun symlink" 51 | 52 | # 5. Create .DirIcon symlink 53 | cd AppDir 54 | ln -s usr/share/icons/hicolor/512x512/flowkeeper/flowkeeper.png ./.DirIcon 55 | cd .. 56 | echo "5. Create .DirIcon symlink" 57 | 58 | # 6. Build AppImage file 59 | ls -al AppDir/ 60 | appimagetool AppDir 61 | echo "6. Built AppImage file: $(ls ./*.AppImage)" 62 | 63 | mv ./*.AppImage ../dist 64 | -------------------------------------------------------------------------------- /scripts/linux/common/flowkeeper: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | PYTHONPATH=/usr/lib/flowkeeper:/usr/libexec/flowkeeper python3 -m fk.desktop.desktop $@ 22 | -------------------------------------------------------------------------------- /scripts/linux/common/flowkeeper.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Flowkeeper 3 | Comment=Pomodoro Technique desktop timer for power users 4 | Exec=/usr/bin/flowkeeper $FK_AUTOSTART_ARGS 5 | Icon=/usr/share/icons/hicolor/512x512/flowkeeper/flowkeeper 6 | Terminal=false 7 | Type=Application 8 | Categories=Utility; 9 | -------------------------------------------------------------------------------- /scripts/linux/common/org.flowkeeper.Flowkeeper.metainfo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.flowkeeper.Flowkeeper 4 | 5 | Flowkeeper 6 | 7 | Pomodoro timer for power users 8 | 9 | https://flowkeeper.org 10 | 11 | MIT 12 | 13 | GPL-3.0-only 14 | 15 | 16 |

17 | Flowkeeper is a Pomodoro timer with a "classic" cross-platform UI paradigm (desktop-first, no Electron). With its keyboard shortcuts and advanced settings, Flowkeeper is optimized for power users. It stays as close as possible to the Pomodoro Technique definition and format from the original book by Francesco Cirillo. 18 |

19 |
20 | 21 | flowkeeper.desktop 22 | 23 | 24 | 25 | https://flowkeeper.org/images/releases/v0.9.0/Linux/fulls/19-main-dark-window-border.png 26 | 27 | 28 | https://flowkeeper.org/images/releases/v0.9.0/Linux/fulls/14-stats-month-window-border.png 29 | 30 | 31 | https://flowkeeper.org/images/releases/v0.9.0/Linux/fulls/16-work-summary-window-border.png 32 | 33 | 34 | 35 | 36 | flowkeeper 37 | 38 | 39 | 40 | 41 | 42 |

Standard XDG directories are used on Linux (#65).

43 |

Voided pomodoros are displayed as ticks to better match the textbook (#92).

44 |

Basic import from GitHub Issues, try Ctrl+I (#00).

45 |

Flowkeeper window now hides automatically on auto-start (#102).

46 |

Flowkeeper now supports Flatpak and Qt 6.8.1 (#63).

47 |
48 |
49 |
50 | 51 | 52 | Flowkeeper Team 53 | 54 | 55 | 56 |
57 | -------------------------------------------------------------------------------- /scripts/linux/debian/debian-control: -------------------------------------------------------------------------------- 1 | Package: flowkeeper 2 | Version: $FK_VERSION 3 | Maintainer: Constantine Kulak 4 | Architecture: amd64 5 | Description: Flowkeeper is a free Pomodoro Technique desktop timer for power users. 6 | -------------------------------------------------------------------------------- /scripts/linux/debian/debian-control-min: -------------------------------------------------------------------------------- 1 | Package: flowkeeper 2 | Version: $FK_VERSION 3 | Maintainer: Constantine Kulak 4 | Architecture: amd64 5 | Description: Flowkeeper is a free Pomodoro Technique desktop timer for power users. 6 | Depends: python3-pyside6.qtcore(>= 6.7.0), python3-pyside6.qtwidgets(>= 6.7.0), python3-pyside6.qtgui(>= 6.7.0), python3-pyside6.qtnetwork(>= 6.7.0), python3-pyside6.qtnetworkauth(>= 6.7.0), python3-pyside6.qtmultimedia(>= 6.7.0), python3-pyside6.qtsvg(>= 6.7.0), python3-pyside6.qtwebsockets(>= 6.7.0), python3-pyside6.qtuitools(>= 6.7.0), python3-pyside6.qtasyncio(>= 6.7.0), python3-pyside6.qtcharts(>= 6.7.0), python3-semantic-version, python3-cryptography, python3-keyring 7 | -------------------------------------------------------------------------------- /scripts/linux/debian/package-deb-min.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | # In the next version(s) think of installing it in /opt/Flowkeeper instead 24 | 25 | # 1. Get the version 26 | echo "1. Version = $FK_VERSION" 27 | 28 | # 2. Prepare temp folder 29 | dist="build/deb" 30 | rm -rf "$dist" 31 | mkdir "$dist" 32 | echo "2. Prepared temp folder" 33 | 34 | # 3. Copy application files 35 | mkdir -p "$dist/usr/lib/flowkeeper" 36 | cp -r src/* "$dist/usr/lib/flowkeeper/" 37 | 38 | mkdir -p "$dist/usr/share/icons/hicolor/512x512/flowkeeper" 39 | cp res/flowkeeper.png "$dist/usr/share/icons/hicolor/512x512/flowkeeper/" 40 | 41 | mkdir -p "$dist/usr/bin" 42 | cp scripts/linux/common/flowkeeper "$dist/usr/bin/flowkeeper" 43 | echo "3. Copied application files" 44 | 45 | # 4. Create a desktop shortcut 46 | mkdir -p "$dist/usr/share/applications" 47 | export FK_AUTOSTART_ARGS="" 48 | < scripts/linux/common/flowkeeper.desktop envsubst > "$dist/usr/share/applications/org.flowkeeper.Flowkeeper.desktop" 49 | echo "4. Created a desktop shortcut:" 50 | cat "$dist/usr/share/applications/org.flowkeeper.Flowkeeper.desktop" 51 | 52 | # 5. Create another one for autostart (with --autostart argument) 53 | mkdir -p "$dist/etc/xdg/autostart" 54 | export FK_AUTOSTART_ARGS="--autostart" 55 | < scripts/linux/common/flowkeeper.desktop envsubst > "$dist/etc/xdg/autostart/org.flowkeeper.Flowkeeper.desktop" 56 | echo "5. Added it to autostart" 57 | 58 | # 6. Create metadata 59 | mkdir "$dist/DEBIAN" 60 | < scripts/linux/debian/debian-control-min envsubst > "$dist/DEBIAN/control" 61 | echo "6. Created metadata" 62 | cat "$dist/DEBIAN/control" 63 | 64 | # 7. Build DEB file 65 | dpkg-deb --build "$dist" dist/flowkeeper-min.deb 66 | echo "7. Built DEB file" 67 | -------------------------------------------------------------------------------- /scripts/linux/debian/package-deb.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | # In the next version(s) think of installing it in /opt/Flowkeeper instead 24 | 25 | # 1. Get the version 26 | echo "1. Version = $FK_VERSION" 27 | 28 | # 2. Prepare temp folder 29 | cd build 30 | rm -rf deb 31 | mkdir deb 32 | echo "2. Prepared temp folder" 33 | 34 | # 3. Copy application files 35 | mkdir -p deb/usr/lib/flowkeeper 36 | mkdir -p deb/usr/share/icons/hicolor/512x512/flowkeeper 37 | cp -r ../dist/standalone/* deb/usr/lib/flowkeeper/ 38 | cp ../res/flowkeeper.png deb/usr/share/icons/hicolor/512x512/flowkeeper/ 39 | echo "3. Copied application files" 40 | 41 | # 4. Create a desktop shortcut 42 | mkdir -p deb/usr/share/applications 43 | export FK_AUTOSTART_ARGS="" 44 | < ../scripts/linux/common/flowkeeper.desktop envsubst > deb/usr/share/applications/org.flowkeeper.Flowkeeper.desktop 45 | echo "4. Created a desktop shortcut:" 46 | cat deb/usr/share/applications/org.flowkeeper.Flowkeeper.desktop 47 | 48 | # 5. Create another one for autostart (with --autostart argument) 49 | mkdir -p deb/etc/xdg/autostart 50 | export FK_AUTOSTART_ARGS="--autostart" 51 | < ../scripts/linux/common/flowkeeper.desktop envsubst > deb/etc/xdg/autostart/org.flowkeeper.Flowkeeper.desktop 52 | echo "5. Added it to autostart" 53 | 54 | # 6. Create a relative symlink in /usr/bin 55 | mkdir -p deb/usr/bin 56 | cd deb/usr/bin 57 | ln -s ../lib/flowkeeper/Flowkeeper ./flowkeeper 58 | cd ../../.. 59 | echo "6. Create a relative symlink in /usr/bin" 60 | 61 | # 7. Create metadata 62 | mkdir deb/DEBIAN 63 | < ../scripts/linux/debian/debian-control envsubst > deb/DEBIAN/control 64 | echo "7. Created metadata" 65 | cat deb/DEBIAN/control 66 | 67 | # 8. Build DEB file 68 | dpkg-deb --build deb ../dist/flowkeeper.deb 69 | echo "8. Built DEB file" 70 | -------------------------------------------------------------------------------- /scripts/linux/package-nuitka.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | source venv/bin/activate 24 | 25 | FK_VERSION=$(scripts/common/get-version.sh) 26 | 27 | PYTHONPATH=src python3 -m nuitka \ 28 | --onefile \ 29 | --enable-plugin=pyside6 \ 30 | --include-qt-plugins=multimedia \ 31 | --product-name=Flowkeeper \ 32 | --product-version="$FK_VERSION" \ 33 | --output-dir=build \ 34 | --output-file=Flowkeeper \ 35 | src/fk/desktop/desktop.py 36 | -------------------------------------------------------------------------------- /scripts/linux/rpm/package-rpm-min.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | # In the next version(s) think of installing it in /opt/Flowkeeper instead 24 | 25 | # 1. Get the version 26 | VERSION_REGEX='^### v(.+) \(.*$' 27 | VERSION_LINE=$(head --lines=1 res/CHANGELOG.txt) 28 | if [[ $VERSION_LINE =~ $VERSION_REGEX ]]; then 29 | export FK_VERSION="${BASH_REMATCH[1]}" 30 | else 31 | export FK_VERSION="N/A" 32 | fi 33 | echo "1. Version = $FK_VERSION" 34 | 35 | # 2. Prepare temp folder 36 | dist="dist/rpm" 37 | rm -rf "$dist" 38 | mkdir -p "$dist" 39 | echo "2. Prepared temp folder" 40 | 41 | # 3. Copy application files 42 | mkdir -p "$dist/usr/lib/flowkeeper" 43 | cp -r src/* "$dist/usr/lib/flowkeeper/" 44 | 45 | mkdir -p "$dist/usr/share/icons/hicolor/512x512/flowkeeper" 46 | cp res/flowkeeper.png "$dist/usr/share/icons/hicolor/512x512/flowkeeper/" 47 | 48 | mkdir -p "$dist/usr/bin" 49 | cp installer/flowkeeper "$dist/usr/bin/flowkeeper" 50 | echo "3. Copied application files" 51 | 52 | # 4. Create a desktop shortcut 53 | mkdir -p "$dist/usr/share/applications" 54 | export FK_AUTOSTART_ARGS="" 55 | < installer/flowkeeper.desktop envsubst > "$dist/usr/share/applications/org.flowkeeper.Flowkeeper.desktop" 56 | echo "4. Created a desktop shortcut:" 57 | cat "$dist/usr/share/applications/org.flowkeeper.Flowkeeper.desktop" 58 | 59 | # 5. Create another one for autostart (with --autostart argument) 60 | mkdir -p "$dist/etc/xdg/autostart" 61 | export FK_AUTOSTART_ARGS="--autostart" 62 | < installer/flowkeeper.desktop envsubst > "$dist/etc/xdg/autostart/org.flowkeeper.Flowkeeper.desktop" 63 | echo "5. Added it to autostart" 64 | -------------------------------------------------------------------------------- /scripts/macos/create-dmg.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | echo "Creating DMG" 24 | ls -al dist/ 25 | 26 | create-dmg \ 27 | --volname "Flowkeeper Installer" \ 28 | --volicon "flowkeeper.icns" \ 29 | --window-pos 200 120 \ 30 | --window-size 800 400 \ 31 | --icon-size 100 \ 32 | --icon "Flowkeeper.app" 200 190 \ 33 | --hide-extension "Flowkeeper.app" \ 34 | --app-drop-link 600 185 \ 35 | "dist/Flowkeeper.dmg" \ 36 | "dist/standalone" 37 | -------------------------------------------------------------------------------- /scripts/macos/create-icons.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | # Adapted from https://apple.stackexchange.com/questions/402621/convert-png-image-icon-to-icns-file-macos 24 | mkdir tmp.iconset 25 | sips -z 16 16 flowkeeper-1024.png --out tmp.iconset/icon_16x16.png 26 | sips -z 32 32 flowkeeper-1024.png --out tmp.iconset/icon_16x16@2x.png 27 | sips -z 32 32 flowkeeper-1024.png --out tmp.iconset/icon_32x32.png 28 | sips -z 64 64 flowkeeper-1024.png --out tmp.iconset/icon_32x32@2x.png 29 | sips -z 128 128 flowkeeper-1024.png --out tmp.iconset/icon_128x128.png 30 | sips -z 256 256 flowkeeper-1024.png --out tmp.iconset/icon_128x128@2x.png 31 | sips -z 256 256 flowkeeper-1024.png --out tmp.iconset/icon_256x256.png 32 | sips -z 512 512 flowkeeper-1024.png --out tmp.iconset/icon_256x256@2x.png 33 | sips -z 512 512 flowkeeper-1024.png --out tmp.iconset/icon_512x512.png 34 | cp flowkeeper-1024.png tmp.iconset/icon_512x512@2x.png 35 | iconutil -c icns tmp.iconset 36 | rm -R tmp.iconset 37 | mv tmp.icns flowkeeper.icns 38 | -------------------------------------------------------------------------------- /scripts/macos/install-certificates.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | # https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development 24 | 25 | # create variables 26 | CERTIFICATE_PATH="$RUNNER_TEMP/build_certificate.p12" 27 | KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db" 28 | 29 | # import certificate and provisioning profile from secrets 30 | echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o "$CERTIFICATE_PATH" 31 | 32 | # create temporary keychain 33 | security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" 34 | security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" 35 | security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" 36 | 37 | # import certificate to keychain 38 | security import "$CERTIFICATE_PATH" -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH" 39 | security set-key-partition-list -S "apple-tool:,apple:" -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" 40 | security list-keychain -d user -s "$KEYCHAIN_PATH" 41 | -------------------------------------------------------------------------------- /scripts/macos/install-create-dmg.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | if ! command -v create-dmg 2>&1 >/dev/null; then 24 | echo "create-dmg is not found, will try to install" 25 | brew install create-dmg 26 | fi 27 | -------------------------------------------------------------------------------- /scripts/macos/notarize-dmg.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | # Create the notary key 24 | xcrun notarytool store-credentials "notary-key" --apple-id "$NOTARIZATION_ID" --team-id "$NOTARIZATION_TEAM" --password "$NOTARIZATION_PASSWORD" 25 | 26 | # Send the DMG for notarization 27 | xcrun notarytool submit "dist/Flowkeeper.dmg" --keychain-profile "notary-key" --wait 28 | -------------------------------------------------------------------------------- /scripts/macos/package-macos-pkg.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | echo "Example PKG build script for macOS, needed for submitting Flowkeeper to App Store" 24 | 25 | sudo productbuild --component ./dist/Flowkeeper.app \ 26 | /Applications \ 27 | --sign "Developer ID Installer: Constantine Kulak (ELWZ9S676C)" \ 28 | --product ./dist/Flowkeeper.app/Contents/Info.plist \ 29 | ./dist/Flowkeeper.pkg -------------------------------------------------------------------------------- /scripts/macos/package-nuitka.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | # Step 0 - Enter venv 24 | source venv/bin/activate 25 | 26 | # Step 1 - Cleanup 27 | rm -rf build dist Flowkeeper.dmg 28 | 29 | # Check if $HOME/Library/Caches/Nuitka/downloads/ccache/v4.2.1/ exists, and download it from 30 | # https://nuitka.net/ccache/v4.2.1/ccache-4.2.1.zip if needed 31 | 32 | FK_VERSION=$(scripts/common/get-version.sh) 33 | 34 | # Step 2 - Create and sign an installer 35 | PYTHONPATH=src python3 -m nuitka \ 36 | --standalone \ 37 | --enable-plugin=pyside6 \ 38 | --macos-app-icon=flowkeeper.icns \ 39 | --macos-create-app-bundle \ 40 | --macos-signed-app-name=org.flowkeeper.Flowkeeper \ 41 | --macos-app-version="$FK_VERSION" \ 42 | --macos-app-name=Flowkeeper \ 43 | --macos-sign-identity="Developer ID Application: Constantine Kulak (ELWZ9S676C)" \ 44 | --product-name=Flowkeeper \ 45 | --product-version="$FK_VERSION" \ 46 | --output-dir=build \ 47 | --output-file=Flowkeeper \ 48 | src/fk/desktop/Flowkeeper.py 49 | 50 | # Step 3 - Create a DMG image 51 | rm -rf dist/flowkeeper 52 | create-dmg \ 53 | --volname "Flowkeeper Installer" \ 54 | --volicon "flowkeeper.icns" \ 55 | --window-pos 200 120 \ 56 | --window-size 800 400 \ 57 | --icon-size 100 \ 58 | --icon "Flowkeeper.app" 200 190 \ 59 | --hide-extension "Flowkeeper.app" \ 60 | --app-drop-link 600 185 \ 61 | "Flowkeeper.dmg" \ 62 | "dist/standalone" 63 | 64 | # Step 4 - Notarize the DMG 65 | xcrun notarytool submit dist/Flowkeeper.dmg --keychain-profile "notary-key" --wait 66 | -------------------------------------------------------------------------------- /scripts/windows/generate-resources-windows.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | PATH=$PATH:$(pwd)/venv/Lib/site-packages/PySide6 24 | 25 | cd res 26 | qrc="resources.qrc" 27 | rcc --project -o "$qrc" 28 | rcc -g python "$qrc" -o "../src/fk/desktop/resources.py" 29 | rm "$qrc" 30 | -------------------------------------------------------------------------------- /scripts/windows/install-innosetup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | echo "Downloading InnoSetup" 24 | powershell Invoke-WebRequest -Uri "https://files.jrsoftware.org/is/6/innosetup-6.2.2.exe" -OutFile "innosetup.exe" 25 | echo "Launching InnoSetup" 26 | cmd "/c start /wait innosetup.exe /VERYSILENT /CURRENTUSER /SUPPRESSMSGBOXES /NOICONS" 27 | echo "Installed" 28 | -------------------------------------------------------------------------------- /scripts/windows/package-installer.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Flowkeeper - Pomodoro timer for power users and teams 5 | # Copyright (c) 2023 Constantine Kulak 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | set -e 22 | 23 | "$HOME/AppData/Local/Programs/Inno Setup 6/ISCC.exe" scripts/windows/windows-installer.iss 24 | mv dist/mysetup.exe dist/setup.exe 25 | -------------------------------------------------------------------------------- /scripts/windows/requirements-vt.txt: -------------------------------------------------------------------------------- 1 | vt-py 2 | -------------------------------------------------------------------------------- /scripts/windows/windows-installer.iss: -------------------------------------------------------------------------------- 1 | [Setup] 2 | AppName=Flowkeeper 3 | AppVersion={#GetEnv('FK_VERSION')} 4 | AppPublisher=flowkeeper.org 5 | AppPublisherURL=https://flowkeeper.org 6 | AppSupportURL=https://flowkeeper.org 7 | AppUpdatesURL=https://flowkeeper.org 8 | DefaultDirName={userpf}\Flowkeeper 9 | DefaultGroupName=Flowkeeper 10 | SetupIconFile=res\flowkeeper.ico 11 | PrivilegesRequired=lowest 12 | SourceDir=..\.. 13 | OutputDir=dist 14 | 15 | [Tasks] 16 | Name: "desktopicon"; Description: "Create a &desktop icon"; GroupDescription: "Additional icons:" 17 | Name: "autostart"; Description: "Launch Flowkeeper when the system boots"; GroupDescription: "Additional icons:" 18 | 19 | [Files] 20 | Source: "dist\standalone\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs 21 | 22 | [Icons] 23 | Name: "{group}\Flowkeeper"; Filename: "{app}\Flowkeeper.exe" 24 | Name: "{userdesktop}\Flowkeeper"; Filename: "{app}\Flowkeeper.exe"; Tasks: desktopicon 25 | Name: "{userstartup}\Flowkeeper"; Parameters: "--autostart"; Filename: "{app}\Flowkeeper.exe"; Tasks: autostart 26 | 27 | [Run] 28 | Filename: "{app}\Flowkeeper.exe"; Description: "Launch Flowkeeper"; Flags: nowait postinstall skipifsilent 29 | -------------------------------------------------------------------------------- /src/fk/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/src/fk/__init__.py -------------------------------------------------------------------------------- /src/fk/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/src/fk/core/__init__.py -------------------------------------------------------------------------------- /src/fk/core/abstract_cryptograph.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import secrets 17 | import string 18 | from abc import ABC, abstractmethod 19 | 20 | from fk.core.abstract_settings import AbstractSettings 21 | from fk.core.events import AfterSettingsChanged 22 | 23 | 24 | class AbstractCryptograph(ABC): 25 | _settings: AbstractSettings 26 | key: str 27 | enabled: bool 28 | 29 | def __init__(self, settings: AbstractSettings): 30 | self._settings = settings 31 | self.key = self._settings.get('Source.encryption_key!') 32 | self.enabled = self._settings.is_e2e_encryption_enabled() 33 | settings.on(AfterSettingsChanged, self._on_setting_changed) 34 | if settings.get('Source.encryption_key!') == '': 35 | self._generate_key() 36 | 37 | def _generate_key(self) -> None: 38 | # UC-2: Launching FK for the first time, a random e2e encryption key is generated 39 | key = ''.join( 40 | secrets.choice(string.ascii_letters + string.digits) for _ in range(20) 41 | ) 42 | self._settings.set({'Source.encryption_key!': key}) 43 | 44 | def _on_setting_changed(self, event: str, old_values: dict[str, str], new_values: dict[str, str]): 45 | self.enabled = self._settings.is_e2e_encryption_enabled() 46 | if 'Source.encryption_key!' in new_values: 47 | self.key = new_values['Source.encryption_key!'] 48 | self._on_key_changed() 49 | 50 | @abstractmethod 51 | def _on_key_changed(self) -> None: 52 | pass 53 | 54 | @abstractmethod 55 | def encrypt(self, s: str) -> str: 56 | pass 57 | 58 | @abstractmethod 59 | def decrypt(self, s: str) -> str: 60 | pass 61 | -------------------------------------------------------------------------------- /src/fk/core/abstract_filesystem_watcher.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from abc import ABC, abstractmethod 17 | from typing import Callable 18 | 19 | 20 | class AbstractFilesystemWatcher(ABC): 21 | @abstractmethod 22 | def watch(self, filename: str, callback: Callable[[str], None]): 23 | pass 24 | 25 | @abstractmethod 26 | def unwatch(self, filename: str) -> None: 27 | pass 28 | 29 | @abstractmethod 30 | def unwatch_all(self) -> None: 31 | pass 32 | -------------------------------------------------------------------------------- /src/fk/core/abstract_serializer.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from abc import ABC, abstractmethod 17 | from typing import TypeVar, Generic 18 | 19 | from fk.core.abstract_cryptograph import AbstractCryptograph 20 | from fk.core.abstract_settings import AbstractSettings 21 | from fk.core.abstract_strategy import AbstractStrategy 22 | 23 | T = TypeVar('T') 24 | TRoot = TypeVar('TRoot') 25 | 26 | 27 | def sanitize_user_input(s: str) -> str: 28 | return s.replace('\n', ' ').replace('\r', '') 29 | 30 | 31 | class AbstractSerializer(ABC, Generic[T, TRoot]): 32 | _settings: AbstractSettings 33 | _cryptograph: AbstractCryptograph 34 | 35 | def __init__(self, settings: AbstractSettings, cryptograph: AbstractCryptograph): 36 | self._settings = settings 37 | self._cryptograph = cryptograph 38 | 39 | @abstractmethod 40 | def serialize(self, s: AbstractStrategy[TRoot]) -> T: 41 | pass 42 | 43 | @abstractmethod 44 | def deserialize(self, t: T) -> AbstractStrategy[TRoot] | None: 45 | pass 46 | -------------------------------------------------------------------------------- /src/fk/core/abstract_timer.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import datetime 17 | from abc import ABC, abstractmethod 18 | from typing import Callable 19 | 20 | 21 | class AbstractTimer(ABC): 22 | @abstractmethod 23 | def schedule(self, 24 | ms: float, 25 | callback: Callable[[dict, datetime.datetime], None], 26 | params: dict | None, 27 | once: bool = False) -> None: 28 | pass 29 | 30 | @abstractmethod 31 | def cancel(self) -> None: 32 | pass 33 | -------------------------------------------------------------------------------- /src/fk/core/backlog.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from __future__ import annotations 17 | 18 | import datetime 19 | from typing import Iterable 20 | 21 | from fk.core.abstract_data_container import AbstractDataContainer 22 | from fk.core.pomodoro import Pomodoro 23 | from fk.core.workitem import Workitem 24 | 25 | 26 | class Backlog(AbstractDataContainer[Workitem, 'User']): 27 | """Backlog is a named list of workitems, belonging to a User.""" 28 | _date_work_started: datetime.datetime | None 29 | 30 | def __init__(self, 31 | name: str, 32 | user: 'User', 33 | uid: str, 34 | create_date: datetime.datetime): 35 | super().__init__(name=name, parent=user, uid=uid, create_date=create_date) 36 | self._date_work_started = None 37 | 38 | def __str__(self): 39 | return f'Backlog "{self._name}"' 40 | 41 | def get_running_workitem(self) -> (Workitem, Pomodoro): 42 | for workitem in self.values(): 43 | for pomodoro in workitem.values(): 44 | if pomodoro.is_running(): 45 | return workitem, pomodoro 46 | return None, None 47 | 48 | def get_incomplete_workitems(self) -> Iterable[Workitem]: 49 | for workitem in self.values(): 50 | if not workitem.is_sealed(): 51 | yield workitem 52 | 53 | def is_today(self) -> bool: 54 | # "Today" = Backlog date corresponds to today's date 55 | # UC-3: The backlog is marked as "today" if it was created on the same date as today (day, month, year) 56 | return datetime.date.today() == self.get_create_date().date() 57 | 58 | def get_owner(self) -> 'User': 59 | return self._parent 60 | 61 | def get_start_date(self) -> datetime.datetime | None: 62 | return self._date_work_started 63 | 64 | def update_start_date(self, when: datetime.datetime) -> None: 65 | if self._date_work_started is None or self._date_work_started > when: 66 | self._date_work_started = when 67 | 68 | def to_dict(self) -> dict: 69 | d = super().to_dict() 70 | d['date_work_started'] = self._date_work_started 71 | return d 72 | -------------------------------------------------------------------------------- /src/fk/core/event_source_factory.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from __future__ import annotations 17 | 18 | from typing import Callable, TypeVar, Generic 19 | 20 | from fk.core.abstract_cryptograph import AbstractCryptograph 21 | from fk.core.abstract_event_source import AbstractEventSource 22 | from fk.core.abstract_settings import AbstractSettings 23 | from fk.core.tenant import Tenant 24 | 25 | TRoot = TypeVar('TRoot') 26 | 27 | 28 | class EventSourceFactory(Generic[TRoot]): 29 | _source_producers: dict[str, Callable[[AbstractSettings, AbstractCryptograph, TRoot], AbstractEventSource[TRoot]]] 30 | _instance: EventSourceFactory[TRoot] = None 31 | 32 | def __init__(self): 33 | self._source_producers = dict() 34 | 35 | def is_valid(self, name: str) -> bool: 36 | return name in self._source_producers 37 | 38 | def get_producer(self, name: str) -> Callable[[AbstractSettings, AbstractCryptograph, TRoot], AbstractEventSource[TRoot]]: 39 | return self._source_producers.get(name) 40 | 41 | def register_producer(self, 42 | name: str, 43 | producer: Callable[[AbstractSettings, AbstractCryptograph, TRoot], AbstractEventSource[TRoot]]) -> None: 44 | self._source_producers[name] = producer 45 | 46 | @staticmethod 47 | def get_event_source_factory() -> EventSourceFactory[Tenant]: 48 | if EventSourceFactory._instance is None: 49 | EventSourceFactory._instance = EventSourceFactory[Tenant]() 50 | return EventSourceFactory._instance 51 | -------------------------------------------------------------------------------- /src/fk/core/interruption.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from __future__ import annotations 17 | 18 | import datetime 19 | import logging 20 | 21 | from fk.core.abstract_data_item import AbstractDataItem 22 | 23 | logger = logging.getLogger(__name__) 24 | 25 | 26 | class Interruption(AbstractDataItem['Pomodoro']): 27 | _reason: str | None 28 | _duration: datetime.timedelta | None 29 | _void: bool 30 | 31 | def __init__(self, 32 | reason: str | None, 33 | duration: datetime.timedelta | None, 34 | void: bool, 35 | uid: str, 36 | pomodoro: 'Pomodoro', 37 | create_date: datetime.datetime): 38 | super().__init__(uid=uid, parent=pomodoro, create_date=create_date) 39 | self._reason = reason 40 | self._duration = duration 41 | self._void = void 42 | 43 | def __str__(self): 44 | if self._reason: 45 | return f"'[{self._reason}]" 46 | else: 47 | return f"'" 48 | 49 | def get_reason(self) -> str | None: 50 | return self._reason 51 | 52 | def get_duration(self) -> datetime.timedelta | None: 53 | return self._duration 54 | 55 | def is_void(self) -> bool: 56 | return (self._void or 57 | self._reason is not None and self._reason.startswith('Pomodoro voided') or 58 | self._reason == 'Voided automatically because you completed the workitem while the timer was running.') 59 | 60 | def get_parent(self) -> 'Pomodoro': 61 | return self._parent 62 | 63 | def dump(self, indent: str = '', mask_uid: bool = False) -> str: 64 | return f'{super().dump(indent, True)}\n' \ 65 | f'{indent} Reason: {self._reason if self._reason else ""}\n' \ 66 | f'{indent} Void: {self._void}\n' \ 67 | f'{indent} Duration: {self._duration if self._duration else ""}' 68 | 69 | def to_dict(self) -> dict: 70 | d = super().to_dict() 71 | d['reason'] = self._reason 72 | d['duration'] = self._duration 73 | d['void'] = self._void 74 | return d 75 | -------------------------------------------------------------------------------- /src/fk/core/no_cryptograph.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from fk.core.abstract_cryptograph import AbstractCryptograph 17 | from fk.core.abstract_settings import AbstractSettings 18 | 19 | 20 | class NoCryptograph(AbstractCryptograph): 21 | def __init__(self, settings: AbstractSettings): 22 | # We don't call super() on purpose here, so that it doesn't try to generate keys 23 | self._settings = settings 24 | self.enabled = False 25 | 26 | def _on_key_changed(self) -> None: 27 | self.enabled = False 28 | 29 | def encrypt(self, s: str) -> str: 30 | return s 31 | 32 | def decrypt(self, s: str) -> str: 33 | return s 34 | -------------------------------------------------------------------------------- /src/fk/core/sandbox.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import os 17 | import platform 18 | 19 | 20 | def get_sandbox_type() -> str | None: 21 | # See https://stackoverflow.com/questions/75274925/is-there-a-way-to-find-out-if-i-am-running-inside-a-flatpak-appimage-or-another 22 | if platform.system() == 'Linux': 23 | if os.environ.get('container') is not None: 24 | return 'Flatpak' 25 | elif os.environ.get('SNAP') is not None: 26 | return 'Snap' 27 | elif os.environ.get('APPIMAGE') is not None: 28 | return 'AppImage' 29 | return None 30 | -------------------------------------------------------------------------------- /src/fk/core/strategy_factory.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import logging 17 | import re 18 | from typing import Type, TypeVar 19 | 20 | from fk.core.abstract_strategy import AbstractStrategy 21 | 22 | logger = logging.getLogger(__name__) 23 | TRoot = TypeVar('TRoot') 24 | STRATEGY_CLASS_NAME_REGEX = re.compile(r'([A-Z][a-zA-Z]*)Strategy') 25 | STRATEGIES = dict[str, Type[AbstractStrategy[TRoot]]]() 26 | 27 | 28 | def strategy(cls: Type[AbstractStrategy[TRoot]]): 29 | m = STRATEGY_CLASS_NAME_REGEX.search(cls.__name__) 30 | if m is not None: 31 | name = m.group(1) 32 | logger.debug(f'Registering strategy {name} -> {cls.__name__}') 33 | STRATEGIES[name] = cls 34 | return cls 35 | else: 36 | raise Exception(f"Invalid strategy class name: {cls.__name__}") 37 | -------------------------------------------------------------------------------- /src/fk/core/tag.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from __future__ import annotations 17 | 18 | import datetime 19 | import logging 20 | from typing import Set 21 | 22 | from fk.core.abstract_data_item import AbstractDataItem 23 | from fk.core.workitem import Workitem 24 | 25 | logger = logging.getLogger(__name__) 26 | 27 | 28 | class Tag(AbstractDataItem['Tags']): 29 | _workitems: Set[Workitem] 30 | 31 | def __init__(self, 32 | name: str, 33 | user: 'User', 34 | create_date: datetime.datetime): 35 | super().__init__(uid=name, 36 | parent=user.get_tags(), 37 | create_date=create_date) 38 | self._workitems = set[Workitem]() 39 | 40 | def __str__(self): 41 | return f'#{self.get_uid()}' 42 | 43 | def get_workitems(self) -> Set[Workitem]: 44 | return self._workitems 45 | 46 | def add_workitem(self, workitem: Workitem) -> None: 47 | self._workitems.add(workitem) 48 | 49 | def remove_workitem(self, workitem: Workitem) -> None: 50 | self._workitems.remove(workitem) 51 | 52 | def dump(self, indent: str = '', mask_uid: bool = False) -> str: 53 | return f'{super().dump(indent, mask_uid)}\n' \ 54 | f'{indent} - Name: {self.get_uid()}' 55 | -------------------------------------------------------------------------------- /src/fk/core/tags.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from __future__ import annotations 17 | 18 | from fk.core.abstract_data_container import AbstractDataContainer 19 | from fk.core.tag import Tag 20 | 21 | 22 | def sanitize_tag(tag: str) -> str: 23 | return ''.join(filter(str.isalnum, tag)) 24 | 25 | 26 | class Tags(AbstractDataContainer[Tag, 'User']): 27 | def __init__(self, user: 'User'): 28 | super().__init__(f'Tags', 29 | user, 30 | f'tags-{user.get_identity()}', 31 | user.get_create_date()) 32 | 33 | def __str__(self): 34 | return f'Tags {self.get_name()}' 35 | 36 | def dump(self, indent: str = '', mask_uid: bool = False) -> str: 37 | return f'{super().dump(indent, mask_uid)}\n' \ 38 | f'{indent} - Tags' 39 | -------------------------------------------------------------------------------- /src/fk/core/tenant.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import datetime 17 | 18 | from fk.core.abstract_data_container import AbstractDataContainer 19 | from fk.core.abstract_settings import AbstractSettings 20 | from fk.core.user import User 21 | 22 | ADMIN_USER = 'admin@local.host' 23 | 24 | 25 | class Tenant(AbstractDataContainer[User, None]): 26 | """Tenant is the root of the data hierarchy in Flowkeeper Client. 27 | It contains users and has no parent.""" 28 | 29 | _settings: AbstractSettings 30 | 31 | def __init__(self, settings: AbstractSettings): 32 | super().__init__('Flowkeeper Desktop Client', 33 | None, 34 | '0', 35 | datetime.datetime.now(datetime.timezone.utc)) 36 | self._settings = settings 37 | self[ADMIN_USER] = User( 38 | self, 39 | ADMIN_USER, 40 | 'System', 41 | datetime.datetime.now(datetime.timezone.utc), 42 | True 43 | ) 44 | 45 | def get_settings(self) -> AbstractSettings: 46 | return self._settings 47 | 48 | def get_user(self, identity: str) -> User: 49 | return self[identity] 50 | 51 | def get_current_user(self) -> User: 52 | return self[self._settings.get_username()] 53 | -------------------------------------------------------------------------------- /src/fk/desktop/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/src/fk/desktop/__init__.py -------------------------------------------------------------------------------- /src/fk/e2e/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/src/fk/e2e/__init__.py -------------------------------------------------------------------------------- /src/fk/qt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/src/fk/qt/__init__.py -------------------------------------------------------------------------------- /src/fk/qt/qt_filesystem_watcher.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import Callable 17 | 18 | from PySide6 import QtCore 19 | 20 | from fk.core.abstract_filesystem_watcher import AbstractFilesystemWatcher 21 | 22 | 23 | class QtFilesystemWatcher(AbstractFilesystemWatcher): 24 | _connections: dict[str, list[Callable]] 25 | _watcher: QtCore.QFileSystemWatcher 26 | 27 | def __init__(self): 28 | self._connections = dict() 29 | self._watcher = QtCore.QFileSystemWatcher() 30 | self._watcher.fileChanged.connect(lambda f: self._on_file_change(f)) 31 | 32 | def watch(self, filename: str, callback: Callable[[str], None]): 33 | self._watcher.addPath(filename) 34 | if filename not in self._connections: 35 | self._connections[filename] = list() 36 | self._connections[filename].append(callback) 37 | 38 | def unwatch(self, filename: str) -> None: 39 | if filename in self._connections: 40 | self._watcher.removePath(filename) 41 | del self._connections[filename] 42 | 43 | def unwatch_all(self) -> None: 44 | self._watcher.removePaths(self._watcher.files()) 45 | self._connections.clear() 46 | 47 | def _on_file_change(self, filename: str) -> None: 48 | for callback in self._connections[filename]: 49 | callback(filename) 50 | -------------------------------------------------------------------------------- /src/fk/qt/qt_invoker.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PySide6.QtCore import QEvent, QObject, QCoreApplication 17 | 18 | 19 | class InvokeEvent(QEvent): 20 | EVENT_TYPE = QEvent.Type(QEvent.registerEventType()) 21 | 22 | def __init__(self, fn, **kwargs): 23 | QEvent.__init__(self, InvokeEvent.EVENT_TYPE) 24 | self.fn = fn 25 | self.kwargs = kwargs 26 | 27 | 28 | class Invoker(QObject): 29 | def event(self, e): 30 | e.fn(**e.kwargs) 31 | return True 32 | 33 | 34 | _invoker = Invoker() 35 | 36 | 37 | def invoke_in_main_thread(fn, **kwargs): 38 | QCoreApplication.postEvent(_invoker, 39 | InvokeEvent(fn, **kwargs)) 40 | -------------------------------------------------------------------------------- /src/fk/qt/render/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/src/fk/qt/render/__init__.py -------------------------------------------------------------------------------- /src/fk/qt/theme_change_event_filter.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import logging 17 | 18 | from PySide6.QtCore import QObject, QEvent 19 | from PySide6.QtGui import Qt 20 | from PySide6.QtWidgets import QMainWindow, QApplication 21 | 22 | from fk.core.abstract_settings import AbstractSettings 23 | 24 | logger = logging.getLogger(__name__) 25 | 26 | 27 | class ThemeChangeEventFilter(QMainWindow): 28 | _window: QMainWindow 29 | _settings: AbstractSettings 30 | 31 | # We need to use it, because Windows sometimes triggers dozens of ThemeChange events at once, and 32 | # we don't want to translate all of them into our settings change events, which might be too slow. 33 | _last_value: Qt.ColorScheme 34 | 35 | def __init__(self, 36 | window: QMainWindow, 37 | settings: AbstractSettings): 38 | super().__init__() 39 | self._window = window 40 | self._settings = settings 41 | self._last_value = QApplication.styleHints().colorScheme() 42 | 43 | def eventFilter(self, widget: QObject, event: QEvent) -> bool: 44 | if event.type() == QEvent.Type.ThemeChange and widget == self._window: 45 | if self._settings.get('Application.theme') == 'auto': 46 | new_theme = QApplication.styleHints().colorScheme() 47 | logger.debug(f'Theme changed from {self._last_value} to {new_theme}') 48 | if new_theme != self._last_value: 49 | self._settings.set({ 50 | 'Application.theme': 'auto' 51 | }, force_fire=True) 52 | self._last_value = new_theme 53 | return False 54 | -------------------------------------------------------------------------------- /src/fk/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/src/fk/tests/__init__.py -------------------------------------------------------------------------------- /src/fk/tests/abstract_test_case.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import abc 17 | from typing import Callable 18 | from unittest import TestCase 19 | 20 | from fk.core.abstract_event_emitter import AbstractEventEmitter 21 | 22 | 23 | class AbstractTestCase(TestCase, abc.ABC): 24 | def assert_events(self, 25 | emitter: AbstractEventEmitter, 26 | action: Callable, 27 | expected_events: list[str], 28 | expected_params: dict[str, dict[str, any]] = None): 29 | fired = list() 30 | 31 | def on_event(event, **kwargs): 32 | fired.append(event) 33 | if expected_params is not None: 34 | params = expected_params.get(event) 35 | if params is not None: 36 | for name in params: 37 | self.assertIn(name, kwargs) 38 | self.assertEqual(kwargs[name], params[name]) 39 | 40 | emitter.on('*', on_event) 41 | action() 42 | self.assertEqual(len(fired), len(expected_events)) 43 | for i in range(len(expected_events)): 44 | self.assertEqual(fired[i], expected_events[i]) 45 | -------------------------------------------------------------------------------- /src/fk/tests/fixtures/test-tags.txt: -------------------------------------------------------------------------------- 1 | 1, 2023-10-18 15:13:24.607620+00:00, admin@local.host: CreateUser("alice@flowkeeper.org", "Alice Cooper") 2 | 2, 2023-10-21 16:31:55.992372+00:00, alice@flowkeeper.org: CreateBacklog("123-456-789", "Sample backlog") 3 | 3, 2023-10-21 16:32:55.992372+00:00, alice@flowkeeper.org: CreateWorkitem("w11", "123-456-789", "#one #1 # ###десять #_TAG #one| #1") 4 | 4, 2023-10-21 16:33:55.992372+00:00, alice@flowkeeper.org: CreateWorkitem("w12", "123-456-789", "#ONE #One #one #two") 5 | -------------------------------------------------------------------------------- /src/fk/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flowkeeper-org/fk-desktop/9466afc05501642e45834343849dc78b2f1bfea1/src/fk/tools/__init__.py -------------------------------------------------------------------------------- /src/fk/tools/minimal_actions.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import logging 17 | 18 | from PySide6.QtWidgets import QMenuBar 19 | 20 | from fk.desktop.application import Application 21 | from fk.qt.backlog_tableview import BacklogTableView 22 | from fk.qt.focus_widget import FocusWidget 23 | from fk.qt.user_tableview import UserTableView 24 | from fk.qt.workitem_tableview import WorkitemTableView 25 | from fk.tools.minimal_common import MinimalCommon 26 | 27 | logger = logging.getLogger(__name__) 28 | 29 | mc = MinimalCommon() 30 | 31 | Application.define_actions(mc.get_actions()) 32 | BacklogTableView.define_actions(mc.get_actions()) 33 | UserTableView.define_actions(mc.get_actions()) 34 | WorkitemTableView.define_actions(mc.get_actions()) 35 | FocusWidget.define_actions(mc.get_actions()) 36 | 37 | mc.get_actions().bind('application', mc.get_app()) 38 | 39 | menu = QMenuBar(mc.get_window()) 40 | menu.addActions(list(mc.get_actions().values())) 41 | mc.get_window().setCentralWidget(menu) 42 | 43 | logger.debug('All actions:') 44 | for action in mc.get_actions().values(): 45 | logger.debug(action.objectName()) 46 | 47 | mc.main_loop() 48 | -------------------------------------------------------------------------------- /src/fk/tools/minimal_audio.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PySide6.QtWidgets import QPushButton 17 | 18 | from fk.qt.audio_player import AudioPlayer 19 | from fk.tools.minimal_common import MinimalCommon 20 | 21 | mc = MinimalCommon() 22 | 23 | audio = AudioPlayer(mc.get_window(), mc.get_app().get_source_holder(), mc.get_settings()) 24 | 25 | button = QPushButton(mc.get_window()) 26 | button.setText('Audio') 27 | mc.get_window().setCentralWidget(button) 28 | 29 | mc.main_loop() 30 | -------------------------------------------------------------------------------- /src/fk/tools/minimal_auth.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PySide6.QtWidgets import QPushButton 17 | 18 | from fk.qt.oauth import authenticate 19 | from fk.tools.minimal_common import MinimalCommon 20 | 21 | mc = MinimalCommon(initialize_source=False) 22 | 23 | button = QPushButton(mc.get_window()) 24 | button.setText('Login...') 25 | button.clicked.connect(lambda: authenticate(mc.get_app(), print)) 26 | mc.get_window().setCentralWidget(button) 27 | 28 | mc.main_loop() 29 | -------------------------------------------------------------------------------- /src/fk/tools/minimal_backlogs.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from fk.qt.backlog_tableview import BacklogTableView 17 | from fk.tools.minimal_common import MinimalCommon 18 | 19 | mc = MinimalCommon(lambda root: backlogs_table.upstream_selected(root.get_current_user())) 20 | 21 | BacklogTableView.define_actions(mc.get_actions()) 22 | backlogs_table: BacklogTableView = BacklogTableView(mc.get_window(), mc.get_app(), mc.get_app().get_source_holder(), mc.get_actions()) 23 | mc.get_actions().bind('backlogs_table', backlogs_table) 24 | 25 | mc.get_window().setCentralWidget(backlogs_table) 26 | 27 | mc.main_loop() 28 | -------------------------------------------------------------------------------- /src/fk/tools/minimal_focus.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from fk.core.timer import PomodoroTimer 17 | from fk.qt.focus_widget import FocusWidget 18 | from fk.qt.qt_timer import QtTimer 19 | from fk.tools.minimal_common import MinimalCommon 20 | 21 | mc = MinimalCommon() 22 | 23 | pomodoro_timer = PomodoroTimer(QtTimer("Pomodoro Tick"), QtTimer("Pomodoro Transition"), mc.get_settings(), mc.get_app().get_source_holder()) 24 | FocusWidget.define_actions(mc.get_actions()) 25 | focus = FocusWidget(mc.get_window(), 26 | mc.get_app(), 27 | pomodoro_timer, 28 | mc.get_app().get_source_holder(), 29 | mc.get_settings(), 30 | mc.get_actions(), 31 | mc.get_settings().get('Application.focus_flavor')) 32 | mc.get_actions().bind('focus', focus) 33 | mc.get_window().setCentralWidget(focus) 34 | 35 | mc.main_loop() 36 | -------------------------------------------------------------------------------- /src/fk/tools/minimal_settings.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from fk.desktop.settings import SettingsDialog 17 | from fk.tools.minimal_common import MinimalCommon 18 | 19 | mc = MinimalCommon() 20 | 21 | dialog = SettingsDialog(mc.get_settings()) 22 | mc.get_window().setCentralWidget(dialog) 23 | 24 | mc.main_loop() 25 | -------------------------------------------------------------------------------- /src/fk/tools/minimal_timer_widget.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | from fk.core.timer import PomodoroTimer 18 | from fk.qt.focus_widget import FocusWidget 19 | from fk.qt.qt_timer import QtTimer 20 | from fk.qt.timer_widget import TimerWidget 21 | from fk.tools.minimal_common import MinimalCommon 22 | 23 | mc = MinimalCommon() 24 | 25 | pomodoro_timer = PomodoroTimer(QtTimer("Pomodoro Tick"), QtTimer("Pomodoro Transition"), mc.get_settings(), mc.get_app().get_source_holder()) 26 | FocusWidget.define_actions(mc.get_actions()) 27 | 28 | action = mc.get_actions()['focus.voidPomodoro'] 29 | 30 | timer = TimerWidget(mc.get_window(), 31 | 'timer', 32 | #mc.get_settings().get('Application.focus_flavor'), 33 | 'minimal', 34 | None, 35 | 500) 36 | timer.set_values(1 * 60 * 60 + 5 * 60 + 20, 37 | None, 38 | None, 39 | None, 40 | 'tracking') 41 | mc.get_window().setCentralWidget(timer) 42 | 43 | mc.main_loop() 44 | -------------------------------------------------------------------------------- /src/fk/tools/minimal_tray.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PySide6.QtWidgets import QPushButton 17 | 18 | from fk.core.pomodoro import Pomodoro, POMODORO_TYPE_NORMAL 19 | from fk.core.timer import PomodoroTimer 20 | from fk.core.workitem import Workitem 21 | from fk.qt.render.classic_timer_renderer import ClassicTimerRenderer 22 | from fk.qt.render.minimal_timer_renderer import MinimalTimerRenderer 23 | from fk.qt.qt_timer import QtTimer 24 | from fk.qt.tray_icon import TrayIcon 25 | from fk.tools.minimal_common import MinimalCommon 26 | 27 | mc = MinimalCommon() 28 | 29 | app = mc.get_app() 30 | window = mc.get_window() 31 | actions = mc.get_actions() 32 | 33 | pomodoro_timer = PomodoroTimer(QtTimer("Pomodoro Tick"), QtTimer("Pomodoro Transition"), mc.get_settings(), app.get_source_holder()) 34 | tray = TrayIcon(window, pomodoro_timer, app.get_source_holder(), actions, 48, MinimalTimerRenderer, True) # TODO: Detect automatically 35 | 36 | tray.setVisible(True) 37 | tray.mode_changed('idle', 'working') 38 | wi = Workitem('Test', '123', None, None) 39 | 40 | value = 0 41 | pomodoro_timer._state = 'work' 42 | 43 | 44 | def tick(): 45 | global value 46 | global pomodoro_timer 47 | tray.tick(Pomodoro(1, False, pomodoro_timer._state, 5000, 5000, POMODORO_TYPE_NORMAL, "123", wi, None), 48 | 'State', 49 | value, 50 | 10, 51 | 'working') 52 | value += 1 53 | if value > 10: 54 | if pomodoro_timer._state == 'work': 55 | pomodoro_timer._state = 'rest' 56 | else: 57 | pomodoro_timer._state = 'work' 58 | value = 0 59 | 60 | 61 | button = QPushButton(window) 62 | button.setText('See tray icon') 63 | button.clicked.connect(lambda: tick()) 64 | window.setCentralWidget(button) 65 | 66 | mc.main_loop() 67 | -------------------------------------------------------------------------------- /src/fk/tools/minimal_tutorial.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PySide6.QtCore import QPoint 17 | from PySide6.QtWidgets import QPushButton, QWidget 18 | 19 | from fk.qt.info_overlay import show_tutorial 20 | from fk.tools.minimal_common import MinimalCommon 21 | 22 | 23 | def get_tutorial_step(step: int, widget: QWidget) -> (str, QPoint, str): 24 | if step == 1: 25 | return 'Welcome to Flowkeeper!', widget.mapToGlobal(widget.rect().topLeft()), 'info' 26 | elif step == 2: 27 | return 'Tutorial step 2 with a somewhat longer description', widget.mapToGlobal(widget.rect().bottomRight()), 'arrow' 28 | elif step == 3: 29 | return 'Thank you!', widget.mapToGlobal(widget.rect().center()), 'info' 30 | 31 | 32 | mc = MinimalCommon(initialize_source=False) 33 | button = QPushButton(mc.get_window()) 34 | button.setFixedWidth(300) 35 | button.setText('Tutorial') 36 | button.clicked.connect(lambda: show_tutorial(lambda step: get_tutorial_step(step, button), None, True)) 37 | mc.get_window().setCentralWidget(button) 38 | 39 | mc.main_loop() 40 | -------------------------------------------------------------------------------- /src/fk/tools/minimal_update.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PySide6.QtWidgets import QTextEdit 17 | from semantic_version import Version 18 | 19 | from fk.qt.app_version import get_current_version, get_latest_version 20 | from fk.tools.minimal_common import MinimalCommon 21 | 22 | mc = MinimalCommon(initialize_source=False) 23 | 24 | txt = QTextEdit(mc.get_window()) 25 | 26 | 27 | def update(latest: Version, changelog: str): 28 | txt.setMarkdown(f'Current version: {get_current_version()}\n\nLatest version: {latest}\n\nChangelog: \n\n{changelog}') 29 | 30 | 31 | get_latest_version(mc.get_app(), update) 32 | mc.get_window().setCentralWidget(txt) 33 | 34 | mc.main_loop() 35 | -------------------------------------------------------------------------------- /src/fk/tools/minimal_users.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from fk.core.tenant import Tenant 17 | from fk.qt.user_tableview import UserTableView 18 | from fk.tools.minimal_common import MinimalCommon 19 | 20 | 21 | def on_data(root: Tenant): 22 | users_table.upstream_selected(root) 23 | 24 | 25 | mc = MinimalCommon(on_data) 26 | 27 | app = mc.get_app() 28 | window = mc.get_window() 29 | actions = mc.get_actions() 30 | 31 | UserTableView.define_actions(actions) 32 | users_table: UserTableView = UserTableView(window, app, app.get_source_holder(), actions) 33 | actions.bind('users_table', users_table) 34 | window.setCentralWidget(users_table) 35 | 36 | mc.main_loop() 37 | -------------------------------------------------------------------------------- /src/fk/tools/minimal_workitems.py: -------------------------------------------------------------------------------- 1 | # Flowkeeper - Pomodoro timer for power users and teams 2 | # Copyright (c) 2023 Constantine Kulak 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from fk.core.tenant import Tenant 17 | from fk.qt.workitem_tableview import WorkitemTableView 18 | from fk.tools.minimal_common import MinimalCommon 19 | 20 | 21 | def select_first_backlog(data: Tenant): 22 | backlogs = list(data.get_current_user().values()) 23 | workitems_table.upstream_selected(backlogs[0]) 24 | 25 | 26 | mc = MinimalCommon(select_first_backlog) 27 | 28 | mc.get_window().resize(600, 400) 29 | WorkitemTableView.define_actions(mc.get_actions()) 30 | workitems_table: WorkitemTableView = WorkitemTableView(mc.get_window(), 31 | mc.get_app(), 32 | mc.get_app().get_source_holder(), 33 | None, 34 | mc.get_actions()) 35 | mc.get_actions().bind('workitems_table', workitems_table) 36 | mc.get_window().setCentralWidget(workitems_table) 37 | 38 | mc.main_loop() 39 | -------------------------------------------------------------------------------- /ws-tests.md: -------------------------------------------------------------------------------- 1 | Lost connection between server and client 2 | Client knows about it 3 | Client doesn't know 4 | 5 | Server is down 6 | Client connects w/cache 7 | Client connects w/o cache 8 | 9 | Client is down 10 | 11 | Reconnect 12 | 13 | Concurrent modification 14 | Same backlog 15 | Same workitem 16 | Different backlogs 17 | 18 | Wrong decryption key 19 | 20 | Someone sends a message before the replay is completed 21 | 22 | Replay contains many strategies 23 | One message 24 | Multiple messages 25 | 26 | Cache is deleted 27 | 28 | Redo log is deleted 29 | 30 | Relogin with another account 31 | 32 | With redo log the UI is not locked when the client goes offline 33 | 34 | Send strategies "in the past", then reconnect -- do they appear correctly? 35 | 36 | --------------------------------------------------------------------------------