feat: List Github notifications
7
.envrc
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Automatically sets up your devbox environment whenever you cd into this
|
||||||
|
# directory via our direnv integration:
|
||||||
|
|
||||||
|
eval "$(devbox generate direnv --print-envrc)"
|
||||||
|
|
||||||
|
# check out https://www.jetpack.io/devbox/docs/ide_configuration/direnv/
|
||||||
|
# for more details
|
||||||
17
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
fail_fast: true
|
||||||
|
repos:
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
- id: lint
|
||||||
|
name: lint
|
||||||
|
description: Lint Raycast extension code
|
||||||
|
entry: just lint
|
||||||
|
language: system
|
||||||
|
files: \.ts.?$
|
||||||
|
pass_filenames: false
|
||||||
|
args: []
|
||||||
|
stages: [commit]
|
||||||
|
- repo: https://github.com/crate-ci/committed
|
||||||
|
rev: v1.0.5
|
||||||
|
hooks:
|
||||||
|
- id: committed
|
||||||
201
LICENSE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2022 David Rousselie
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
Before Width: | Height: | Size: 123 KiB |
1
assets/github-discussion-closed.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 2.75C0 1.783.784 1 1.75 1h12.5c.967 0 1.75.783 1.75 1.75v9.5A1.75 1.75 0 0 1 14.25 14H8.061l-2.574 2.573A1.457 1.457 0 0 1 3 15.543V14H1.75A1.75 1.75 0 0 1 0 12.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.189l2.72-2.719a.747.747 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm20.5 6h-3.5a.75.75 0 0 1 0-1.5h3.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 22.25 20H21v1.543a1.457 1.457 0 0 1-2.487 1.03L15.939 20H10.75A1.75 1.75 0 0 1 9 18.25v-1.465a.75.75 0 0 1 1.5 0v1.465c0 .138.112.25.25.25h5.5c.199 0 .39.079.53.22l2.72 2.719V19.25a.75.75 0 0 1 .75-.75h2a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm-9.72-3.22-5 5a.747.747 0 0 1-1.06 0l-2.5-2.5a.749.749 0 1 1 1.06-1.06L7 8.689l4.47-4.469a.749.749 0 1 1 1.06 1.06Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 890 B |
1
assets/github-discussion-duplicate.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 2.75C0 1.783.784 1 1.75 1h12.5c.967 0 1.75.783 1.75 1.75v9.5A1.75 1.75 0 0 1 14.25 14H8.061l-2.574 2.573A1.457 1.457 0 0 1 3 15.543V14H1.75A1.75 1.75 0 0 1 0 12.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.189l2.72-2.719a.747.747 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm20.5 6h-3.5a.75.75 0 0 1 0-1.5h3.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 22.25 20H21v1.543a1.457 1.457 0 0 1-2.487 1.03L15.939 20H10.75A1.75 1.75 0 0 1 9 18.25v-1.465a.75.75 0 0 1 1.5 0v1.465c0 .138.112.25.25.25h5.5c.199 0 .39.079.53.22l2.72 2.719V19.25a.75.75 0 0 1 .75-.75h2a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25ZM11.28 5.53l-5 5a.749.749 0 1 1-1.06-1.06l5-5a.749.749 0 1 1 1.06 1.06Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 845 B |
1
assets/github-discussion-opened.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M1.75 1h12.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 14.25 14H8.061l-2.574 2.573A1.458 1.458 0 0 1 3 15.543V14H1.75A1.75 1.75 0 0 1 0 12.25v-9.5C0 1.784.784 1 1.75 1ZM1.5 2.75v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25Z"></path><path d="M22.5 8.75a.25.25 0 0 0-.25-.25h-3.5a.75.75 0 0 1 0-1.5h3.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 22.25 20H21v1.543a1.457 1.457 0 0 1-2.487 1.03L15.939 20H10.75A1.75 1.75 0 0 1 9 18.25v-1.465a.75.75 0 0 1 1.5 0v1.465c0 .138.112.25.25.25h5.5a.75.75 0 0 1 .53.22l2.72 2.72v-2.19a.75.75 0 0 1 .75-.75h2a.25.25 0 0 0 .25-.25v-9.5Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 801 B |
1
assets/github-discussion-outdated.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 2.75C0 1.783.784 1 1.75 1h12.5c.967 0 1.75.783 1.75 1.75v9.5A1.75 1.75 0 0 1 14.25 14H8.061l-2.574 2.573A1.457 1.457 0 0 1 3 15.543V14H1.75A1.75 1.75 0 0 1 0 12.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.189l2.72-2.719a.747.747 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm20.5 6h-3.5a.75.75 0 0 1 0-1.5h3.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 22.25 20H21v1.543a1.457 1.457 0 0 1-2.487 1.03L15.939 20H10.75A1.75 1.75 0 0 1 9 18.25v-1.465a.75.75 0 0 1 1.5 0v1.465c0 .138.112.25.25.25h5.5c.199 0 .39.079.53.22l2.72 2.719V19.25a.75.75 0 0 1 .75-.75h2a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25ZM8.5 4.75v3.14l1.15.488a.608.608 0 0 1 .037.017l1.393.681a.75.75 0 0 1-.66 1.348l-1.374-.673-1.589-.674A.751.751 0 0 1 7 8.386V4.75a.75.75 0 0 1 1.5 0Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 925 B |
1
assets/github-logo-dark.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/></svg>
|
||||||
|
After Width: | Height: | Size: 963 B |
1
assets/github-logo-light.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#fff"/></svg>
|
||||||
|
After Width: | Height: | Size: 960 B |
1
assets/github-pullrequest-closed.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M22.266 2.711a.75.75 0 1 0-1.061-1.06l-1.983 1.983-1.984-1.983a.75.75 0 1 0-1.06 1.06l1.983 1.983-1.983 1.984a.75.75 0 0 0 1.06 1.06l1.984-1.983 1.983 1.983a.75.75 0 0 0 1.06-1.06l-1.983-1.984 1.984-1.983ZM4.75 1.5a3.25 3.25 0 0 1 .745 6.414A.827.827 0 0 1 5.5 8v8a.827.827 0 0 1-.005.086A3.25 3.25 0 0 1 4.75 22.5a3.25 3.25 0 0 1-.745-6.414A.827.827 0 0 1 4 16V8c0-.029.002-.057.005-.086A3.25 3.25 0 0 1 4.75 1.5ZM16 19.25a3.252 3.252 0 0 1 2.5-3.163V9.625a.75.75 0 0 1 1.5 0v6.462a3.252 3.252 0 0 1-.75 6.413A3.25 3.25 0 0 1 16 19.25ZM3 4.75a1.75 1.75 0 1 0 3.501-.001A1.75 1.75 0 0 0 3 4.75Zm0 14.5a1.75 1.75 0 1 0 3.501-.001A1.75 1.75 0 0 0 3 19.25Zm16.25-1.75a1.75 1.75 0 1 0 .001 3.501 1.75 1.75 0 0 0-.001-3.501Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 826 B |
1
assets/github-pullrequest-draft.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M4.75 1.5a3.25 3.25 0 0 1 .745 6.414A.827.827 0 0 1 5.5 8v8a.827.827 0 0 1-.005.086A3.25 3.25 0 0 1 4.75 22.5a3.25 3.25 0 0 1-.745-6.414A.827.827 0 0 1 4 16V8c0-.029.002-.057.005-.086A3.25 3.25 0 0 1 4.75 1.5ZM16 19.25a3.25 3.25 0 1 1 6.5 0 3.25 3.25 0 0 1-6.5 0ZM3 4.75a1.75 1.75 0 1 0 3.501-.001A1.75 1.75 0 0 0 3 4.75Zm0 14.5a1.75 1.75 0 1 0 3.501-.001A1.75 1.75 0 0 0 3 19.25Zm16.25-1.75a1.75 1.75 0 1 0 .001 3.501 1.75 1.75 0 0 0-.001-3.501Zm0-11.5a1.75 1.75 0 1 0 0-3.5 1.75 1.75 0 0 0 0 3.5ZM21 11.25a1.75 1.75 0 1 1-3.5 0 1.75 1.75 0 0 1 3.5 0Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 659 B |
1
assets/github-pullrequest.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M16 19.25a3.25 3.25 0 1 1 6.5 0 3.25 3.25 0 0 1-6.5 0Zm-14.5 0a3.25 3.25 0 1 1 6.5 0 3.25 3.25 0 0 1-6.5 0Zm0-14.5a3.25 3.25 0 1 1 6.5 0 3.25 3.25 0 0 1-6.5 0ZM4.75 3a1.75 1.75 0 1 0 .001 3.501A1.75 1.75 0 0 0 4.75 3Zm0 14.5a1.75 1.75 0 1 0 .001 3.501A1.75 1.75 0 0 0 4.75 17.5Zm14.5 0a1.75 1.75 0 1 0 .001 3.501 1.75 1.75 0 0 0-.001-3.501Z"></path><path d="M13.405 1.72a.75.75 0 0 1 0 1.06L12.185 4h4.065A3.75 3.75 0 0 1 20 7.75v8.75a.75.75 0 0 1-1.5 0V7.75a2.25 2.25 0 0 0-2.25-2.25h-4.064l1.22 1.22a.75.75 0 0 1-1.061 1.06l-2.5-2.5a.75.75 0 0 1 0-1.06l2.5-2.5a.75.75 0 0 1 1.06 0ZM4.75 7.25A.75.75 0 0 1 5.5 8v8A.75.75 0 0 1 4 16V8a.75.75 0 0 1 .75-.75Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 763 B |
1
assets/google-mail-logo-dark.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="0 0 50 50" fill="#000"><title>Google Mail</title><path d="M 43.753906 6.4023438 C 42.53621 6.3489969 41.294792 6.712898 40.271484 7.46875 L 37.525391 9.4960938 L 25 18.755859 L 12.591797 9.5839844 A 1.0001 1.0001 0 0 0 11.949219 9.3007812 L 12.199219 9.3007812 L 9.734375 7.4765625 C 8.7104042 6.7188363 7.4671493 6.3528895 6.2480469 6.40625 C 5.0289444 6.4596105 3.8349462 6.9314667 2.9082031 7.8457031 C 1.7309454 9.0063798 1 10.629831 1 12.410156 L 1 15.84375 A 1.0001 1.0001 0 0 0 1 16.138672 L 1 39.5 C 1 41.421188 2.5788117 43 4.5 43 L 12 43 A 1.0001 1.0001 0 0 0 13 42 L 13 25.373047 L 24.40625 33.804688 A 1.0001 1.0001 0 0 0 25.59375 33.804688 L 37 25.373047 L 37 42 A 1.0001 1.0001 0 0 0 38 43 L 45.5 43 C 47.421188 43 49 41.421188 49 39.5 L 49 16.119141 A 1.0001 1.0001 0 0 0 49 15.859375 L 49 12.410156 C 49 10.6517 48.290455 9.0357821 47.128906 7.8730469 C 47.095336 7.8394769 47.084086 7.83018 47.097656 7.84375 A 1.0001 1.0001 0 0 0 47.091797 7.8378906 C 46.165242 6.9256756 44.971603 6.4556905 43.753906 6.4023438 z M 43.644531 8.4003906 C 44.400835 8.4300436 45.134049 8.7168876 45.689453 9.2636719 C 45.708363 9.2823439 45.722171 9.2964424 45.712891 9.2871094 C 46.50934 10.084374 47 11.188613 47 12.410156 L 47 15.496094 L 39 21.408203 L 39 11 A 1.0001 1.0001 0 0 0 38.996094 10.898438 L 41.458984 9.078125 A 1.0001 1.0001 0 0 0 41.460938 9.078125 C 42.109578 8.598977 42.888228 8.3707375 43.644531 8.4003906 z M 6.3574219 8.40625 C 7.1145694 8.37661 7.8958927 8.6037105 8.5449219 9.0839844 L 11.003906 10.902344 A 1.0001 1.0001 0 0 0 11 11 L 11 21.408203 L 3 15.496094 L 3 12.410156 C 3 11.174482 3.5017577 10.068855 4.3125 9.2695312 C 4.8677569 8.7217677 5.6002743 8.4358895 6.3574219 8.40625 z M 37 12.371094 L 37 22.886719 L 25 31.755859 L 13 22.886719 L 13 12.373047 L 24.40625 20.804688 A 1.0001 1.0001 0 0 0 25.59375 20.804688 L 37 12.371094 z M 3 17.982422 L 11 23.896484 L 11 41 L 4.5 41 C 3.6591883 41 3 40.340812 3 39.5 L 3 17.982422 z M 47 17.982422 L 47 39.5 C 47 40.340812 46.340812 41 45.5 41 L 39 41 L 39 23.896484 L 47 17.982422 z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
1
assets/google-mail-logo-light.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="0 0 50 50" fill="currentColor"><title>Google Mail</title><path d="M 43.753906 6.4023438 C 42.53621 6.3489969 41.294792 6.712898 40.271484 7.46875 L 37.525391 9.4960938 L 25 18.755859 L 12.591797 9.5839844 A 1.0001 1.0001 0 0 0 11.949219 9.3007812 L 12.199219 9.3007812 L 9.734375 7.4765625 C 8.7104042 6.7188363 7.4671493 6.3528895 6.2480469 6.40625 C 5.0289444 6.4596105 3.8349462 6.9314667 2.9082031 7.8457031 C 1.7309454 9.0063798 1 10.629831 1 12.410156 L 1 15.84375 A 1.0001 1.0001 0 0 0 1 16.138672 L 1 39.5 C 1 41.421188 2.5788117 43 4.5 43 L 12 43 A 1.0001 1.0001 0 0 0 13 42 L 13 25.373047 L 24.40625 33.804688 A 1.0001 1.0001 0 0 0 25.59375 33.804688 L 37 25.373047 L 37 42 A 1.0001 1.0001 0 0 0 38 43 L 45.5 43 C 47.421188 43 49 41.421188 49 39.5 L 49 16.119141 A 1.0001 1.0001 0 0 0 49 15.859375 L 49 12.410156 C 49 10.6517 48.290455 9.0357821 47.128906 7.8730469 C 47.095336 7.8394769 47.084086 7.83018 47.097656 7.84375 A 1.0001 1.0001 0 0 0 47.091797 7.8378906 C 46.165242 6.9256756 44.971603 6.4556905 43.753906 6.4023438 z M 43.644531 8.4003906 C 44.400835 8.4300436 45.134049 8.7168876 45.689453 9.2636719 C 45.708363 9.2823439 45.722171 9.2964424 45.712891 9.2871094 C 46.50934 10.084374 47 11.188613 47 12.410156 L 47 15.496094 L 39 21.408203 L 39 11 A 1.0001 1.0001 0 0 0 38.996094 10.898438 L 41.458984 9.078125 A 1.0001 1.0001 0 0 0 41.460938 9.078125 C 42.109578 8.598977 42.888228 8.3707375 43.644531 8.4003906 z M 6.3574219 8.40625 C 7.1145694 8.37661 7.8958927 8.6037105 8.5449219 9.0839844 L 11.003906 10.902344 A 1.0001 1.0001 0 0 0 11 11 L 11 21.408203 L 3 15.496094 L 3 12.410156 C 3 11.174482 3.5017577 10.068855 4.3125 9.2695312 C 4.8677569 8.7217677 5.6002743 8.4358895 6.3574219 8.40625 z M 37 12.371094 L 37 22.886719 L 25 31.755859 L 13 22.886719 L 13 12.373047 L 24.40625 20.804688 A 1.0001 1.0001 0 0 0 25.59375 20.804688 L 37 12.371094 z M 3 17.982422 L 11 23.896484 L 11 41 L 4.5 41 C 3.6591883 41 3 40.340812 3 39.5 L 3 17.982422 z M 47 17.982422 L 47 39.5 C 47 40.340812 46.340812 41 45.5 41 L 39 41 L 39 23.896484 L 47 17.982422 z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
5
assets/linear-logo-dark.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="#222326" width="200" height="200" viewBox="0 0 100 100">
|
||||||
|
<path
|
||||||
|
d="M1.22541 61.5228c-.2225-.9485.90748-1.5459 1.59638-.857L39.3342 97.1782c.6889.6889.0915 1.8189-.857 1.5964C20.0515 94.4522 5.54779 79.9485 1.22541 61.5228ZM.00189135 46.8891c-.01764375.2833.08887215.5599.28957165.7606L52.3503 99.7085c.2007.2007.4773.3075.7606.2896 2.3692-.1476 4.6938-.46 6.9624-.9259.7645-.157 1.0301-1.0963.4782-1.6481L2.57595 39.4485c-.55186-.5519-1.49117-.2863-1.648174.4782-.465915 2.2686-.77832 4.5932-.92588465 6.9624ZM4.21093 29.7054c-.16649.3738-.08169.8106.20765 1.1l64.77602 64.776c.2894.2894.7262.3742 1.1.2077 1.7861-.7956 3.5171-1.6927 5.1855-2.684.5521-.328.6373-1.0867.1832-1.5407L8.43566 24.3367c-.45409-.4541-1.21271-.3689-1.54074.1832-.99132 1.6684-1.88843 3.3994-2.68399 5.1855ZM12.6587 18.074c-.3701-.3701-.393-.9637-.0443-1.3541C21.7795 6.45931 35.1114 0 49.9519 0 77.5927 0 100 22.4073 100 50.0481c0 14.8405-6.4593 28.1724-16.7199 37.3375-.3903.3487-.984.3258-1.3542-.0443L12.6587 18.074Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
5
assets/linear-logo-light.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="#fff" width="200" height="200" viewBox="0 0 100 100">
|
||||||
|
<path
|
||||||
|
d="M1.22541 61.5228c-.2225-.9485.90748-1.5459 1.59638-.857L39.3342 97.1782c.6889.6889.0915 1.8189-.857 1.5964C20.0515 94.4522 5.54779 79.9485 1.22541 61.5228ZM.00189135 46.8891c-.01764375.2833.08887215.5599.28957165.7606L52.3503 99.7085c.2007.2007.4773.3075.7606.2896 2.3692-.1476 4.6938-.46 6.9624-.9259.7645-.157 1.0301-1.0963.4782-1.6481L2.57595 39.4485c-.55186-.5519-1.49117-.2863-1.648174.4782-.465915 2.2686-.77832 4.5932-.92588465 6.9624ZM4.21093 29.7054c-.16649.3738-.08169.8106.20765 1.1l64.77602 64.776c.2894.2894.7262.3742 1.1.2077 1.7861-.7956 3.5171-1.6927 5.1855-2.684.5521-.328.6373-1.0867.1832-1.5407L8.43566 24.3367c-.45409-.4541-1.21271-.3689-1.54074.1832-.99132 1.6684-1.88843 3.3994-2.68399 5.1855ZM12.6587 18.074c-.3701-.3701-.393-.9637-.0443-1.3541C21.7795 6.45931 35.1114 0 49.9519 0 77.5927 0 100 22.4073 100 50.0481c0 14.8405-6.4593 28.1724-16.7199 37.3375-.3903.3487-.984.3258-1.3542-.0443L12.6587 18.074Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
1
assets/todoist-logo-dark.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="0 0 24 24" fill="#000" stroke="#000"><title>Todoist</title><path d="M21 0H3C1.35 0 0 1.35 0 3v3.858s3.854 2.24 4.098 2.38c.31.18.694.177 1.004 0 .26-.147 8.02-4.608 8.136-4.675.279-.161.58-.107.748-.01.164.097.606.348.84.48.232.134.221.502.013.622l-9.712 5.59c-.346.2-.69.204-1.048.002C3.478 10.907.998 9.463 0 8.882v2.02l4.098 2.38c.31.18.694.177 1.004 0 .26-.147 8.02-4.609 8.136-4.676.279-.16.58-.106.748-.008.164.096.606.347.84.48.232.133.221.5.013.62-.208.121-9.288 5.346-9.712 5.59-.346.2-.69.205-1.048.002C3.478 14.951.998 13.506 0 12.926v2.02l4.098 2.38c.31.18.694.177 1.004 0 .26-.147 8.02-4.609 8.136-4.676.279-.16.58-.106.748-.009.164.097.606.348.84.48.232.133.221.502.013.622l-9.712 5.59c-.346.199-.69.204-1.048.001C3.478 18.994.998 17.55 0 16.97V21c0 1.65 1.35 3 3 3h18c1.65 0 3-1.35 3-3V3c0-1.65-1.35-3-3-3z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 897 B |
1
assets/todoist-logo-light.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg fill="#FFF" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Todoist</title><path d="M21 0H3C1.35 0 0 1.35 0 3v3.858s3.854 2.24 4.098 2.38c.31.18.694.177 1.004 0 .26-.147 8.02-4.608 8.136-4.675.279-.161.58-.107.748-.01.164.097.606.348.84.48.232.134.221.502.013.622l-9.712 5.59c-.346.2-.69.204-1.048.002C3.478 10.907.998 9.463 0 8.882v2.02l4.098 2.38c.31.18.694.177 1.004 0 .26-.147 8.02-4.609 8.136-4.676.279-.16.58-.106.748-.008.164.096.606.347.84.48.232.133.221.5.013.62-.208.121-9.288 5.346-9.712 5.59-.346.2-.69.205-1.048.002C3.478 14.951.998 13.506 0 12.926v2.02l4.098 2.38c.31.18.694.177 1.004 0 .26-.147 8.02-4.609 8.136-4.676.279-.16.58-.106.748-.009.164.097.606.348.84.48.232.133.221.502.013.622l-9.712 5.59c-.346.199-.69.204-1.048.001C3.478 18.994.998 17.55 0 16.97V21c0 1.65 1.35 3 3 3h18c1.65 0 3-1.35 3-3V3c0-1.65-1.35-3-3-3z"/></svg>
|
||||||
|
After Width: | Height: | Size: 866 B |
BIN
assets/ui-logo-transparent.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
18
devbox.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
"nodejs@latest",
|
||||||
|
"nodePackages.prettier@latest",
|
||||||
|
"pre-commit@latest",
|
||||||
|
"just@latest"
|
||||||
|
],
|
||||||
|
"shell": {
|
||||||
|
"init_hook": [
|
||||||
|
"echo 'Welcome to devbox!' > /dev/null"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"test": [
|
||||||
|
"echo \"Error: no test specified\" && exit 1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
85
devbox.lock
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"lockfile_version": "1",
|
||||||
|
"packages": {
|
||||||
|
"just@latest": {
|
||||||
|
"last_modified": "2024-01-14T03:55:27Z",
|
||||||
|
"resolved": "github:NixOS/nixpkgs/dd5621df6dcb90122b50da5ec31c411a0de3e538#just",
|
||||||
|
"source": "devbox-search",
|
||||||
|
"version": "1.23.0",
|
||||||
|
"systems": {
|
||||||
|
"aarch64-darwin": {
|
||||||
|
"store_path": "/nix/store/vdj4pgs99brvnz55axgsd6q3cydz4g92-just-1.23.0"
|
||||||
|
},
|
||||||
|
"aarch64-linux": {
|
||||||
|
"store_path": "/nix/store/8nx31h529h85m39b97rspqfryr4cwy4p-just-1.23.0"
|
||||||
|
},
|
||||||
|
"x86_64-darwin": {
|
||||||
|
"store_path": "/nix/store/pl6kcpl9swl9r2xbmq34zaxcy58lxz42-just-1.23.0"
|
||||||
|
},
|
||||||
|
"x86_64-linux": {
|
||||||
|
"store_path": "/nix/store/mvxixdbl6z79p012gdz2qhys8h41blji-just-1.23.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nodePackages.prettier@latest": {
|
||||||
|
"last_modified": "2024-01-14T03:55:27Z",
|
||||||
|
"resolved": "github:NixOS/nixpkgs/dd5621df6dcb90122b50da5ec31c411a0de3e538#nodePackages.prettier",
|
||||||
|
"source": "devbox-search",
|
||||||
|
"version": "3.1.0",
|
||||||
|
"systems": {
|
||||||
|
"aarch64-darwin": {
|
||||||
|
"store_path": "/nix/store/k5zc646kj5dk2hw389wn7s7izj3073kl-prettier-3.1.0"
|
||||||
|
},
|
||||||
|
"aarch64-linux": {
|
||||||
|
"store_path": "/nix/store/k15crikrxnllb2piy1146slnj24b0s44-prettier-3.1.0"
|
||||||
|
},
|
||||||
|
"x86_64-darwin": {
|
||||||
|
"store_path": "/nix/store/wyvksdib2kszhrhsasrn9b9nlk0p6fi7-prettier-3.1.0"
|
||||||
|
},
|
||||||
|
"x86_64-linux": {
|
||||||
|
"store_path": "/nix/store/i9nq5lkzjpf73315qm0ic02i4y4zqykc-prettier-3.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nodejs@latest": {
|
||||||
|
"last_modified": "2024-01-14T03:55:27Z",
|
||||||
|
"resolved": "github:NixOS/nixpkgs/dd5621df6dcb90122b50da5ec31c411a0de3e538#nodejs_21",
|
||||||
|
"source": "devbox-search",
|
||||||
|
"version": "21.5.0",
|
||||||
|
"systems": {
|
||||||
|
"aarch64-darwin": {
|
||||||
|
"store_path": "/nix/store/ybpqk26vz7k9grapsgx0sd900s0sp4sa-nodejs-21.5.0"
|
||||||
|
},
|
||||||
|
"aarch64-linux": {
|
||||||
|
"store_path": "/nix/store/brnzb5xxgdx6bbicygz83ybi5inqp09v-nodejs-21.5.0"
|
||||||
|
},
|
||||||
|
"x86_64-darwin": {
|
||||||
|
"store_path": "/nix/store/yvgnx3lj8am9mqn30yr09sb4ia7qy3w8-nodejs-21.5.0"
|
||||||
|
},
|
||||||
|
"x86_64-linux": {
|
||||||
|
"store_path": "/nix/store/nxfirpvaycr7wqzwl6wqifpdrqn7is7x-nodejs-21.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pre-commit@latest": {
|
||||||
|
"last_modified": "2024-01-18T00:05:01Z",
|
||||||
|
"resolved": "github:NixOS/nixpkgs/921fb3319c2a296fc65048272d22f3db889d7292#pre-commit",
|
||||||
|
"source": "devbox-search",
|
||||||
|
"version": "3.6.0",
|
||||||
|
"systems": {
|
||||||
|
"aarch64-darwin": {
|
||||||
|
"store_path": "/nix/store/i49v43p1a33gmdib1hg0fp9chn1bzakb-pre-commit-3.6.0"
|
||||||
|
},
|
||||||
|
"aarch64-linux": {
|
||||||
|
"store_path": "/nix/store/pfhx6ckglrj4j0wv17nzms2qc3wrbszi-pre-commit-3.6.0"
|
||||||
|
},
|
||||||
|
"x86_64-darwin": {
|
||||||
|
"store_path": "/nix/store/l0nf5ivi8vbc35r5ppm7ak5hx6c7nf50-pre-commit-3.6.0"
|
||||||
|
},
|
||||||
|
"x86_64-linux": {
|
||||||
|
"store_path": "/nix/store/1j57wynzsd9a0fihjj21rj42m870a7sx-pre-commit-3.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
justfile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
default:
|
||||||
|
@just --choose
|
||||||
|
|
||||||
|
# Build Universal Inbox extension for Raycast
|
||||||
|
build:
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Lint extension code
|
||||||
|
lint:
|
||||||
|
npm run lint
|
||||||
|
|
||||||
|
# Lint and fix extension code
|
||||||
|
fix:
|
||||||
|
npm run fix-lint
|
||||||
|
|
||||||
|
# Run extension in development mode
|
||||||
|
run:
|
||||||
|
npm run dev
|
||||||
2208
package-lock.json
generated
Normal file
32
package.json
@@ -3,7 +3,7 @@
|
|||||||
"name": "universal-inbox",
|
"name": "universal-inbox",
|
||||||
"title": "Universal Inbox",
|
"title": "Universal Inbox",
|
||||||
"description": "Manage Universal Inbox notifications from Raycast",
|
"description": "Manage Universal Inbox notifications from Raycast",
|
||||||
"icon": "command-icon.png",
|
"icon": "ui-logo-transparent.png",
|
||||||
"author": "david_rousselie",
|
"author": "david_rousselie",
|
||||||
"owner": "universal-inbox",
|
"owner": "universal-inbox",
|
||||||
"categories": [
|
"categories": [
|
||||||
@@ -11,24 +11,44 @@
|
|||||||
"Productivity"
|
"Productivity"
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"preferences": [
|
||||||
|
{
|
||||||
|
"name": "apiKey",
|
||||||
|
"description": "Your Universal Inbox key",
|
||||||
|
"type": "password",
|
||||||
|
"required": true,
|
||||||
|
"title": "Universal Inbox API Key",
|
||||||
|
"placeholder": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "universalInboxBaseUrl",
|
||||||
|
"description": "The Universal Inbox instance URL to use",
|
||||||
|
"type": "textfield",
|
||||||
|
"required": true,
|
||||||
|
"title": "Universal Inbox instance URL",
|
||||||
|
"placeholder": "https://app.universal-inbox.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
"commands": [
|
"commands": [
|
||||||
{
|
{
|
||||||
"name": "index",
|
"name": "index",
|
||||||
"title": "List notifications",
|
"title": "List Notifications",
|
||||||
"description": "List Universal Inbox notifications",
|
"description": "List Universal Inbox notifications",
|
||||||
"mode": "view"
|
"mode": "view"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@raycast/api": "^1.65.1"
|
"@raycast/api": "^1.65.1",
|
||||||
|
"@raycast/utils": "^1.10.1",
|
||||||
|
"node-fetch": "^3.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@raycast/eslint-config": "^1.0.6",
|
"@raycast/eslint-config": "^1.0.6",
|
||||||
"@types/node": "20.8.10",
|
"@types/node": "20.8.10",
|
||||||
"@types/react": "18.2.27",
|
"@types/react": "18.2.27",
|
||||||
"eslint": "^8.51.0",
|
"eslint": "^8.56.0",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.0.3",
|
||||||
"typescript": "^5.2.2"
|
"typescript": "^5.3.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "ray build -e dist",
|
"build": "ray build -e dist",
|
||||||
@@ -37,4 +57,4 @@
|
|||||||
"lint": "ray lint",
|
"lint": "ray lint",
|
||||||
"publish": "npx @raycast/api@latest publish"
|
"publish": "npx @raycast/api@latest publish"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
73
src/NotificationActions.tsx
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { Action, ActionPanel, Icon } from "@raycast/api";
|
||||||
|
import { useMemo, ReactElement } from "react";
|
||||||
|
import { getNotificationHtmlUrl } from "./notification";
|
||||||
|
import { Notification } from "./types";
|
||||||
|
|
||||||
|
function deleteNotification(notification: Notification) {
|
||||||
|
console.log(`Deleting notification ${notification.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unsubscribeFromNotification(notification: Notification) {
|
||||||
|
console.log(`Unsubcribing from notification ${notification.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function snoozeNotification(notification: Notification) {
|
||||||
|
console.log(`Snoozing notification ${notification.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTaskFromNotification(notification: Notification) {
|
||||||
|
console.log(`Creating task notification ${notification.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function linkNotificationToTask(notification: Notification) {
|
||||||
|
console.log(`Linking notification ${notification.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NotificationActions({
|
||||||
|
notification,
|
||||||
|
detailsTarget,
|
||||||
|
}: {
|
||||||
|
notification: Notification;
|
||||||
|
detailsTarget: ReactElement;
|
||||||
|
}) {
|
||||||
|
const notification_html_url = useMemo(() => {
|
||||||
|
return getNotificationHtmlUrl(notification);
|
||||||
|
}, [notification]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ActionPanel>
|
||||||
|
<Action.Push title="Show Details" target={detailsTarget} />
|
||||||
|
<Action.OpenInBrowser url={notification_html_url} />
|
||||||
|
<Action
|
||||||
|
title="Delete Notification"
|
||||||
|
icon={Icon.Trash}
|
||||||
|
shortcut={{ modifiers: ["ctrl"], key: "d" }}
|
||||||
|
onAction={() => deleteNotification(notification)}
|
||||||
|
/>
|
||||||
|
<Action
|
||||||
|
title="Unsubscribe From Notification"
|
||||||
|
icon={Icon.BellDisabled}
|
||||||
|
shortcut={{ modifiers: ["ctrl"], key: "u" }}
|
||||||
|
onAction={() => unsubscribeFromNotification(notification)}
|
||||||
|
/>
|
||||||
|
<Action
|
||||||
|
title="Snooze"
|
||||||
|
icon={Icon.Clock}
|
||||||
|
shortcut={{ modifiers: ["ctrl"], key: "s" }}
|
||||||
|
onAction={() => snoozeNotification(notification)}
|
||||||
|
/>
|
||||||
|
<Action
|
||||||
|
title="Create Task"
|
||||||
|
icon={Icon.Calendar}
|
||||||
|
shortcut={{ modifiers: ["ctrl"], key: "d" }}
|
||||||
|
onAction={() => createTaskFromNotification(notification)}
|
||||||
|
/>
|
||||||
|
<Action
|
||||||
|
title="Link to Task"
|
||||||
|
icon={Icon.Link}
|
||||||
|
shortcut={{ modifiers: ["ctrl"], key: "d" }}
|
||||||
|
onAction={() => linkNotificationToTask(notification)}
|
||||||
|
/>
|
||||||
|
</ActionPanel>
|
||||||
|
);
|
||||||
|
}
|
||||||
63
src/NotificationTaskActions.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { Action, ActionPanel, Icon } from "@raycast/api";
|
||||||
|
import { useMemo, ReactElement } from "react";
|
||||||
|
import { getNotificationHtmlUrl } from "./notification";
|
||||||
|
import { Notification } from "./types";
|
||||||
|
|
||||||
|
function deleteNotification(notification: Notification) {
|
||||||
|
console.log(`Deleting notification ${notification.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unsubscribeFromNotification(notification: Notification) {
|
||||||
|
console.log(`Unsubcribing from notification ${notification.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function snoozeNotification(notification: Notification) {
|
||||||
|
console.log(`Snoozing notification ${notification.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeTask(notification: Notification) {
|
||||||
|
console.log(`Completing task ${notification.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NotificationTaskActions({
|
||||||
|
notification,
|
||||||
|
detailsTarget,
|
||||||
|
}: {
|
||||||
|
notification: Notification;
|
||||||
|
detailsTarget: ReactElement;
|
||||||
|
}) {
|
||||||
|
const notification_html_url = useMemo(() => {
|
||||||
|
return getNotificationHtmlUrl(notification);
|
||||||
|
}, [notification]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ActionPanel>
|
||||||
|
<Action.Push title="Show Details" target={detailsTarget} />
|
||||||
|
<Action.OpenInBrowser url={notification_html_url} />
|
||||||
|
<Action
|
||||||
|
title="Delete Notification"
|
||||||
|
icon={Icon.Trash}
|
||||||
|
shortcut={{ modifiers: ["ctrl"], key: "d" }}
|
||||||
|
onAction={() => deleteNotification(notification)}
|
||||||
|
/>
|
||||||
|
<Action
|
||||||
|
title="Unsubscribe From Notification"
|
||||||
|
icon={Icon.BellDisabled}
|
||||||
|
shortcut={{ modifiers: ["ctrl"], key: "u" }}
|
||||||
|
onAction={() => unsubscribeFromNotification(notification)}
|
||||||
|
/>
|
||||||
|
<Action
|
||||||
|
title="Snooze"
|
||||||
|
icon={Icon.Clock}
|
||||||
|
shortcut={{ modifiers: ["ctrl"], key: "s" }}
|
||||||
|
onAction={() => snoozeNotification(notification)}
|
||||||
|
/>
|
||||||
|
<Action
|
||||||
|
title="Complete Task"
|
||||||
|
icon={Icon.Calendar}
|
||||||
|
shortcut={{ modifiers: ["ctrl"], key: "c" }}
|
||||||
|
onAction={() => completeTask(notification)}
|
||||||
|
/>
|
||||||
|
</ActionPanel>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,17 +1,75 @@
|
|||||||
import { ActionPanel, Detail, List, Action } from "@raycast/api";
|
import { Action, ActionPanel, Detail, List, getPreferenceValues, openExtensionPreferences } from "@raycast/api";
|
||||||
|
import { useFetch } from "@raycast/utils";
|
||||||
|
import { NotificationActions } from "./NotificationActions";
|
||||||
|
import { GithubNotificationListItem } from "./integrations/github/GithubNotificationListItem";
|
||||||
|
import { GoogleMailNotificationListItem } from "./integrations/google-mail/GoogleMailNotificationListItem";
|
||||||
|
import { LinearNotificationListItem } from "./integrations/linear/LinearNotificationListItem";
|
||||||
|
import { TodoistNotificationListItem } from "./integrations/todoist/TodoistNotificationListItem";
|
||||||
|
import { Notification, NotificationListItemProps, Page, UniversalInboxPreferences } from "./types";
|
||||||
|
|
||||||
export default function Command() {
|
export default function Command() {
|
||||||
return (
|
const preferences = getPreferenceValues<UniversalInboxPreferences>();
|
||||||
<List>
|
|
||||||
<List.Item
|
if (
|
||||||
icon="list-icon.png"
|
preferences.apiKey === undefined ||
|
||||||
title="Greeting"
|
preferences.apiKey === ""
|
||||||
|
/* preferences.universalInboxBaseUrl === undefined ||
|
||||||
|
* preferences.universalInboxBaseUrl === "" */
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<Detail
|
||||||
|
markdown={"API key incorrect. Please update it in extension preferences and try again."}
|
||||||
actions={
|
actions={
|
||||||
<ActionPanel>
|
<ActionPanel>
|
||||||
<Action.Push title="Show Details" target={<Detail markdown="# Hey! 👋" />} />
|
<Action title="Open Extension Preferences" onAction={openExtensionPreferences} />
|
||||||
</ActionPanel>
|
</ActionPanel>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { isLoading, data } = useFetch<Page<Notification>>(
|
||||||
|
`${preferences.universalInboxBaseUrl}/api/notifications?status=Unread,Read&with_tasks=true`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${preferences.apiKey}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List isLoading={isLoading}>
|
||||||
|
{data?.content.map((notification: Notification) => {
|
||||||
|
return <NotificationListItem key={notification.id} notification={notification} />;
|
||||||
|
})}
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function NotificationListItem({ notification }: NotificationListItemProps) {
|
||||||
|
switch (notification.metadata.type) {
|
||||||
|
case "Github":
|
||||||
|
return <GithubNotificationListItem notification={notification} />;
|
||||||
|
case "Linear":
|
||||||
|
return <LinearNotificationListItem notification={notification} />;
|
||||||
|
case "GoogleMail":
|
||||||
|
return <GoogleMailNotificationListItem notification={notification} />;
|
||||||
|
case "Todoist":
|
||||||
|
return <TodoistNotificationListItem notification={notification} />;
|
||||||
|
default:
|
||||||
|
return <DefaultNotificationListItem notification={notification} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function DefaultNotificationListItem({ notification }: NotificationListItemProps) {
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
key={notification.id}
|
||||||
|
title={notification.title}
|
||||||
|
subtitle={`#${notification.source_id}`}
|
||||||
|
actions={
|
||||||
|
<NotificationActions notification={notification} detailsTarget={<Detail markdown="# To be implemented 👋" />} />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
35
src/integrations/github/GithubNotificationListItem.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { environment } from "@raycast/api";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { NotificationListItemProps } from "../../types";
|
||||||
|
import { GithubDiscussionNotificationListItem } from "./listitem/GithubDiscussionNotificationListItem";
|
||||||
|
import { GithubPullRequestNotificationListItem } from "./listitem/GithubPullRequestNotificationListItem";
|
||||||
|
|
||||||
|
export function GithubNotificationListItem({ notification }: NotificationListItemProps) {
|
||||||
|
const icon = useMemo(() => {
|
||||||
|
if (environment.appearance === "dark") {
|
||||||
|
return "github-logo-light.svg";
|
||||||
|
}
|
||||||
|
return "github-logo-dark.svg";
|
||||||
|
}, [environment]);
|
||||||
|
|
||||||
|
switch (notification.details?.type) {
|
||||||
|
case "GithubPullRequest":
|
||||||
|
return (
|
||||||
|
<GithubPullRequestNotificationListItem
|
||||||
|
icon={icon}
|
||||||
|
notification={notification}
|
||||||
|
githubPullRequest={notification.details.content}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case "GithubDiscussion":
|
||||||
|
return (
|
||||||
|
<GithubDiscussionNotificationListItem
|
||||||
|
icon={icon}
|
||||||
|
notification={notification}
|
||||||
|
githubDiscussion={notification.details.content}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { Color, Icon, List } from "@raycast/api";
|
||||||
|
import { NotificationActions } from "../../../NotificationActions";
|
||||||
|
import { Notification } from "../../../types";
|
||||||
|
import { getGithubActorAccessory } from "../misc";
|
||||||
|
import { GithubDiscussion, GithubDiscussionStateReason } from "../types";
|
||||||
|
import { GithubDiscussionPreview } from "../preview/GithubDiscussionPreview";
|
||||||
|
|
||||||
|
interface GithubDiscussionNotificationListItemProps {
|
||||||
|
icon: string;
|
||||||
|
notification: Notification;
|
||||||
|
githubDiscussion: GithubDiscussion;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GithubDiscussionNotificationListItem({
|
||||||
|
icon,
|
||||||
|
notification,
|
||||||
|
githubDiscussion,
|
||||||
|
}: GithubDiscussionNotificationListItemProps) {
|
||||||
|
const subtitle = `${githubDiscussion.repository.name_with_owner}`;
|
||||||
|
|
||||||
|
const author = getGithubActorAccessory(githubDiscussion.author);
|
||||||
|
const discussionStatus = getGithubDiscussionStatusAccessory(githubDiscussion.state_reason);
|
||||||
|
|
||||||
|
const accessories: List.Item.Accessory[] = [
|
||||||
|
author,
|
||||||
|
{ date: new Date(githubDiscussion.updated_at), tooltip: `Updated at ${githubDiscussion.updated_at}` },
|
||||||
|
];
|
||||||
|
|
||||||
|
if (discussionStatus) {
|
||||||
|
accessories.unshift(discussionStatus);
|
||||||
|
}
|
||||||
|
if (githubDiscussion.comments_count > 0) {
|
||||||
|
accessories.unshift({
|
||||||
|
text: githubDiscussion.comments_count.toString(),
|
||||||
|
icon: Icon.Bubble,
|
||||||
|
tooltip: `${githubDiscussion.comments_count} comments`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
key={notification.id}
|
||||||
|
title={notification.title}
|
||||||
|
icon={icon}
|
||||||
|
subtitle={subtitle}
|
||||||
|
accessories={accessories}
|
||||||
|
actions={
|
||||||
|
<NotificationActions
|
||||||
|
notification={notification}
|
||||||
|
detailsTarget={<GithubDiscussionPreview notification={notification} githubDiscussion={githubDiscussion} />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGithubDiscussionStatusAccessory(stateReason?: GithubDiscussionStateReason): List.Item.Accessory {
|
||||||
|
switch (stateReason) {
|
||||||
|
case GithubDiscussionStateReason.Duplicate:
|
||||||
|
return {
|
||||||
|
icon: { source: "github-discussion-duplicate.svg", tintColor: Color.SecondaryText },
|
||||||
|
tooltip: "Answered",
|
||||||
|
};
|
||||||
|
case GithubDiscussionStateReason.Outdated:
|
||||||
|
return {
|
||||||
|
icon: { source: "github-discussion-outdated.svg", tintColor: Color.SecondaryText },
|
||||||
|
tooltip: "Answered",
|
||||||
|
};
|
||||||
|
case GithubDiscussionStateReason.Reopened:
|
||||||
|
return { icon: { source: "github-discussion-opened.svg", tintColor: Color.Green }, tooltip: "Answered" };
|
||||||
|
case GithubDiscussionStateReason.Resolved:
|
||||||
|
return { icon: { source: "github-discussion-closed.svg", tintColor: Color.Magenta }, tooltip: "Answered" };
|
||||||
|
default:
|
||||||
|
return { icon: { source: "github-discussion-opened.svg", tintColor: Color.Green }, tooltip: "Answered" };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
import { Color, Icon, List } from "@raycast/api";
|
||||||
|
import { NotificationActions } from "../../../NotificationActions";
|
||||||
|
import { Notification } from "../../../types";
|
||||||
|
import { GithubPullRequestPreview } from "../preview/GithubPullRequestPreview";
|
||||||
|
import {
|
||||||
|
GithubPullRequestState,
|
||||||
|
GithubPullRequestReviewDecision,
|
||||||
|
GithubCheckConclusionState,
|
||||||
|
GithubPullRequest,
|
||||||
|
GithubCommitChecks,
|
||||||
|
GithubCheckStatusState,
|
||||||
|
GithubCheckSuite,
|
||||||
|
} from "../types";
|
||||||
|
import { getGithubActorAccessory } from "../misc";
|
||||||
|
|
||||||
|
interface GithubPullRequestNotificationListItemProps {
|
||||||
|
icon: string;
|
||||||
|
notification: Notification;
|
||||||
|
githubPullRequest: GithubPullRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GithubPullRequestNotificationListItem({
|
||||||
|
icon,
|
||||||
|
notification,
|
||||||
|
githubPullRequest,
|
||||||
|
}: GithubPullRequestNotificationListItemProps) {
|
||||||
|
const subtitle = `${githubPullRequest.head_repository?.name_with_owner} #${githubPullRequest.number}`;
|
||||||
|
|
||||||
|
const author = getGithubActorAccessory(githubPullRequest.author);
|
||||||
|
const prChecks = getGithubPullRequestChecksAccessory(githubPullRequest.latest_commit);
|
||||||
|
const review = getGithubPullRequestReviewAccessory(githubPullRequest.review_decision);
|
||||||
|
const prStatus = getGithubPullRequestStateAccessory(githubPullRequest);
|
||||||
|
|
||||||
|
const accessories: List.Item.Accessory[] = [
|
||||||
|
author,
|
||||||
|
{
|
||||||
|
date: new Date(githubPullRequest.updated_at),
|
||||||
|
tooltip: `Updated at ${githubPullRequest.updated_at}`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (prStatus) {
|
||||||
|
accessories.unshift(prStatus);
|
||||||
|
}
|
||||||
|
if (githubPullRequest.comments_count > 0) {
|
||||||
|
accessories.unshift({
|
||||||
|
text: githubPullRequest.comments_count.toString(),
|
||||||
|
icon: Icon.Bubble,
|
||||||
|
tooltip: `${githubPullRequest.comments_count} comments`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (review) {
|
||||||
|
accessories.unshift(review);
|
||||||
|
}
|
||||||
|
if (prChecks) {
|
||||||
|
accessories.unshift(prChecks);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
key={notification.id}
|
||||||
|
title={notification.title}
|
||||||
|
icon={icon}
|
||||||
|
accessories={accessories}
|
||||||
|
subtitle={subtitle}
|
||||||
|
actions={
|
||||||
|
<NotificationActions
|
||||||
|
notification={notification}
|
||||||
|
detailsTarget={<GithubPullRequestPreview notification={notification} githubPullRequest={githubPullRequest} />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGithubPullRequestChecksAccessory(latestCommit: GithubCommitChecks): List.Item.Accessory | null {
|
||||||
|
const progress = computePullRequestChecksProgress(latestCommit.check_suites);
|
||||||
|
if (!progress) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch (progress.status()) {
|
||||||
|
case GithubCheckStatusState.Pending:
|
||||||
|
return { icon: Icon.Pause, tooltip: "Pending" };
|
||||||
|
case GithubCheckStatusState.InProgress:
|
||||||
|
return { icon: Icon.Pause, tooltip: "In progress" }; // TODO Spinner
|
||||||
|
case GithubCheckStatusState.Completed:
|
||||||
|
switch (progress.conclusion()) {
|
||||||
|
case GithubCheckConclusionState.Success:
|
||||||
|
return { icon: Icon.CheckCircle, tooltip: "Success" };
|
||||||
|
case GithubCheckConclusionState.Failure:
|
||||||
|
return { icon: Icon.XMarkCircle, tooltip: "Failure" };
|
||||||
|
default:
|
||||||
|
return { icon: Icon.QuestionMarkCircle, tooltip: "Neutral" };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return { icon: Icon.QuestionMarkCircle, tooltip: "Neutral" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GithubChecksProgress {
|
||||||
|
checksCount = 0;
|
||||||
|
completedChecksCount = 0;
|
||||||
|
failedChecksCount = 0;
|
||||||
|
|
||||||
|
status(): GithubCheckStatusState {
|
||||||
|
if (this.completedChecksCount === 0) {
|
||||||
|
return GithubCheckStatusState.Pending;
|
||||||
|
}
|
||||||
|
if (this.completedChecksCount === this.checksCount) {
|
||||||
|
return GithubCheckStatusState.Completed;
|
||||||
|
}
|
||||||
|
return GithubCheckStatusState.InProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
conclusion(): GithubCheckConclusionState {
|
||||||
|
if (this.status() === GithubCheckStatusState.InProgress) {
|
||||||
|
return GithubCheckConclusionState.Neutral;
|
||||||
|
}
|
||||||
|
if (this.failedChecksCount > 0) {
|
||||||
|
return GithubCheckConclusionState.Failure;
|
||||||
|
}
|
||||||
|
return GithubCheckConclusionState.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function computePullRequestChecksProgress(checkSuites?: Array<GithubCheckSuite>): GithubChecksProgress | null {
|
||||||
|
if (checkSuites) {
|
||||||
|
const progress = new GithubChecksProgress();
|
||||||
|
|
||||||
|
for (const checkSuite of checkSuites) {
|
||||||
|
if (checkSuite.status !== GithubCheckStatusState.Queued) {
|
||||||
|
for (const checkRun of checkSuite.check_runs) {
|
||||||
|
progress.checksCount += 1;
|
||||||
|
if (checkRun.status === GithubCheckStatusState.Completed) {
|
||||||
|
progress.completedChecksCount += 1;
|
||||||
|
if (checkRun.conclusion && checkRun.conclusion !== GithubCheckConclusionState.Success) {
|
||||||
|
progress.failedChecksCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress.checksCount === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGithubPullRequestReviewAccessory(
|
||||||
|
reviewDecision?: GithubPullRequestReviewDecision,
|
||||||
|
): List.Item.Accessory | null {
|
||||||
|
switch (reviewDecision) {
|
||||||
|
case GithubPullRequestReviewDecision.Approved:
|
||||||
|
return { tag: { value: "Approved", color: Color.Green } };
|
||||||
|
case GithubPullRequestReviewDecision.ChangesRequested:
|
||||||
|
return { tag: { value: "Changes requested", color: Color.Red } };
|
||||||
|
case GithubPullRequestReviewDecision.ReviewRequired:
|
||||||
|
return { tag: { value: "Review required", color: Color.Orange } };
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGithubPullRequestStateAccessory(githubPullRequest: GithubPullRequest): List.Item.Accessory | null {
|
||||||
|
switch (githubPullRequest.state) {
|
||||||
|
case GithubPullRequestState.Open:
|
||||||
|
if (githubPullRequest.is_draft) {
|
||||||
|
return {
|
||||||
|
icon: {
|
||||||
|
source: "github-pullrequest-draft.svg",
|
||||||
|
tintColor: Color.SecondaryText,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
icon: { source: "github-pullrequest.svg", tintColor: Color.Green },
|
||||||
|
};
|
||||||
|
case GithubPullRequestState.Closed:
|
||||||
|
return {
|
||||||
|
icon: {
|
||||||
|
source: "github-pullrequest-closed.svg",
|
||||||
|
tintColor: Color.SecondaryText,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case GithubPullRequestState.Merged:
|
||||||
|
return {
|
||||||
|
icon: { source: "github-pullrequest.svg", tintColor: Color.Magenta },
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/integrations/github/misc.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Icon, Image, List } from "@raycast/api";
|
||||||
|
import { GithubActor, getGithubActorName } from "./types";
|
||||||
|
|
||||||
|
export function getGithubActorAccessory(actor?: GithubActor): List.Item.Accessory {
|
||||||
|
if (actor) {
|
||||||
|
return {
|
||||||
|
icon: actor.content.avatar_url ? { source: actor.content.avatar_url, mask: Image.Mask.Circle } : Icon.Person,
|
||||||
|
tooltip: getGithubActorName(actor),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { icon: Icon.Person, tooltip: "Unknown" };
|
||||||
|
}
|
||||||
27
src/integrations/github/preview/GithubDiscussionPreview.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { Detail, ActionPanel, Action } from "@raycast/api";
|
||||||
|
import { Notification } from "../../../types";
|
||||||
|
import { GithubDiscussion } from "../types";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { getNotificationHtmlUrl } from "../../../notification";
|
||||||
|
|
||||||
|
interface GithubDiscussionPreviewProps {
|
||||||
|
notification: Notification;
|
||||||
|
githubDiscussion: GithubDiscussion;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GithubDiscussionPreview({ notification, githubDiscussion }: GithubDiscussionPreviewProps) {
|
||||||
|
const notification_html_url = useMemo(() => {
|
||||||
|
return getNotificationHtmlUrl(notification);
|
||||||
|
}, [notification]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Detail
|
||||||
|
markdown={`# ${githubDiscussion.title}`}
|
||||||
|
actions={
|
||||||
|
<ActionPanel>
|
||||||
|
<Action.OpenInBrowser url={notification_html_url} />
|
||||||
|
</ActionPanel>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
27
src/integrations/github/preview/GithubPullRequestPreview.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { Detail, ActionPanel, Action } from "@raycast/api";
|
||||||
|
import { Notification } from "../../../types";
|
||||||
|
import { GithubPullRequest } from "../types";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { getNotificationHtmlUrl } from "../../../notification";
|
||||||
|
|
||||||
|
interface GithubPullRequestPreviewProps {
|
||||||
|
notification: Notification;
|
||||||
|
githubPullRequest: GithubPullRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GithubPullRequestPreview({ notification, githubPullRequest }: GithubPullRequestPreviewProps) {
|
||||||
|
const notification_html_url = useMemo(() => {
|
||||||
|
return getNotificationHtmlUrl(notification);
|
||||||
|
}, [notification]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Detail
|
||||||
|
markdown={`# ${githubPullRequest.title}`}
|
||||||
|
actions={
|
||||||
|
<ActionPanel>
|
||||||
|
<Action.OpenInBrowser url={notification_html_url} />
|
||||||
|
</ActionPanel>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
221
src/integrations/github/types.ts
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
export interface GithubPullRequest {
|
||||||
|
id: string;
|
||||||
|
number: number;
|
||||||
|
url: string;
|
||||||
|
title: string;
|
||||||
|
body: string;
|
||||||
|
state: GithubPullRequestState;
|
||||||
|
is_draft: boolean;
|
||||||
|
closed_at?: Date;
|
||||||
|
created_at: Date;
|
||||||
|
updated_at: Date;
|
||||||
|
merged_at?: Date;
|
||||||
|
mergeable_state: GithubMergeableState;
|
||||||
|
merge_state_status: GithubMergeStateStatus;
|
||||||
|
merged_by?: GithubActor;
|
||||||
|
deletions: number;
|
||||||
|
additions: number;
|
||||||
|
changed_files: number;
|
||||||
|
labels: Array<GithubLabel>;
|
||||||
|
comments_count: number;
|
||||||
|
comments: Array<GithubIssueComment>;
|
||||||
|
latest_commit: GithubCommitChecks;
|
||||||
|
base_ref_name: string;
|
||||||
|
base_repository?: GithubRepositorySummary;
|
||||||
|
head_ref_name: string;
|
||||||
|
head_repository?: GithubRepositorySummary;
|
||||||
|
author?: GithubActor;
|
||||||
|
assignees: Array<GithubActor>;
|
||||||
|
review_decision?: GithubPullRequestReviewDecision;
|
||||||
|
reviews: Array<GithubPullRequestReview>;
|
||||||
|
review_requests: Array<GithubReviewer>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GithubPullRequestState {
|
||||||
|
Open = "Open",
|
||||||
|
Closed = "Closed",
|
||||||
|
Merged = "Merged",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GithubMergeableState {
|
||||||
|
Unknown = "Unknown",
|
||||||
|
Mergeable = "Mergeable",
|
||||||
|
Conflicting = "Conflicting",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GithubMergeStateStatus {
|
||||||
|
Behind = "Behind",
|
||||||
|
Blocked = "Blocked",
|
||||||
|
Clean = "Clean",
|
||||||
|
Dirty = "Dirty",
|
||||||
|
Draft = "Draft",
|
||||||
|
HasHooks = "HasHooks",
|
||||||
|
Unknown = "Unknown",
|
||||||
|
Unstable = "Unstable",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GithubActor =
|
||||||
|
| { type: "GithubUserSummary"; content: GithubUserSummary }
|
||||||
|
| { type: "GithubBotSummary"; content: GithubBotSummary };
|
||||||
|
|
||||||
|
export function getGithubActorName(actor?: GithubActor): string {
|
||||||
|
if (!actor) {
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
switch (actor.type) {
|
||||||
|
case "GithubUserSummary":
|
||||||
|
return actor.content.name ? actor.content.name : actor.content.login;
|
||||||
|
case "GithubBotSummary":
|
||||||
|
return actor.content.login;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubUserSummary {
|
||||||
|
name?: string;
|
||||||
|
login: string;
|
||||||
|
avatar_url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubBotSummary {
|
||||||
|
login: string;
|
||||||
|
avatar_url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubTeamSummary {
|
||||||
|
avatar_url?: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubMannequinSummary {
|
||||||
|
avatar_url: string;
|
||||||
|
login: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubLabel {
|
||||||
|
name: string;
|
||||||
|
color: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubIssueComment {
|
||||||
|
url: string;
|
||||||
|
body: string;
|
||||||
|
created_at: Date;
|
||||||
|
author?: GithubActor;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubCommitChecks {
|
||||||
|
git_commit_id: string;
|
||||||
|
check_suites?: Array<GithubCheckSuite>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubCheckSuite {
|
||||||
|
check_runs: Array<GithubCheckRun>;
|
||||||
|
conclusion?: GithubCheckConclusionState;
|
||||||
|
status: GithubCheckStatusState;
|
||||||
|
workflow?: GithubWorkflow;
|
||||||
|
app: GithubCheckSuiteApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubCheckRun {
|
||||||
|
name: string;
|
||||||
|
conclusion?: GithubCheckConclusionState;
|
||||||
|
status: GithubCheckStatusState;
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GithubCheckConclusionState {
|
||||||
|
ActionRequired = "ActionRequired",
|
||||||
|
Cancelled = "Cancelled",
|
||||||
|
Failure = "Failure",
|
||||||
|
Neutral = "Neutral",
|
||||||
|
Skipped = "Skipped",
|
||||||
|
Stale = "Stale",
|
||||||
|
StartupFailure = "StartupFailure",
|
||||||
|
Success = "Success",
|
||||||
|
TimedOut = "TimedOut",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GithubCheckStatusState {
|
||||||
|
Completed = "Completed",
|
||||||
|
InProgress = "InProgress",
|
||||||
|
Pending = "Pending",
|
||||||
|
Queued = "Queued",
|
||||||
|
Requested = "Requested",
|
||||||
|
Waiting = "Waiting",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubWorkflow {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubCheckSuiteApp {
|
||||||
|
name: string;
|
||||||
|
logo_url?: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubRepositorySummary {
|
||||||
|
name_with_owner: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GithubPullRequestReviewDecision {
|
||||||
|
Approved = "Approved",
|
||||||
|
ChangesRequested = "ChangesRequested",
|
||||||
|
ReviewRequired = "ReviewRequired",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubPullRequestReview {
|
||||||
|
author?: GithubActor;
|
||||||
|
body: string;
|
||||||
|
state: GithubPullRequestReviewState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GithubPullRequestReviewState {
|
||||||
|
Approved = "Approved",
|
||||||
|
ChangesRequested = "ChangesRequested",
|
||||||
|
Commented = "Commented",
|
||||||
|
Dismissed = "Dismissed",
|
||||||
|
Pending = "Pending",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GithubReviewer =
|
||||||
|
| { type: "GithubUserSummary"; content: GithubUserSummary }
|
||||||
|
| { type: "GithubTeamSummary"; content: GithubTeamSummary }
|
||||||
|
| { type: "GithubBotSummary"; content: GithubBotSummary }
|
||||||
|
| { type: "GithubMannequinSummary"; content: GithubMannequinSummary };
|
||||||
|
|
||||||
|
export interface GithubDiscussion {
|
||||||
|
id: string;
|
||||||
|
number: number;
|
||||||
|
url: string;
|
||||||
|
title: string;
|
||||||
|
body: string;
|
||||||
|
repository: GithubRepositorySummary;
|
||||||
|
state_reason?: GithubDiscussionStateReason;
|
||||||
|
closed_at?: Date;
|
||||||
|
created_at: Date;
|
||||||
|
updated_at: Date;
|
||||||
|
labels: Array<GithubLabel>;
|
||||||
|
comments_count: number;
|
||||||
|
author?: GithubActor;
|
||||||
|
answer_chosen_at?: Date;
|
||||||
|
answer_chosen_by?: GithubActor;
|
||||||
|
answer?: GithubDiscussionComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GithubDiscussionStateReason {
|
||||||
|
Duplicate = "Duplicate",
|
||||||
|
Outdated = "Outdated",
|
||||||
|
Reopened = "Reopened",
|
||||||
|
Resolved = "Resolved",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubDiscussionComment {
|
||||||
|
url: string;
|
||||||
|
body: string;
|
||||||
|
created_at: Date;
|
||||||
|
author?: GithubActor;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { Detail, List, environment } from "@raycast/api";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { NotificationActions } from "../../NotificationActions";
|
||||||
|
import { NotificationListItemProps } from "../../types";
|
||||||
|
|
||||||
|
export function GoogleMailNotificationListItem({ notification }: NotificationListItemProps) {
|
||||||
|
const icon = useMemo(() => {
|
||||||
|
if (environment.appearance === "dark") {
|
||||||
|
return "google-mail-logo-light.svg";
|
||||||
|
}
|
||||||
|
return "google-mail-logo-dark.svg";
|
||||||
|
}, [environment]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
key={notification.id}
|
||||||
|
title={notification.title}
|
||||||
|
icon={icon}
|
||||||
|
subtitle={`#${notification.source_id}`}
|
||||||
|
actions={
|
||||||
|
<NotificationActions notification={notification} detailsTarget={<Detail markdown="# To be implemented 👋" />} />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
25
src/integrations/linear/LinearNotificationListItem.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { Detail, List, environment } from "@raycast/api";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { NotificationActions } from "../../NotificationActions";
|
||||||
|
import { NotificationListItemProps } from "../../types";
|
||||||
|
|
||||||
|
export function LinearNotificationListItem({ notification }: NotificationListItemProps) {
|
||||||
|
const icon = useMemo(() => {
|
||||||
|
if (environment.appearance === "dark") {
|
||||||
|
return "linear-logo-light.svg";
|
||||||
|
}
|
||||||
|
return "linear-logo-dark.svg";
|
||||||
|
}, [environment]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
key={notification.id}
|
||||||
|
title={notification.title}
|
||||||
|
icon={icon}
|
||||||
|
subtitle={`#${notification.source_id}`}
|
||||||
|
actions={
|
||||||
|
<NotificationActions notification={notification} detailsTarget={<Detail markdown="# To be implemented 👋" />} />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
28
src/integrations/todoist/TodoistNotificationListItem.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Detail, List, environment } from "@raycast/api";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { NotificationTaskActions } from "../../NotificationTaskActions";
|
||||||
|
import { NotificationListItemProps } from "../../types";
|
||||||
|
|
||||||
|
export function TodoistNotificationListItem({ notification }: NotificationListItemProps) {
|
||||||
|
const icon = useMemo(() => {
|
||||||
|
if (environment.appearance === "dark") {
|
||||||
|
return "todoist-icon-light.svg";
|
||||||
|
}
|
||||||
|
return "todoist-icon-dark.svg";
|
||||||
|
}, [environment]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
key={notification.id}
|
||||||
|
title={notification.title}
|
||||||
|
icon={icon}
|
||||||
|
subtitle={`#${notification.source_id}`}
|
||||||
|
actions={
|
||||||
|
<NotificationTaskActions
|
||||||
|
notification={notification}
|
||||||
|
detailsTarget={<Detail markdown="# To be implemented 👋" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
14
src/notification.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Notification } from "./types";
|
||||||
|
|
||||||
|
export function getNotificationHtmlUrl(notification: Notification) {
|
||||||
|
switch (notification.details?.type) {
|
||||||
|
case "GithubPullRequest":
|
||||||
|
return notification.details.content.url;
|
||||||
|
case "GithubDiscussion":
|
||||||
|
return notification.details.content.url;
|
||||||
|
default: {
|
||||||
|
// TODO
|
||||||
|
return "https://github.com";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/types.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { GithubDiscussion, GithubPullRequest } from "./integrations/github/types";
|
||||||
|
|
||||||
|
export interface UniversalInboxPreferences {
|
||||||
|
apiKey: string;
|
||||||
|
universalInboxBaseUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Page<T> {
|
||||||
|
page: number;
|
||||||
|
per_page: number;
|
||||||
|
total: number;
|
||||||
|
content: Array<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Notification {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
source_id: string;
|
||||||
|
status: NotificationStatus;
|
||||||
|
metadata: NotificationMetadata;
|
||||||
|
updated_at: Date;
|
||||||
|
last_read_at?: Date;
|
||||||
|
snoozed_until?: Date;
|
||||||
|
user_id: string;
|
||||||
|
task_id?: string;
|
||||||
|
details?: NotificationDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NotificationMetadata = {
|
||||||
|
type: "Github" | "Linear" | "GoogleMail" | "Todoist";
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
content: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type NotificationDetails =
|
||||||
|
| { type: "GithubPullRequest"; content: GithubPullRequest }
|
||||||
|
| { type: "GithubDiscussion"; content: GithubDiscussion };
|
||||||
|
|
||||||
|
export enum NotificationStatus {
|
||||||
|
Unread = "Unread",
|
||||||
|
Read = "Read",
|
||||||
|
Deleted = "Deleted",
|
||||||
|
Unsubscribed = "Unsubscribed",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NotificationListItemProps = {
|
||||||
|
notification: Notification;
|
||||||
|
};
|
||||||