mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2025-08-25 17:39:23 +00:00
Compare commits
316 Commits
v2024.05.1
...
features/p
Author | SHA1 | Date | |
---|---|---|---|
582d14708f | |||
ea79165362 | |||
460d71075b | |||
3d7d1142d3 | |||
6aa84daf5e | |||
b37edc3712 | |||
9ff772f098 | |||
![]() |
6974c6beba | ||
ff196693df | |||
00e5766f90 | |||
3f72185817 | |||
03a90922d8 | |||
38fe9c149b | |||
cdee0a620c | |||
fccabf6eb5 | |||
3dae674c4e | |||
c76134b064 | |||
284c2bd87f | |||
543295fc21 | |||
![]() |
894cc24eca | ||
![]() |
49849605c4 | ||
![]() |
fc18e71f40 | ||
![]() |
595458aa46 | ||
![]() |
87e1f7eb7a | ||
![]() |
eefe2ad454 | ||
![]() |
928f207668 | ||
![]() |
7c75c2ddec | ||
469a398db7 | |||
![]() |
5f1671ac54 | ||
![]() |
67728021d2 | ||
![]() |
2eaf5bf6da | ||
![]() |
12b7c9de89 | ||
![]() |
fbd0adf292 | ||
![]() |
38bd99c2f1 | ||
![]() |
879a251209 | ||
![]() |
976703aa36 | ||
![]() |
95042dca93 | ||
![]() |
0a46403f6c | ||
![]() |
f958e47265 | ||
![]() |
831ea5225e | ||
![]() |
5a7cf68858 | ||
6219b082de | |||
321079a639 | |||
425860de98 | |||
![]() |
fb85aac231 | ||
![]() |
86adc942ca | ||
![]() |
1ec37d5a1c | ||
![]() |
bae927411f | ||
![]() |
528146310a | ||
![]() |
0d2e256254 | ||
![]() |
b425462cc6 | ||
![]() |
2ad93154de | ||
![]() |
e905040c68 | ||
![]() |
db53593fef | ||
![]() |
35bb5282bc | ||
![]() |
608f2a248d | ||
![]() |
2525e5ca0d | ||
![]() |
5c014bcb07 | ||
![]() |
f81e31529d | ||
![]() |
f874fb3f81 | ||
![]() |
ca19afe3a0 | ||
![]() |
930c0d6529 | ||
a84305b6d3 | |||
6b97c5123f | |||
0322939cba | |||
8ed80da82c | |||
90acd9ebea | |||
![]() |
58152ef3dc | ||
![]() |
b433f0a113 | ||
![]() |
2192675071 | ||
![]() |
000516678c | ||
![]() |
1e382b55f1 | ||
![]() |
8a7b9e9368 | ||
![]() |
26bede0b05 | ||
![]() |
8a9d034d60 | ||
![]() |
4a46896372 | ||
![]() |
4352e1510d | ||
![]() |
c4728fbb34 | ||
![]() |
f82dc9b3b8 | ||
![]() |
403e4c2719 | ||
![]() |
2b7dbe0a2c | ||
![]() |
8b1744fe01 | ||
![]() |
c21bb125b9 | ||
![]() |
4ef3406a07 | ||
![]() |
28954a4968 | ||
![]() |
77bd3c8290 | ||
![]() |
fba1854795 | ||
![]() |
d21bf7c97d | ||
![]() |
f2db08ad7f | ||
![]() |
14248ed0ff | ||
![]() |
7fdc48fbaf | ||
![]() |
aec18b1bcf | ||
![]() |
b07bc39e57 | ||
![]() |
78eee2c2d8 | ||
![]() |
8c170d12e2 | ||
![]() |
deb84ba2c4 | ||
![]() |
ea3286b452 | ||
![]() |
84d2e4b4bd | ||
![]() |
e0ba68f1ed | ||
![]() |
1ede045625 | ||
![]() |
b844fec832 | ||
![]() |
2ab15b3f6e | ||
6910809a97 | |||
![]() |
0f19ac0a3f | ||
![]() |
05c397c252 | ||
329010e8e0 | |||
![]() |
ec1b8a0df2 | ||
![]() |
9c3868bfc9 | ||
40689caef6 | |||
021dd0ae52 | |||
002d0a5709 | |||
10b17b2d6f | |||
0f5a10c3fa | |||
590d91c2d4 | |||
5f7f34f29d | |||
![]() |
90be9a09bf | ||
![]() |
8445140df9 | ||
![]() |
4fc8bc1ddc | ||
![]() |
3ebc137208 | ||
![]() |
0ff0cc9cd9 | ||
![]() |
6d63195f72 | ||
![]() |
3129b0961e | ||
![]() |
156c62cc43 | ||
![]() |
81af079fb5 | ||
![]() |
987b4a3f32 | ||
![]() |
a426e23138 | ||
![]() |
7a66d9aaae | ||
![]() |
aa54cd6f24 | ||
![]() |
8e7366c5ed | ||
![]() |
1088190830 | ||
![]() |
1e009bdd11 | ||
![]() |
2a031ab1ef | ||
![]() |
ce9099c230 | ||
![]() |
396b8a5465 | ||
![]() |
799f432ea4 | ||
![]() |
c0cf43b12d | ||
![]() |
98a1398c17 | ||
![]() |
979b42dd2c | ||
![]() |
bcfee1489f | ||
![]() |
4f33a3e765 | ||
![]() |
d424783689 | ||
![]() |
dd6d771d6e | ||
![]() |
898fc6d81a | ||
![]() |
eb2ff2f543 | ||
![]() |
b33c975632 | ||
![]() |
e3acbf2b76 | ||
![]() |
68625880f8 | ||
![]() |
9dfd2cd2b6 | ||
![]() |
8d22b475ce | ||
![]() |
ed68871645 | ||
![]() |
6860f5a6f3 | ||
![]() |
d57cae035d | ||
![]() |
7d3b2a8181 | ||
![]() |
0907e05d81 | ||
![]() |
955a6e27d3 | ||
![]() |
41d56259da | ||
![]() |
230146d20e | ||
![]() |
793315c151 | ||
![]() |
84d120d17b | ||
![]() |
13e598c212 | ||
![]() |
8d9af25797 | ||
![]() |
9ba426c101 | ||
![]() |
be9ad7d3ba | ||
![]() |
1ad9f11c1b | ||
![]() |
ab07faec2d | ||
![]() |
3bd7357b10 | ||
![]() |
8e9d5333ef | ||
![]() |
bd5433f9ba | ||
![]() |
0a72669396 | ||
![]() |
c951549942 | ||
![]() |
3c5a856383 | ||
![]() |
48753291c3 | ||
![]() |
1f11fd4503 | ||
![]() |
e968d4df7c | ||
![]() |
c30b1a61ad | ||
![]() |
5376a63375 | ||
![]() |
5b4d2accbf | ||
![]() |
95714425b3 | ||
![]() |
62ae44b1c6 | ||
![]() |
b14f5ffd00 | ||
![]() |
4ad6cf6f34 | ||
![]() |
c41c19a03a | ||
![]() |
f34c4a66ff | ||
![]() |
b57fb401b9 | ||
![]() |
13aeb23972 | ||
![]() |
9a4015b448 | ||
![]() |
634c41cca5 | ||
![]() |
e12e237f05 | ||
![]() |
23762fe0c0 | ||
![]() |
a8831abe6c | ||
![]() |
dae7e39cf0 | ||
![]() |
06f55753bc | ||
![]() |
2c856d928b | ||
![]() |
f616574c91 | ||
![]() |
d55e1937ee | ||
![]() |
d58b1316e6 | ||
![]() |
298d935285 | ||
![]() |
6287def612 | ||
![]() |
4577952a71 | ||
![]() |
e03edf22ad | ||
![]() |
2a0e265411 | ||
![]() |
31f5835de8 | ||
![]() |
b69e4f376f | ||
![]() |
9883a3bd9d | ||
![]() |
57bb08cd04 | ||
![]() |
2059e476fb | ||
![]() |
6611bdafc3 | ||
![]() |
563ca968e6 | ||
![]() |
fe457fabe6 | ||
![]() |
d31f226b11 | ||
![]() |
418590f82d | ||
![]() |
61a8429870 | ||
![]() |
1849532c58 | ||
![]() |
72285e4620 | ||
![]() |
77e4dcef04 | ||
![]() |
3bba679d48 | ||
![]() |
71677c27e3 | ||
![]() |
6d78004dc7 | ||
![]() |
32a320fdf8 | ||
![]() |
2325872a38 | ||
![]() |
93e52492d3 | ||
![]() |
05e226f812 | ||
![]() |
92d15e0905 | ||
![]() |
83a38951a9 | ||
![]() |
63cb6c0df0 | ||
![]() |
70202a66a3 | ||
![]() |
cc30c676ef | ||
![]() |
5132b7dbeb | ||
![]() |
bab9215059 | ||
![]() |
d63ca0281b | ||
![]() |
c0ce551727 | ||
![]() |
2937ee6794 | ||
![]() |
ae39b9496d | ||
![]() |
3dc226a15a | ||
![]() |
f1fcba5aa7 | ||
![]() |
6fde0eea08 | ||
![]() |
19509cc1fa | ||
![]() |
5ab652a7f7 | ||
![]() |
767608584d | ||
![]() |
1cb6f4889b | ||
![]() |
465ada9296 | ||
![]() |
6b78f91bc7 | ||
![]() |
f13aad9d7f | ||
![]() |
e72b4cd997 | ||
![]() |
9d13060d65 | ||
![]() |
edb0f0cb2a | ||
![]() |
06f68eacf8 | ||
![]() |
3ac12bb179 | ||
![]() |
ee294908ff | ||
![]() |
1b5bb3b7ae | ||
![]() |
9142cad3c2 | ||
![]() |
81c09e6759 | ||
![]() |
db7d032916 | ||
![]() |
69729f5ac0 | ||
![]() |
d5235e7881 | ||
![]() |
9e4691bc05 | ||
![]() |
738182cf3a | ||
![]() |
ab2def8145 | ||
![]() |
9226ecdd90 | ||
![]() |
0ec60eb54f | ||
![]() |
ae08334335 | ||
![]() |
a0aa6262f9 | ||
![]() |
10b8b4214e | ||
![]() |
cea32f95d0 | ||
![]() |
b27be24cb7 | ||
![]() |
5c321e7601 | ||
![]() |
1ebbd90701 | ||
![]() |
70883d844b | ||
![]() |
d01085427a | ||
![]() |
ee74605b19 | ||
![]() |
aaf7fca14a | ||
![]() |
9ed6af000d | ||
![]() |
d24f114636 | ||
![]() |
fd3ac675f4 | ||
![]() |
4d161c3c7c | ||
![]() |
89dd25370a | ||
![]() |
cdeb0ece1c | ||
![]() |
13c6094a89 | ||
![]() |
2fd756aaff | ||
![]() |
9eb87d50f6 | ||
![]() |
1ec32bf119 | ||
![]() |
9df58cc7b5 | ||
![]() |
281a6853d8 | ||
![]() |
5223d64f59 | ||
![]() |
915dcad2e9 | ||
![]() |
a2ebbcba60 | ||
![]() |
089c984fbb | ||
![]() |
9d66b23c8e | ||
![]() |
33ccb0a8a9 | ||
![]() |
c06c6beff4 | ||
438115d8e8 | |||
650747f423 | |||
![]() |
80dd29fb13 | ||
28283c72a3 | |||
![]() |
30ce19ad6c | ||
a17616b3dc | |||
![]() |
d5ad0fede5 | ||
52ee056759 | |||
fe43ce562c | |||
503bf72b11 | |||
14a956d207 | |||
![]() |
cd9756ae61 | ||
68651ff736 | |||
49c32ad4b8 | |||
bd69745cef | |||
![]() |
404e545a6d | ||
23468c7c63 | |||
fb08352a91 | |||
00b33550f4 | |||
![]() |
1b61545375 | ||
![]() |
31beabe86e | ||
![]() |
bb531a41d0 | ||
6db1e0b79c | |||
94ad7ce180 | |||
![]() |
7112d0e159 | ||
5620635a36 |
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -9,3 +9,5 @@ updates:
|
|||||||
directory: "/" # Location of package manifests
|
directory: "/" # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
ignore:
|
||||||
|
- dependency-name: "lief"
|
@@ -2,6 +2,25 @@ TWBlue Changelog
|
|||||||
|
|
||||||
## changes in this version
|
## changes in this version
|
||||||
|
|
||||||
|
* Mastodon:
|
||||||
|
* A language selector has been added for posting in TWBlue. It is now possible to choose the language in which a post will be made, which will be useful for content filtering and other language-dependent features. The default language can be chosen based on your Mastodon account’s language, the language of the post you’re replying to, or, if no automatic selection is possible, TWBlue’s own language will be used by default.
|
||||||
|
* TWBlue should be able to display all posts in the post displayer dialog.
|
||||||
|
* reading long posts in the graphical user interface should work better.
|
||||||
|
|
||||||
|
## Changes in version 2024.5.23
|
||||||
|
|
||||||
|
* Core:
|
||||||
|
* The way sessions are named has been changed. Now the account is indicated first, followed by the social network it belongs to.
|
||||||
|
* An option has been added to the global options dialog that allows for the reading of long posts in the graphical interface. This is especially useful since, by default, the graphical interface can only display a limited number of characters in the post.
|
||||||
|
* Some options that are no longer necessary in the application have been removed from the global settings dialog.
|
||||||
|
* Mastodon:
|
||||||
|
* fixed an error that caused TWBlue to not display some posts correctly.
|
||||||
|
* Fixed name for community timelines when created during startup. Now it should be clear if it's a federated or local timeline.
|
||||||
|
* Defined shortcuts to fields on the update profile dialog so it will be easier to navigate.
|
||||||
|
* Now it is possible to load conversations directly from community timelines.
|
||||||
|
|
||||||
|
## Changes in version 2024.5.19
|
||||||
|
|
||||||
In this version of TWBlue, which is being released several months after the previous one, we focused on adding initial support for GoToSocial-type networks. GoToSocial is a server for creating decentralized networks similar to Mastodon. Its API is very similar but retains some differences. In this version, TWBlue can be used to log into GoToSocial accounts, although there will be some features, such as the Streaming API and Markdown support, that are not yet functional. Another significant addition is support for creating community timelines, which will allow you to load the local and public timeline of remote instances. This is useful if your instance does not federate directly with them, as it will allow you to see posts from other communities and interact directly with them. Finally, the translation module has been rewritten; it now supports using LibreTranslate by default and DeepL, for which an API key is required. Below is the detailed list of changes:
|
In this version of TWBlue, which is being released several months after the previous one, we focused on adding initial support for GoToSocial-type networks. GoToSocial is a server for creating decentralized networks similar to Mastodon. Its API is very similar but retains some differences. In this version, TWBlue can be used to log into GoToSocial accounts, although there will be some features, such as the Streaming API and Markdown support, that are not yet functional. Another significant addition is support for creating community timelines, which will allow you to load the local and public timeline of remote instances. This is useful if your instance does not federate directly with them, as it will allow you to see posts from other communities and interact directly with them. Finally, the translation module has been rewritten; it now supports using LibreTranslate by default and DeepL, for which an API key is required. Below is the detailed list of changes:
|
||||||
|
|
||||||
* Core:
|
* Core:
|
||||||
|
1543
doc/locales/bg/LC_MESSAGES/twblue-documentation.po
Normal file
1543
doc/locales/bg/LC_MESSAGES/twblue-documentation.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,57 +1,57 @@
|
|||||||
accessible_output2 @ git+https://github.com/accessibleapps/accessible_output2@57bda997d98e87dd78aa049e7021cf777871619b
|
accessible_output2 @ git+https://github.com/accessibleapps/accessible_output2@57bda997d98e87dd78aa049e7021cf777871619b
|
||||||
arrow==1.3.0
|
arrow==1.3.0
|
||||||
attrs==23.2.0
|
attrs==25.1.0
|
||||||
backports.functools-lru-cache==2.0.0
|
backports.functools-lru-cache==2.0.0
|
||||||
blurhash==1.1.4
|
blurhash==1.1.4
|
||||||
certifi==2024.2.2
|
certifi==2025.1.31
|
||||||
chardet==5.2.0
|
chardet==5.2.0
|
||||||
charset-normalizer==3.3.2
|
charset-normalizer==3.4.1
|
||||||
colorama==0.4.6
|
colorama==0.4.6
|
||||||
configobj==5.0.8
|
configobj==5.0.9
|
||||||
coverage==7.5.1
|
coverage==7.6.12
|
||||||
cx-Freeze==7.0.0
|
cx-Freeze==7.2.10
|
||||||
cx-Logging==3.2.0
|
cx-Logging==3.2.1
|
||||||
decorator==5.1.1
|
decorator==5.2.1
|
||||||
demoji==1.1.0
|
demoji==1.1.0
|
||||||
deepl==1.18.0
|
deepl==1.21.0
|
||||||
future==1.0.0
|
future==1.0.0
|
||||||
idna==3.7
|
idna==3.10
|
||||||
importlib-metadata==7.1.0
|
importlib-metadata==8.6.1
|
||||||
iniconfig==2.0.0
|
iniconfig==2.0.0
|
||||||
libloader @ git+https://github.com/accessibleapps/libloader@bc94811c095b2e57a036acd88660be9a33260267
|
libloader @ git+https://github.com/accessibleapps/libloader@bc94811c095b2e57a036acd88660be9a33260267
|
||||||
libretranslatepy==2.1.4
|
libretranslatepy==2.1.4
|
||||||
lief==0.14.1
|
lief==0.15.1
|
||||||
Markdown==3.6
|
Markdown==3.7
|
||||||
Mastodon.py==1.8.1
|
Mastodon.py==2.0.1
|
||||||
numpy==1.26.4
|
numpy==2.2.3
|
||||||
oauthlib==3.2.2
|
oauthlib==3.2.2
|
||||||
packaging==24.0
|
packaging==24.2
|
||||||
pillow==10.3.0
|
pillow==11.1.0
|
||||||
platform_utils @ git+https://github.com/accessibleapps/platform_utils@e0d79f7b399c4ea677a633d2dde9202350d62c38
|
platform_utils @ git+https://github.com/accessibleapps/platform_utils@e0d79f7b399c4ea677a633d2dde9202350d62c38
|
||||||
pluggy==1.5.0
|
pluggy==1.5.0
|
||||||
psutil==5.9.8
|
psutil==7.0.0
|
||||||
pyenchant==3.2.2
|
pyenchant==3.2.2
|
||||||
pypiwin32==223
|
pypiwin32==223
|
||||||
Pypubsub==4.0.3
|
Pypubsub==4.0.3
|
||||||
PySocks==1.7.1
|
PySocks==1.7.1
|
||||||
pytest==8.2.0
|
pytest==8.3.5
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
python-magic-bin==0.4.14
|
python-magic-bin==0.4.14
|
||||||
python-vlc==3.0.20123
|
python-vlc==3.0.21203
|
||||||
pywin32==306
|
pywin32==308
|
||||||
requests==2.31.0
|
requests==2.32.3
|
||||||
requests-oauthlib==2.0.0
|
requests-oauthlib==2.0.0
|
||||||
requests-toolbelt==1.0.0
|
requests-toolbelt==1.0.0
|
||||||
rfc3986==2.0.0
|
rfc3986==2.0.0
|
||||||
six==1.16.0
|
six==1.17.0
|
||||||
sniffio==1.3.1
|
sniffio==1.3.1
|
||||||
sound_lib @ git+https://github.com/accessibleapps/sound_lib@a439f0943fb95ee7b6ba24f51a686f47c4ad66b2
|
sound_lib @ git+https://github.com/accessibleapps/sound_lib@a439f0943fb95ee7b6ba24f51a686f47c4ad66b2
|
||||||
sqlitedict==2.1.0
|
sqlitedict==2.1.0
|
||||||
twitter-text-parser==3.0.0
|
twitter-text-parser==3.0.0
|
||||||
types-python-dateutil==2.9.0.20240316
|
types-python-dateutil==2.9.0.20241206
|
||||||
urllib3==2.2.1
|
urllib3==2.3.0
|
||||||
win-inet-pton==1.1.0
|
win-inet-pton==1.1.0
|
||||||
winpaths==0.2
|
winpaths==0.2
|
||||||
wxPython==4.2.1
|
wxPython==4.2.2
|
||||||
youtube-dl==2021.12.17
|
youtube-dl==2021.12.17
|
||||||
zipp==3.18.1
|
zipp==3.21.0
|
@@ -9,8 +9,7 @@ update_period = integer(default=2)
|
|||||||
hide_gui = boolean(default=False)
|
hide_gui = boolean(default=False)
|
||||||
voice_enabled = boolean(default=False)
|
voice_enabled = boolean(default=False)
|
||||||
ask_at_exit = boolean(default=True)
|
ask_at_exit = boolean(default=True)
|
||||||
autostart = boolean(default=False)
|
read_long_posts_in_gui = boolean(default=True)
|
||||||
handle_longtweets = boolean(default=True)
|
|
||||||
use_invisible_keyboard_shorcuts = boolean(default=True)
|
use_invisible_keyboard_shorcuts = boolean(default=True)
|
||||||
play_ready_sound = boolean(default=True)
|
play_ready_sound = boolean(default=True)
|
||||||
speak_ready_msg = boolean(default=True)
|
speak_ready_msg = boolean(default=True)
|
||||||
@@ -18,9 +17,6 @@ log_level = string(default="error")
|
|||||||
load_keymap = string(default="default.keymap")
|
load_keymap = string(default="default.keymap")
|
||||||
donation_dialog_displayed = boolean(default=False)
|
donation_dialog_displayed = boolean(default=False)
|
||||||
check_for_updates = boolean(default=True)
|
check_for_updates = boolean(default=True)
|
||||||
remember_mention_and_longtweet = boolean(default=False)
|
|
||||||
longtweet = boolean(default=false)
|
|
||||||
mention_all = boolean(default=False)
|
|
||||||
no_streaming = boolean(default=False)
|
no_streaming = boolean(default=False)
|
||||||
|
|
||||||
[proxy]
|
[proxy]
|
||||||
|
@@ -70,7 +70,7 @@ class BaseBuffer(base.Buffer):
|
|||||||
response = post.message.ShowModal()
|
response = post.message.ShowModal()
|
||||||
if response == wx.ID_OK:
|
if response == wx.ID_OK:
|
||||||
post_data = post.get_data()
|
post_data = post.get_data()
|
||||||
call_threaded(self.session.send_post, posts=post_data, visibility=post.get_visibility(), **kwargs)
|
call_threaded(self.session.send_post, posts=post_data, visibility=post.get_visibility(), language=post.get_language(), **kwargs)
|
||||||
if hasattr(post.message, "destroy"):
|
if hasattr(post.message, "destroy"):
|
||||||
post.message.destroy()
|
post.message.destroy()
|
||||||
|
|
||||||
@@ -108,13 +108,28 @@ class BaseBuffer(base.Buffer):
|
|||||||
min_id = self.session.db[self.name][0].id
|
min_id = self.session.db[self.name][0].id
|
||||||
else:
|
else:
|
||||||
min_id = self.session.db[self.name][-1].id
|
min_id = self.session.db[self.name][-1].id
|
||||||
|
# loads pinned posts from user accounts.
|
||||||
|
# Load those posts only when there are no items previously loaded.
|
||||||
|
if "-timeline" in self.name and "account_statuses" in self.function and len(self.session.db.get(self.name, [])) == 0:
|
||||||
|
pinned_posts = self.session.api.account_statuses(pinned=True, limit=count, *self.args, **self.kwargs)
|
||||||
|
pinned_posts.reverse()
|
||||||
|
else:
|
||||||
|
pinned_posts = None
|
||||||
try:
|
try:
|
||||||
results = getattr(self.session.api, self.function)(min_id=min_id, limit=count, *self.args, **self.kwargs)
|
results = getattr(self.session.api, self.function)(min_id=min_id, limit=count, *self.args, **self.kwargs)
|
||||||
results.reverse()
|
results.reverse()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception("Error %s" % (str(e)))
|
log.exception("Error %s" % (str(e)))
|
||||||
return
|
return
|
||||||
|
if self.session.settings["general"]["reverse_timelines"]:
|
||||||
|
if pinned_posts != None and len(pinned_posts) > 0:
|
||||||
|
amount_of_pinned_posts = self.session.order_buffer(self.name, pinned_posts)
|
||||||
number_of_items = self.session.order_buffer(self.name, results)
|
number_of_items = self.session.order_buffer(self.name, results)
|
||||||
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
|
if pinned_posts != None and len(pinned_posts) > 0:
|
||||||
|
amount_of_pinned_posts = self.session.order_buffer(self.name, pinned_posts)
|
||||||
|
if pinned_posts != None and len(pinned_posts) > 0:
|
||||||
|
number_of_items = amount_of_pinned_posts+number_of_items
|
||||||
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
log.debug("Number of items retrieved: %d" % (number_of_items,))
|
||||||
if hasattr(self, "finished_timeline") and self.finished_timeline == False:
|
if hasattr(self, "finished_timeline") and self.finished_timeline == False:
|
||||||
if "-timeline" in self.name:
|
if "-timeline" in self.name:
|
||||||
@@ -157,6 +172,9 @@ class BaseBuffer(base.Buffer):
|
|||||||
items_db = self.session.db[self.name]
|
items_db = self.session.db[self.name]
|
||||||
for i in items:
|
for i in items:
|
||||||
if utils.find_item(i, self.session.db[self.name]) == None:
|
if utils.find_item(i, self.session.db[self.name]) == None:
|
||||||
|
filter_status = utils.evaluate_filters(post=i, current_context=utils.get_current_context(self.name))
|
||||||
|
if filter_status == "hide":
|
||||||
|
continue
|
||||||
elements.append(i)
|
elements.append(i)
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
items_db.insert(0, i)
|
items_db.insert(0, i)
|
||||||
@@ -335,16 +353,19 @@ class BaseBuffer(base.Buffer):
|
|||||||
visibility = "unlisted"
|
visibility = "unlisted"
|
||||||
if item.reblog != None:
|
if item.reblog != None:
|
||||||
users = ["@{} ".format(user.acct) for user in item.reblog.mentions if user.id != self.session.db["user_id"]]
|
users = ["@{} ".format(user.acct) for user in item.reblog.mentions if user.id != self.session.db["user_id"]]
|
||||||
|
language = item.reblog.language
|
||||||
if item.reblog.account.acct != item.account.acct and "@{} ".format(item.reblog.account.acct) not in users:
|
if item.reblog.account.acct != item.account.acct and "@{} ".format(item.reblog.account.acct) not in users:
|
||||||
users.append("@{} ".format(item.reblog.account.acct))
|
users.append("@{} ".format(item.reblog.account.acct))
|
||||||
else:
|
else:
|
||||||
users = ["@{} ".format(user.acct) for user in item.mentions if user.id != self.session.db["user_id"]]
|
users = ["@{} ".format(user.acct) for user in item.mentions if user.id != self.session.db["user_id"]]
|
||||||
|
language = item.language
|
||||||
if "@{} ".format(item.account.acct) not in users and item.account.id != self.session.db["user_id"]:
|
if "@{} ".format(item.account.acct) not in users and item.account.id != self.session.db["user_id"]:
|
||||||
users.insert(0, "@{} ".format(item.account.acct))
|
users.insert(0, "@{} ".format(item.account.acct))
|
||||||
users_str = "".join(users)
|
users_str = "".join(users)
|
||||||
post = messages.post(session=self.session, title=title, caption=caption, text=users_str)
|
post = messages.post(session=self.session, title=title, caption=caption, text=users_str)
|
||||||
visibility_settings = dict(public=0, unlisted=1, private=2, direct=3)
|
visibility_settings = dict(public=0, unlisted=1, private=2, direct=3)
|
||||||
post.message.visibility.SetSelection(visibility_settings.get(visibility))
|
post.message.visibility.SetSelection(visibility_settings.get(visibility))
|
||||||
|
post.set_language(language)
|
||||||
# Respect content warning settings.
|
# Respect content warning settings.
|
||||||
if item.sensitive:
|
if item.sensitive:
|
||||||
post.message.sensitive.SetValue(item.sensitive)
|
post.message.sensitive.SetValue(item.sensitive)
|
||||||
@@ -353,7 +374,7 @@ class BaseBuffer(base.Buffer):
|
|||||||
response = post.message.ShowModal()
|
response = post.message.ShowModal()
|
||||||
if response == wx.ID_OK:
|
if response == wx.ID_OK:
|
||||||
post_data = post.get_data()
|
post_data = post.get_data()
|
||||||
call_threaded(self.session.send_post, reply_to=item.id, posts=post_data, visibility=post.get_visibility())
|
call_threaded(self.session.send_post, reply_to=item.id, posts=post_data, visibility=post.get_visibility(), language=post.get_language())
|
||||||
if hasattr(post.message, "destroy"):
|
if hasattr(post.message, "destroy"):
|
||||||
post.message.destroy()
|
post.message.destroy()
|
||||||
|
|
||||||
@@ -380,7 +401,7 @@ class BaseBuffer(base.Buffer):
|
|||||||
response = post.message.ShowModal()
|
response = post.message.ShowModal()
|
||||||
if response == wx.ID_OK:
|
if response == wx.ID_OK:
|
||||||
post_data = post.get_data()
|
post_data = post.get_data()
|
||||||
call_threaded(self.session.send_post, posts=post_data, visibility="direct", reply_to=item.id)
|
call_threaded(self.session.send_post, posts=post_data, visibility="direct", reply_to=item.id, language=post.get_language())
|
||||||
if hasattr(post.message, "destroy"):
|
if hasattr(post.message, "destroy"):
|
||||||
post.message.destroy()
|
post.message.destroy()
|
||||||
|
|
||||||
@@ -406,6 +427,8 @@ class BaseBuffer(base.Buffer):
|
|||||||
original_date = arrow.get(self.session.db[self.name][self.buffer.list.get_selected()].created_at)
|
original_date = arrow.get(self.session.db[self.name][self.buffer.list.get_selected()].created_at)
|
||||||
ts = original_date.humanize(locale=languageHandler.getLanguage())
|
ts = original_date.humanize(locale=languageHandler.getLanguage())
|
||||||
self.buffer.list.list.SetItem(self.buffer.list.get_selected(), 2, ts)
|
self.buffer.list.list.SetItem(self.buffer.list.get_selected(), 2, ts)
|
||||||
|
if config.app["app-settings"]["read_long_posts_in_gui"] == True and self.buffer.list.list.HasFocus():
|
||||||
|
wx.CallLater(40, output.speak, self.get_message(), interrupt=True)
|
||||||
if self.session.settings['sound']['indicate_audio'] and utils.is_audio_or_video(post):
|
if self.session.settings['sound']['indicate_audio'] and utils.is_audio_or_video(post):
|
||||||
self.session.sound.play("audio.ogg")
|
self.session.sound.play("audio.ogg")
|
||||||
if self.session.settings['sound']['indicate_img'] and utils.is_image(post):
|
if self.session.settings['sound']['indicate_img'] and utils.is_image(post):
|
||||||
@@ -549,7 +572,7 @@ class BaseBuffer(base.Buffer):
|
|||||||
except MastodonNotFoundError:
|
except MastodonNotFoundError:
|
||||||
output.speak(_("No status found with that ID"))
|
output.speak(_("No status found with that ID"))
|
||||||
return
|
return
|
||||||
# print(post)
|
# print(item)
|
||||||
msg = messages.viewPost(self.session, item, offset_hours=self.session.db["utc_offset"], item_url=self.get_item_url(item=item))
|
msg = messages.viewPost(self.session, item, offset_hours=self.session.db["utc_offset"], item_url=self.get_item_url(item=item))
|
||||||
|
|
||||||
def ocr_image(self):
|
def ocr_image(self):
|
||||||
@@ -619,14 +642,14 @@ class BaseBuffer(base.Buffer):
|
|||||||
return
|
return
|
||||||
poll = self.session.api_call(call_name="poll_vote", id=poll.id, choices=options, preexec_message=_("Sending vote..."))
|
poll = self.session.api_call(call_name="poll_vote", id=poll.id, choices=options, preexec_message=_("Sending vote..."))
|
||||||
|
|
||||||
def post_from_error(self, visibility, reply_to, data):
|
def post_from_error(self, visibility, reply_to, data, lang):
|
||||||
title = _("Post")
|
title = _("Post")
|
||||||
caption = _("Write your post here")
|
caption = _("Write your post here")
|
||||||
post = messages.post(session=self.session, title=title, caption=caption)
|
post = messages.post(session=self.session, title=title, caption=caption)
|
||||||
post.set_post_data(visibility=visibility, data=data)
|
post.set_post_data(visibility=visibility, data=data, language=language)
|
||||||
response = post.message.ShowModal()
|
response = post.message.ShowModal()
|
||||||
if response == wx.ID_OK:
|
if response == wx.ID_OK:
|
||||||
post_data = post.get_data()
|
post_data = post.get_data()
|
||||||
call_threaded(self.session.send_post, posts=post_data, reply_to=reply_to, visibility=post.get_visibility())
|
call_threaded(self.session.send_post, posts=post_data, reply_to=reply_to, visibility=post.get_visibility(), language=post.get_language())
|
||||||
if hasattr(post.message, "destroy"):
|
if hasattr(post.message, "destroy"):
|
||||||
post.message.destroy()
|
post.message.destroy()
|
||||||
|
@@ -4,6 +4,7 @@ import logging
|
|||||||
import wx
|
import wx
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
import output
|
import output
|
||||||
|
import config
|
||||||
from mastodon import MastodonNotFoundError
|
from mastodon import MastodonNotFoundError
|
||||||
from controller.mastodon import messages
|
from controller.mastodon import messages
|
||||||
from controller.buffers.mastodon.base import BaseBuffer
|
from controller.buffers.mastodon.base import BaseBuffer
|
||||||
@@ -162,6 +163,8 @@ class ConversationListBuffer(BaseBuffer):
|
|||||||
|
|
||||||
def onFocus(self, *args, **kwargs):
|
def onFocus(self, *args, **kwargs):
|
||||||
post = self.get_item()
|
post = self.get_item()
|
||||||
|
if config.app["app-settings"]["read_long_posts_in_gui"] == True and self.buffer.list.list.HasFocus():
|
||||||
|
wx.CallLater(40, output.speak, self.get_message(), interrupt=True)
|
||||||
if self.session.settings['sound']['indicate_audio'] and utils.is_audio_or_video(post):
|
if self.session.settings['sound']['indicate_audio'] and utils.is_audio_or_video(post):
|
||||||
self.session.sound.play("audio.ogg")
|
self.session.sound.play("audio.ogg")
|
||||||
if self.session.settings['sound']['indicate_img'] and utils.is_image(post):
|
if self.session.settings['sound']['indicate_img'] and utils.is_image(post):
|
||||||
@@ -188,7 +191,7 @@ class ConversationListBuffer(BaseBuffer):
|
|||||||
response = post.message.ShowModal()
|
response = post.message.ShowModal()
|
||||||
if response == wx.ID_OK:
|
if response == wx.ID_OK:
|
||||||
post_data = post.get_data()
|
post_data = post.get_data()
|
||||||
call_threaded(self.session.send_post, reply_to=item.id, posts=post_data, visibility=visibility)
|
call_threaded(self.session.send_post, reply_to=item.id, posts=post_data, visibility=visibility, language=post.get_language())
|
||||||
if hasattr(post.message, "destroy"):
|
if hasattr(post.message, "destroy"):
|
||||||
post.message.destroy()
|
post.message.destroy()
|
||||||
|
|
||||||
|
@@ -56,6 +56,9 @@ class MentionsBuffer(BaseBuffer):
|
|||||||
items_db = self.session.db[self.name]
|
items_db = self.session.db[self.name]
|
||||||
for i in items:
|
for i in items:
|
||||||
if utils.find_item(i, self.session.db[self.name]) == None:
|
if utils.find_item(i, self.session.db[self.name]) == None:
|
||||||
|
filter_status = utils.evaluate_filters(post=i, current_context=utils.get_current_context(self.name))
|
||||||
|
if filter_status == "hide":
|
||||||
|
continue
|
||||||
elements.append(i)
|
elements.append(i)
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
if self.session.settings["general"]["reverse_timelines"] == False:
|
||||||
items_db.insert(0, i)
|
items_db.insert(0, i)
|
||||||
|
@@ -1,8 +1,12 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
import arrow
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
|
import wx
|
||||||
import output
|
import output
|
||||||
|
import languageHandler
|
||||||
|
import config
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from controller.buffers.mastodon.base import BaseBuffer
|
from controller.buffers.mastodon.base import BaseBuffer
|
||||||
from controller.mastodon import messages
|
from controller.mastodon import messages
|
||||||
@@ -38,8 +42,11 @@ class NotificationsBuffer(BaseBuffer):
|
|||||||
original_date = arrow.get(self.session.db[self.name][self.buffer.list.get_selected()].created_at)
|
original_date = arrow.get(self.session.db[self.name][self.buffer.list.get_selected()].created_at)
|
||||||
ts = original_date.humanize(locale=languageHandler.getLanguage())
|
ts = original_date.humanize(locale=languageHandler.getLanguage())
|
||||||
self.buffer.list.list.SetItem(self.buffer.list.get_selected(), 1, ts)
|
self.buffer.list.list.SetItem(self.buffer.list.get_selected(), 1, ts)
|
||||||
|
if config.app["app-settings"]["read_long_posts_in_gui"] == True and self.buffer.list.list.HasFocus():
|
||||||
|
wx.CallLater(40, output.speak, self.get_message(), interrupt=True)
|
||||||
|
|
||||||
def bind_events(self):
|
def bind_events(self):
|
||||||
|
self.buffer.set_focus_function(self.onFocus)
|
||||||
widgetUtils.connect_event(self.buffer.list.list, widgetUtils.KEYPRESS, self.get_event)
|
widgetUtils.connect_event(self.buffer.list.list, widgetUtils.KEYPRESS, self.get_event)
|
||||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.post_status, self.buffer.post)
|
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.post_status, self.buffer.post)
|
||||||
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.destroy_status, self.buffer.dismiss)
|
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.destroy_status, self.buffer.dismiss)
|
||||||
@@ -119,7 +126,7 @@ class NotificationsBuffer(BaseBuffer):
|
|||||||
response = post.message.ShowModal()
|
response = post.message.ShowModal()
|
||||||
if response == wx.ID_OK:
|
if response == wx.ID_OK:
|
||||||
post_data = post.get_data()
|
post_data = post.get_data()
|
||||||
call_threaded(self.session.send_post, posts=post_data, visibility="direct")
|
call_threaded(self.session.send_post, posts=post_data, visibility="direct", language=post.get_language())
|
||||||
if hasattr(post.message, "destroy"):
|
if hasattr(post.message, "destroy"):
|
||||||
post.message.destroy()
|
post.message.destroy()
|
||||||
|
|
||||||
|
@@ -165,6 +165,8 @@ class Controller(object):
|
|||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.update_buffer, self.view.update_buffer)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.update_buffer, self.view.update_buffer)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.manage_aliases, self.view.manageAliases)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.manage_aliases, self.view.manageAliases)
|
||||||
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.report_error, self.view.reportError)
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.report_error, self.view.reportError)
|
||||||
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.create_filter, self.view.filter)
|
||||||
|
widgetUtils.connect_event(self.view, widgetUtils.MENU, self.manage_filters, self.view.manage_filters)
|
||||||
|
|
||||||
def set_systray_icon(self):
|
def set_systray_icon(self):
|
||||||
self.systrayIcon = sysTrayIcon.SysTrayIcon()
|
self.systrayIcon = sysTrayIcon.SysTrayIcon()
|
||||||
@@ -1086,10 +1088,10 @@ class Controller(object):
|
|||||||
# if "direct_messages" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
|
# if "direct_messages" not in buffer.session.settings["other_buffers"]["muted_buffers"]:
|
||||||
# self.notify(buffer.session, sound_to_play)
|
# self.notify(buffer.session, sound_to_play)
|
||||||
|
|
||||||
def mastodon_error_post(self, name, reply_to, visibility, posts):
|
def mastodon_error_post(self, name, reply_to, visibility, posts, language):
|
||||||
home = self.search_buffer("home_timeline", name)
|
home = self.search_buffer("home_timeline", name)
|
||||||
if home != None:
|
if home != None:
|
||||||
wx.CallAfter(home.post_from_error, visibility=visibility, reply_to=reply_to, data=posts)
|
wx.CallAfter(home.post_from_error, visibility=visibility, reply_to=reply_to, data=posts, lang=language)
|
||||||
|
|
||||||
def change_buffer_title(self, name, buffer, title):
|
def change_buffer_title(self, name, buffer, title):
|
||||||
buffer_index = self.view.search(buffer, name)
|
buffer_index = self.view.search(buffer, name)
|
||||||
@@ -1155,3 +1157,15 @@ class Controller(object):
|
|||||||
handler = self.get_handler(type=buffer.session.type)
|
handler = self.get_handler(type=buffer.session.type)
|
||||||
if handler and hasattr(handler, 'community_timeline'):
|
if handler and hasattr(handler, 'community_timeline'):
|
||||||
handler.community_timeline(self, buffer)
|
handler.community_timeline(self, buffer)
|
||||||
|
|
||||||
|
def create_filter(self, *args, **kwargs):
|
||||||
|
buffer = self.get_best_buffer()
|
||||||
|
handler = self.get_handler(type=buffer.session.type)
|
||||||
|
if handler and hasattr(handler, 'create_filter'):
|
||||||
|
handler.create_filter(self, buffer)
|
||||||
|
|
||||||
|
def manage_filters(self, *args, **kwargs):
|
||||||
|
buffer = self.get_best_buffer()
|
||||||
|
handler = self.get_handler(type=buffer.session.type)
|
||||||
|
if handler and hasattr(handler, 'manage_filters'):
|
||||||
|
handler.manage_filters(self, buffer)
|
1
src/controller/mastodon/filters/__init__.py
Normal file
1
src/controller/mastodon/filters/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
112
src/controller/mastodon/filters/create_filter.py
Normal file
112
src/controller/mastodon/filters/create_filter.py
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import widgetUtils
|
||||||
|
from wxUI.dialogs.mastodon.filters import create_filter as dialog
|
||||||
|
from mastodon import MastodonAPIError
|
||||||
|
|
||||||
|
class CreateFilterController(object):
|
||||||
|
def __init__(self, session, filter_data=None):
|
||||||
|
super(CreateFilterController, self).__init__()
|
||||||
|
self.session = session
|
||||||
|
self.filter_data = filter_data
|
||||||
|
self.dialog = dialog.CreateFilterDialog(parent=None)
|
||||||
|
if self.filter_data is not None:
|
||||||
|
self.keywords = self.filter_data.get("keywords")
|
||||||
|
self.load_filter_data()
|
||||||
|
else:
|
||||||
|
self.keywords = []
|
||||||
|
widgetUtils.connect_event(self.dialog.keyword_panel.add_button, widgetUtils.BUTTON_PRESSED, self.on_add_keyword)
|
||||||
|
widgetUtils.connect_event(self.dialog.keyword_panel.remove_button, widgetUtils.BUTTON_PRESSED, self.on_remove_keyword)
|
||||||
|
|
||||||
|
def on_add_keyword(self, event):
|
||||||
|
""" Adds a keyword to the list. """
|
||||||
|
keyword = self.dialog.keyword_panel.keyword_text.GetValue().strip()
|
||||||
|
whole_word = self.dialog.keyword_panel.whole_word_checkbox.GetValue()
|
||||||
|
if keyword:
|
||||||
|
for idx, kw in enumerate(self.keywords):
|
||||||
|
if kw['keyword'] == keyword:
|
||||||
|
return
|
||||||
|
keyword_data = {
|
||||||
|
'keyword': keyword,
|
||||||
|
'whole_word': whole_word
|
||||||
|
}
|
||||||
|
self.keywords.append(keyword_data)
|
||||||
|
self.dialog.keyword_panel.add_keyword(keyword, whole_word)
|
||||||
|
|
||||||
|
def on_remove_keyword(self, event):
|
||||||
|
removed = self.dialog.keyword_panel.remove_keyword()
|
||||||
|
if removed is not None:
|
||||||
|
self.keywords.pop(removed)
|
||||||
|
|
||||||
|
def get_expires_in_seconds(self, selection, value):
|
||||||
|
if selection == 0:
|
||||||
|
return None
|
||||||
|
if selection == 1:
|
||||||
|
return value * 3600
|
||||||
|
elif selection == 2:
|
||||||
|
return value * 86400
|
||||||
|
elif selection == 3:
|
||||||
|
return value * 604800
|
||||||
|
elif selection == 4:
|
||||||
|
return value * 2592000
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_expires_in(self, seconds):
|
||||||
|
if seconds is None:
|
||||||
|
self.dialog.expiration_choice.SetSelection(0)
|
||||||
|
self.dialog.expiration_value.Enable(False)
|
||||||
|
return
|
||||||
|
if seconds % 2592000 == 0 and seconds >= 2592000:
|
||||||
|
self.dialog.expiration_choice.SetSelection(4)
|
||||||
|
self.dialog.expiration_value.SetValue(seconds // 2592000)
|
||||||
|
elif seconds % 604800 == 0 and seconds >= 604800:
|
||||||
|
self.dialog.expiration_choice.SetSelection(3)
|
||||||
|
self.dialog.expiration_value.SetValue(seconds // 604800)
|
||||||
|
elif seconds % 86400 == 0 and seconds >= 86400:
|
||||||
|
self.dialog.expiration_choice.SetSelection(2)
|
||||||
|
self.dialog.expiration_value.SetValue(seconds // 86400)
|
||||||
|
else:
|
||||||
|
self.dialog.expiration_choice.SetSelection(1)
|
||||||
|
self.dialog.expiration_value.SetValue(max(1, seconds // 3600))
|
||||||
|
self.dialog.expiration_value.Enable(True)
|
||||||
|
|
||||||
|
def load_filter_data(self):
|
||||||
|
if 'title' in self.filter_data:
|
||||||
|
self.dialog.name_ctrl.SetValue(self.filter_data['title'])
|
||||||
|
self.dialog.SetTitle(_("Update Filter: {}").format(self.filter_data['title']))
|
||||||
|
if 'context' in self.filter_data:
|
||||||
|
for context in self.filter_data['context']:
|
||||||
|
if context in self.dialog.context_checkboxes:
|
||||||
|
self.dialog.context_checkboxes[context].SetValue(True)
|
||||||
|
if 'filter_action' in self.filter_data:
|
||||||
|
action_index = self.dialog.actions.index(self.filter_data['filter_action']) if self.filter_data['filter_action'] in self.dialog.actions else 0
|
||||||
|
self.dialog.action_choice.SetSelection(action_index)
|
||||||
|
if 'expires_in' in self.filter_data:
|
||||||
|
self.set_expires_in(self.filter_data['expires_in'])
|
||||||
|
print(self.filter_data)
|
||||||
|
if 'keywords' in self.filter_data:
|
||||||
|
self.keywords = self.filter_data['keywords']
|
||||||
|
self.dialog.keyword_panel.set_keywords(self.filter_data['keywords'])
|
||||||
|
|
||||||
|
def get_filter_data(self):
|
||||||
|
filter_data = {
|
||||||
|
'title': self.dialog.name_ctrl.GetValue(),
|
||||||
|
'context': [],
|
||||||
|
'filter_action': self.dialog.actions[self.dialog.action_choice.GetSelection()],
|
||||||
|
'expires_in': self.get_expires_in_seconds(selection=self.dialog.expiration_choice.GetSelection(), value=self.dialog.expiration_value.GetValue()),
|
||||||
|
'keywords_attributes': self.keywords
|
||||||
|
}
|
||||||
|
for context, checkbox in self.dialog.context_checkboxes.items():
|
||||||
|
if checkbox.GetValue():
|
||||||
|
filter_data['context'].append(context)
|
||||||
|
return filter_data
|
||||||
|
|
||||||
|
def get_response(self):
|
||||||
|
response = self.dialog.ShowModal()
|
||||||
|
if response == widgetUtils.OK:
|
||||||
|
filter_data = self.get_filter_data()
|
||||||
|
if self.filter_data == None:
|
||||||
|
result = self.session.api.create_filter_v2(**filter_data)
|
||||||
|
else:
|
||||||
|
result = self.session.api.update_filter_v2(filter_id=self.filter_data['id'], **filter_data)
|
||||||
|
return result
|
||||||
|
return None
|
99
src/controller/mastodon/filters/manage_filters.py
Normal file
99
src/controller/mastodon/filters/manage_filters.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import datetime
|
||||||
|
import wx
|
||||||
|
import widgetUtils
|
||||||
|
from wxUI import commonMessageDialogs
|
||||||
|
from wxUI.dialogs.mastodon.filters import manage_filters as dialog
|
||||||
|
from . import create_filter
|
||||||
|
from mastodon import MastodonError
|
||||||
|
|
||||||
|
class ManageFiltersController(object):
|
||||||
|
def __init__(self, session):
|
||||||
|
super(ManageFiltersController, self).__init__()
|
||||||
|
self.session = session
|
||||||
|
self.selected_filter_idx = -1
|
||||||
|
self.error_loading = False
|
||||||
|
self.dialog = dialog.ManageFiltersDialog(parent=None)
|
||||||
|
self.dialog.filter_list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_filter_selected)
|
||||||
|
self.dialog.filter_list.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.on_filter_deselected)
|
||||||
|
widgetUtils.connect_event(self.dialog.add_button, wx.EVT_BUTTON, self.on_add_filter)
|
||||||
|
widgetUtils.connect_event(self.dialog.edit_button, wx.EVT_BUTTON, self.on_edit_filter)
|
||||||
|
widgetUtils.connect_event(self.dialog.remove_button, wx.EVT_BUTTON, self.on_remove_filter)
|
||||||
|
self.load_filter_data()
|
||||||
|
|
||||||
|
def on_filter_selected(self, event):
|
||||||
|
"""Handle filter selection event."""
|
||||||
|
self.selected_filter_idx = event.GetIndex()
|
||||||
|
self.dialog.edit_button.Enable()
|
||||||
|
self.dialog.remove_button.Enable()
|
||||||
|
|
||||||
|
def on_filter_deselected(self, event):
|
||||||
|
"""Handle filter deselection event."""
|
||||||
|
self.selected_filter_idx = -1
|
||||||
|
self.dialog.edit_button.Disable()
|
||||||
|
self.dialog.remove_button.Disable()
|
||||||
|
|
||||||
|
def get_selected_filter_id(self):
|
||||||
|
"""Get the ID of the currently selected filter."""
|
||||||
|
if self.selected_filter_idx != -1:
|
||||||
|
return self.dialog.filter_list.GetItemData(self.selected_filter_idx)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def load_filter_data(self):
|
||||||
|
try:
|
||||||
|
filters = self.session.api.filters_v2()
|
||||||
|
self.dialog.filter_list.DeleteAllItems()
|
||||||
|
self.on_filter_deselected(None)
|
||||||
|
for i, filter_obj in enumerate(filters):
|
||||||
|
index = self.dialog.filter_list.InsertItem(i, filter_obj.title)
|
||||||
|
keyword_count = len(filter_obj.keywords)
|
||||||
|
self.dialog.filter_list.SetItem(index, 1, str(keyword_count))
|
||||||
|
contexts = ", ".join(filter_obj.context)
|
||||||
|
self.dialog.filter_list.SetItem(index, 2, contexts)
|
||||||
|
self.dialog.filter_list.SetItem(index, 3, filter_obj.filter_action)
|
||||||
|
if filter_obj.expires_at:
|
||||||
|
expiry_str = filter_obj.expires_at.strftime("%Y-%m-%d %H:%M")
|
||||||
|
else:
|
||||||
|
expiry_str = _("Never")
|
||||||
|
self.dialog.filter_list.SetItem(index, 4, expiry_str)
|
||||||
|
self.dialog.filter_list.SetItemData(index, int(filter_obj.id) if isinstance(filter_obj.id, (int, str)) else 0)
|
||||||
|
except MastodonError as e:
|
||||||
|
commonMessageDialogs.error_loading_filters()
|
||||||
|
self.error_loading = True
|
||||||
|
|
||||||
|
def on_add_filter(self, *args, **kwargs):
|
||||||
|
filterController = create_filter.CreateFilterController(self.session)
|
||||||
|
try:
|
||||||
|
filter = filterController.get_response()
|
||||||
|
self.load_filter_data()
|
||||||
|
except MastodonError as error:
|
||||||
|
commonMessageDialogs.error_adding_filter()
|
||||||
|
return self.on_add_filter()
|
||||||
|
|
||||||
|
def on_edit_filter(self, *args, **kwargs):
|
||||||
|
filter_id = self.get_selected_filter_id()
|
||||||
|
if filter_id == None:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
filter_data = self.session.api.filter_v2(filter_id)
|
||||||
|
filterController = create_filter.CreateFilterController(self.session, filter_data=filter_data)
|
||||||
|
filterController.get_response()
|
||||||
|
self.load_filter_data()
|
||||||
|
except MastodonError as error:
|
||||||
|
commonMessageDialogs.error_adding_filter()
|
||||||
|
|
||||||
|
def on_remove_filter(self, *args, **kwargs):
|
||||||
|
filter_id = self.get_selected_filter_id()
|
||||||
|
if filter_id == None:
|
||||||
|
return
|
||||||
|
dlg = commonMessageDialogs.remove_filter()
|
||||||
|
if dlg == widgetUtils.NO:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
self.session.api.delete_filter_v2(filter_id)
|
||||||
|
self.load_filter_data()
|
||||||
|
except MastodonError as error:
|
||||||
|
commonMessageDialogs.error_removing_filter()
|
||||||
|
|
||||||
|
def get_response(self):
|
||||||
|
return self.dialog.ShowModal() == wx.ID_OK
|
@@ -15,6 +15,7 @@ from wxUI.dialogs.mastodon import updateProfile as update_profile_dialogs
|
|||||||
from wxUI.dialogs.mastodon import showUserProfile, communityTimeline
|
from wxUI.dialogs.mastodon import showUserProfile, communityTimeline
|
||||||
from sessions.mastodon.utils import html_filter
|
from sessions.mastodon.utils import html_filter
|
||||||
from . import userActions, settings
|
from . import userActions, settings
|
||||||
|
from .filters import create_filter, manage_filters
|
||||||
|
|
||||||
log = logging.getLogger("controller.mastodon.handler")
|
log = logging.getLogger("controller.mastodon.handler")
|
||||||
|
|
||||||
@@ -50,9 +51,9 @@ class Handler(object):
|
|||||||
details=_("Show user profile"),
|
details=_("Show user profile"),
|
||||||
favs=None,
|
favs=None,
|
||||||
# In buffer Menu.
|
# In buffer Menu.
|
||||||
community_timeline =_("Create community timeline"),
|
community_timeline =_("Create c&ommunity timeline"),
|
||||||
filter=None,
|
filter=_("Create a &filter"),
|
||||||
manage_filters=None
|
manage_filters=_("&Manage filters")
|
||||||
)
|
)
|
||||||
# Name for the "tweet" menu in the menu bar.
|
# Name for the "tweet" menu in the menu bar.
|
||||||
self.item_menu = _("&Post")
|
self.item_menu = _("&Post")
|
||||||
@@ -110,8 +111,10 @@ class Handler(object):
|
|||||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Communities"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="communities", account=name))
|
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Communities"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="communities", account=name))
|
||||||
communities_position =controller.view.search("communities", name)
|
communities_position =controller.view.search("communities", name)
|
||||||
for community in session.settings["other_buffers"]["communities"]:
|
for community in session.settings["other_buffers"]["communities"]:
|
||||||
pub.sendMessage("createBuffer", buffer_type="CommunityBuffer", session_type=session.type, buffer_title=_("Community for {}").format(community.split("@")[1].replace("https://", "")), parent_tab=communities_position, start=True, kwargs=dict(parent=controller.view.nb, function="timeline", compose_func="compose_post", name=community, sessionObject=session, community_url=community.split("@")[1], account=session.get_name(), sound="search_updated.ogg", timeline=community.split("@")[0]))
|
bufftype = _("Local") if community.split("@")[0] == "local" else _("federated")
|
||||||
|
community_name = community.split("@")[1].replace("https://", "")
|
||||||
|
title = _(f"{bufftype} timeline for {community_name}")
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="CommunityBuffer", session_type=session.type, buffer_title=title, parent_tab=communities_position, start=True, kwargs=dict(parent=controller.view.nb, function="timeline", compose_func="compose_post", name=community, sessionObject=session, community_url=community.split("@")[1], account=session.get_name(), sound="search_updated.ogg", timeline=community.split("@")[0]))
|
||||||
# for i in session.settings["other_buffers"]["trending_topic_buffers"]:
|
# for i in session.settings["other_buffers"]["trending_topic_buffers"]:
|
||||||
# pub.sendMessage("createBuffer", buffer_type="TrendsBuffer", session_type=session.type, buffer_title=_("Trending topics for %s") % (i), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="%s_tt" % (i,), sessionObject=session, name, trendsFor=i, sound="trends_updated.ogg"))
|
# pub.sendMessage("createBuffer", buffer_type="TrendsBuffer", session_type=session.type, buffer_title=_("Trending topics for %s") % (i), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="%s_tt" % (i,), sessionObject=session, name, trendsFor=i, sound="trends_updated.ogg"))
|
||||||
|
|
||||||
@@ -128,7 +131,12 @@ class Handler(object):
|
|||||||
pub.sendMessage("buffer-title-changed", buffer=buffer)
|
pub.sendMessage("buffer-title-changed", buffer=buffer)
|
||||||
|
|
||||||
def open_conversation(self, controller, buffer):
|
def open_conversation(self, controller, buffer):
|
||||||
post = buffer.get_item()
|
# detect if we are in a community buffer.
|
||||||
|
# Community buffers are special because we'll need to retrieve the object locally at first.
|
||||||
|
if hasattr(buffer, "community_url"):
|
||||||
|
post = buffer.get_item_from_instance()
|
||||||
|
else:
|
||||||
|
post = buffer.get_item()
|
||||||
if post.reblog != None:
|
if post.reblog != None:
|
||||||
post = post.reblog
|
post = post.reblog
|
||||||
conversations_position =controller.view.search("direct_messages", buffer.session.get_name())
|
conversations_position =controller.view.search("direct_messages", buffer.session.get_name())
|
||||||
@@ -137,7 +145,11 @@ class Handler(object):
|
|||||||
def follow(self, buffer):
|
def follow(self, buffer):
|
||||||
if not hasattr(buffer, "get_item"):
|
if not hasattr(buffer, "get_item"):
|
||||||
return
|
return
|
||||||
item = buffer.get_item()
|
# Community buffers are special because we'll need to retrieve the object locally at first.
|
||||||
|
if hasattr(buffer, "community_url"):
|
||||||
|
item = buffer.get_item_from_instance()
|
||||||
|
else:
|
||||||
|
item = buffer.get_item()
|
||||||
if buffer.type == "user":
|
if buffer.type == "user":
|
||||||
users = [item.acct]
|
users = [item.acct]
|
||||||
elif buffer.type == "baseBuffer":
|
elif buffer.type == "baseBuffer":
|
||||||
@@ -189,7 +201,10 @@ class Handler(object):
|
|||||||
def open_timeline(self, controller, buffer):
|
def open_timeline(self, controller, buffer):
|
||||||
if not hasattr(buffer, "get_item"):
|
if not hasattr(buffer, "get_item"):
|
||||||
return
|
return
|
||||||
item = buffer.get_item()
|
if hasattr(buffer, "community_url"):
|
||||||
|
item = buffer.get_item_from_instance()
|
||||||
|
else:
|
||||||
|
item = buffer.get_item()
|
||||||
if buffer.type == "user":
|
if buffer.type == "user":
|
||||||
users = [item.acct]
|
users = [item.acct]
|
||||||
elif buffer.type == "baseBuffer":
|
elif buffer.type == "baseBuffer":
|
||||||
@@ -392,3 +407,16 @@ class Handler(object):
|
|||||||
buffer.session.settings.write()
|
buffer.session.settings.write()
|
||||||
communities_position =controller.view.search("communities", buffer.session.get_name())
|
communities_position =controller.view.search("communities", buffer.session.get_name())
|
||||||
pub.sendMessage("createBuffer", buffer_type="CommunityBuffer", session_type=buffer.session.type, buffer_title=title, parent_tab=communities_position, start=True, kwargs=dict(parent=controller.view.nb, function="timeline", name=tl_info, sessionObject=buffer.session, account=buffer.session.get_name(), sound="tweet_timeline.ogg", community_url=url, timeline=bufftype))
|
pub.sendMessage("createBuffer", buffer_type="CommunityBuffer", session_type=buffer.session.type, buffer_title=title, parent_tab=communities_position, start=True, kwargs=dict(parent=controller.view.nb, function="timeline", name=tl_info, sessionObject=buffer.session, account=buffer.session.get_name(), sound="tweet_timeline.ogg", community_url=url, timeline=bufftype))
|
||||||
|
|
||||||
|
def create_filter(self, controller, buffer):
|
||||||
|
filterController = create_filter.CreateFilterController(buffer.session)
|
||||||
|
try:
|
||||||
|
filter = filterController.get_response()
|
||||||
|
except MastodonError as error:
|
||||||
|
log.exception("Error adding filter.")
|
||||||
|
commonMessageDialogs.error_adding_filter()
|
||||||
|
return self.create_filter(controller=controller, buffer=buffer)
|
||||||
|
|
||||||
|
def manage_filters(self, controller, buffer):
|
||||||
|
manageFiltersController = manage_filters.ManageFiltersController(buffer.session)
|
||||||
|
manageFiltersController.get_response()
|
@@ -5,6 +5,7 @@ import wx
|
|||||||
import widgetUtils
|
import widgetUtils
|
||||||
import config
|
import config
|
||||||
import output
|
import output
|
||||||
|
import languageHandler
|
||||||
from twitter_text import parse_tweet, config
|
from twitter_text import parse_tweet, config
|
||||||
from mastodon import MastodonError
|
from mastodon import MastodonError
|
||||||
from controller import messages
|
from controller import messages
|
||||||
@@ -32,9 +33,12 @@ class post(messages.basicMessage):
|
|||||||
self.max = session.char_limit
|
self.max = session.char_limit
|
||||||
self.title = title
|
self.title = title
|
||||||
self.session = session
|
self.session = session
|
||||||
self.message = postDialogs.Post(caption=caption, text=text, *args, **kwargs)
|
langs = self.session.supported_languages
|
||||||
|
display_langs = [l.name for l in langs]
|
||||||
|
self.message = postDialogs.Post(caption=caption, text=text, languages=display_langs, *args, **kwargs)
|
||||||
self.message.SetTitle(title)
|
self.message.SetTitle(title)
|
||||||
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
|
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
|
||||||
|
self.set_language(self.session.default_language)
|
||||||
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
|
widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck)
|
||||||
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
|
widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor)
|
||||||
widgetUtils.connect_event(self.message.spoiler, widgetUtils.ENTERED_TEXT, self.text_processor)
|
widgetUtils.connect_event(self.message.spoiler, widgetUtils.ENTERED_TEXT, self.text_processor)
|
||||||
@@ -70,7 +74,21 @@ class post(messages.basicMessage):
|
|||||||
self.add_post(event=None, update_gui=False)
|
self.add_post(event=None, update_gui=False)
|
||||||
return self.thread
|
return self.thread
|
||||||
|
|
||||||
def set_post_data(self, visibility, data):
|
def set_language(self, language=None):
|
||||||
|
""" Attempt to set the default language for a post. """
|
||||||
|
# language can be provided in a post (replying or recovering from errors).
|
||||||
|
# Also it can be provided in user preferences (retrieved in the session).
|
||||||
|
# If no language is provided, let's fallback to TWBlue's user language.
|
||||||
|
if language != None:
|
||||||
|
language_code = language
|
||||||
|
else:
|
||||||
|
# Let's cut langcode_VARIANT to ISO-639 two letter code only.
|
||||||
|
language_code = languageHandler.curLang[:2]
|
||||||
|
for lang in self.session.supported_languages:
|
||||||
|
if lang.code == language_code:
|
||||||
|
self.message.language.SetStringSelection(lang.name)
|
||||||
|
|
||||||
|
def set_post_data(self, visibility, data, language):
|
||||||
if len(data) == 0:
|
if len(data) == 0:
|
||||||
return
|
return
|
||||||
if len(data) > 1:
|
if len(data) > 1:
|
||||||
@@ -87,6 +105,7 @@ class post(messages.basicMessage):
|
|||||||
self.message.on_sensitivity_changed()
|
self.message.on_sensitivity_changed()
|
||||||
for attachment in self.attachments:
|
for attachment in self.attachments:
|
||||||
self.message.add_item(item=[attachment["file"], attachment["type"], attachment["description"]])
|
self.message.add_item(item=[attachment["file"], attachment["type"], attachment["description"]])
|
||||||
|
self.set_language(language)
|
||||||
self.text_processor()
|
self.text_processor()
|
||||||
|
|
||||||
def text_processor(self, *args, **kwargs):
|
def text_processor(self, *args, **kwargs):
|
||||||
@@ -231,6 +250,14 @@ class post(messages.basicMessage):
|
|||||||
visibility_settings = ["public", "unlisted", "private", "direct"]
|
visibility_settings = ["public", "unlisted", "private", "direct"]
|
||||||
return visibility_settings[self.message.visibility.GetSelection()]
|
return visibility_settings[self.message.visibility.GetSelection()]
|
||||||
|
|
||||||
|
def get_language(self):
|
||||||
|
langs = self.session.supported_languages
|
||||||
|
lang = self.message.language.GetSelection()
|
||||||
|
if lang >= 0:
|
||||||
|
print(langs[lang].code)
|
||||||
|
return langs[lang].code
|
||||||
|
return None
|
||||||
|
|
||||||
def set_visibility(self, setting):
|
def set_visibility(self, setting):
|
||||||
visibility_settings = ["public", "unlisted", "private", "direct"]
|
visibility_settings = ["public", "unlisted", "private", "direct"]
|
||||||
visibility_setting = visibility_settings.index(setting)
|
visibility_setting = visibility_settings.index(setting)
|
||||||
@@ -259,7 +286,6 @@ class viewPost(post):
|
|||||||
source = source_obj.get("name")
|
source = source_obj.get("name")
|
||||||
self.message = postDialogs.viewPost(text=text, boosts_count=boost_count, favs_count=favs_count, source=source, date=date, privacy=privacy)
|
self.message = postDialogs.viewPost(text=text, boosts_count=boost_count, favs_count=favs_count, source=source, date=date, privacy=privacy)
|
||||||
participants = [post.account.id] + [account.id for account in post.mentions]
|
participants = [post.account.id] + [account.id for account in post.mentions]
|
||||||
print(post, participants)
|
|
||||||
if self.session.db["user_id"] in participants:
|
if self.session.db["user_id"] in participants:
|
||||||
self.message.mute.Enable(True)
|
self.message.mute.Enable(True)
|
||||||
if post.muted:
|
if post.muted:
|
||||||
|
@@ -6,7 +6,6 @@ import config
|
|||||||
import languageHandler
|
import languageHandler
|
||||||
import application
|
import application
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from mysc import autostart as autostart_windows
|
|
||||||
from wxUI.dialogs import configuration
|
from wxUI.dialogs import configuration
|
||||||
from wxUI import commonMessageDialogs
|
from wxUI import commonMessageDialogs
|
||||||
|
|
||||||
@@ -48,21 +47,16 @@ class globalSettingsController(object):
|
|||||||
self.dialog.create_general(langs,self.kmfriendlies)
|
self.dialog.create_general(langs,self.kmfriendlies)
|
||||||
self.dialog.general.language.SetSelection(id)
|
self.dialog.general.language.SetSelection(id)
|
||||||
self.dialog.general.km.SetSelection(self.kmid)
|
self.dialog.general.km.SetSelection(self.kmid)
|
||||||
if paths.mode == "installed":
|
|
||||||
self.dialog.set_value("general", "autostart", config.app["app-settings"]["autostart"])
|
|
||||||
else:
|
|
||||||
self.dialog.general.autostart.Enable(False)
|
|
||||||
self.dialog.set_value("general", "ask_at_exit", config.app["app-settings"]["ask_at_exit"])
|
self.dialog.set_value("general", "ask_at_exit", config.app["app-settings"]["ask_at_exit"])
|
||||||
self.dialog.set_value("general", "no_streaming", config.app["app-settings"]["no_streaming"])
|
self.dialog.set_value("general", "no_streaming", config.app["app-settings"]["no_streaming"])
|
||||||
self.dialog.set_value("general", "play_ready_sound", config.app["app-settings"]["play_ready_sound"])
|
self.dialog.set_value("general", "play_ready_sound", config.app["app-settings"]["play_ready_sound"])
|
||||||
self.dialog.set_value("general", "speak_ready_msg", config.app["app-settings"]["speak_ready_msg"])
|
self.dialog.set_value("general", "speak_ready_msg", config.app["app-settings"]["speak_ready_msg"])
|
||||||
self.dialog.set_value("general", "handle_longtweets", config.app["app-settings"]["handle_longtweets"])
|
self.dialog.set_value("general", "read_long_posts_in_gui", config.app["app-settings"]["read_long_posts_in_gui"])
|
||||||
self.dialog.set_value("general", "use_invisible_shorcuts", config.app["app-settings"]["use_invisible_keyboard_shorcuts"])
|
self.dialog.set_value("general", "use_invisible_shorcuts", config.app["app-settings"]["use_invisible_keyboard_shorcuts"])
|
||||||
self.dialog.set_value("general", "disable_sapi5", config.app["app-settings"]["voice_enabled"])
|
self.dialog.set_value("general", "disable_sapi5", config.app["app-settings"]["voice_enabled"])
|
||||||
self.dialog.set_value("general", "hide_gui", config.app["app-settings"]["hide_gui"])
|
self.dialog.set_value("general", "hide_gui", config.app["app-settings"]["hide_gui"])
|
||||||
self.dialog.set_value("general", "update_period", config.app["app-settings"]["update_period"])
|
self.dialog.set_value("general", "update_period", config.app["app-settings"]["update_period"])
|
||||||
self.dialog.set_value("general", "check_for_updates", config.app["app-settings"]["check_for_updates"])
|
self.dialog.set_value("general", "check_for_updates", config.app["app-settings"]["check_for_updates"])
|
||||||
self.dialog.set_value("general", "remember_mention_and_longtweet", config.app["app-settings"]["remember_mention_and_longtweet"])
|
|
||||||
proxyTypes = [_("System default"), _("HTTP"), _("SOCKS v4"), _("SOCKS v4 with DNS support"), _("SOCKS v5"), _("SOCKS v5 with DNS support")]
|
proxyTypes = [_("System default"), _("HTTP"), _("SOCKS v4"), _("SOCKS v4 with DNS support"), _("SOCKS v5"), _("SOCKS v5 with DNS support")]
|
||||||
self.dialog.create_proxy(proxyTypes)
|
self.dialog.create_proxy(proxyTypes)
|
||||||
try:
|
try:
|
||||||
@@ -92,9 +86,6 @@ class globalSettingsController(object):
|
|||||||
kmFile.close()
|
kmFile.close()
|
||||||
log.debug("Triggered app restart due to a keymap change.")
|
log.debug("Triggered app restart due to a keymap change.")
|
||||||
self.needs_restart = True
|
self.needs_restart = True
|
||||||
if config.app["app-settings"]["autostart"] != self.dialog.get_value("general", "autostart") and paths.mode == "installed":
|
|
||||||
config.app["app-settings"]["autostart"] = self.dialog.get_value("general", "autostart")
|
|
||||||
autostart_windows.setAutoStart(application.name, enable=self.dialog.get_value("general", "autostart"))
|
|
||||||
if config.app["app-settings"]["use_invisible_keyboard_shorcuts"] != self.dialog.get_value("general", "use_invisible_shorcuts"):
|
if config.app["app-settings"]["use_invisible_keyboard_shorcuts"] != self.dialog.get_value("general", "use_invisible_shorcuts"):
|
||||||
config.app["app-settings"]["use_invisible_keyboard_shorcuts"] = self.dialog.get_value("general", "use_invisible_shorcuts")
|
config.app["app-settings"]["use_invisible_keyboard_shorcuts"] = self.dialog.get_value("general", "use_invisible_shorcuts")
|
||||||
pub.sendMessage("invisible-shorcuts-changed", registered=self.dialog.get_value("general", "use_invisible_shorcuts"))
|
pub.sendMessage("invisible-shorcuts-changed", registered=self.dialog.get_value("general", "use_invisible_shorcuts"))
|
||||||
@@ -109,11 +100,10 @@ class globalSettingsController(object):
|
|||||||
config.app["app-settings"]["voice_enabled"] = self.dialog.get_value("general", "disable_sapi5")
|
config.app["app-settings"]["voice_enabled"] = self.dialog.get_value("general", "disable_sapi5")
|
||||||
config.app["app-settings"]["hide_gui"] = self.dialog.get_value("general", "hide_gui")
|
config.app["app-settings"]["hide_gui"] = self.dialog.get_value("general", "hide_gui")
|
||||||
config.app["app-settings"]["ask_at_exit"] = self.dialog.get_value("general", "ask_at_exit")
|
config.app["app-settings"]["ask_at_exit"] = self.dialog.get_value("general", "ask_at_exit")
|
||||||
config.app["app-settings"]["handle_longtweets"] = self.dialog.get_value("general", "handle_longtweets")
|
config.app["app-settings"]["read_long_posts_in_gui"] = self.dialog.get_value("general", "read_long_posts_in_gui")
|
||||||
config.app["app-settings"]["play_ready_sound"] = self.dialog.get_value("general", "play_ready_sound")
|
config.app["app-settings"]["play_ready_sound"] = self.dialog.get_value("general", "play_ready_sound")
|
||||||
config.app["app-settings"]["speak_ready_msg"] = self.dialog.get_value("general", "speak_ready_msg")
|
config.app["app-settings"]["speak_ready_msg"] = self.dialog.get_value("general", "speak_ready_msg")
|
||||||
config.app["app-settings"]["check_for_updates"] = self.dialog.get_value("general", "check_for_updates")
|
config.app["app-settings"]["check_for_updates"] = self.dialog.get_value("general", "check_for_updates")
|
||||||
config.app["app-settings"]["remember_mention_and_longtweet"] = self.dialog.get_value("general", "remember_mention_and_longtweet")
|
|
||||||
if config.app["proxy"]["type"]!=self.dialog.get_value("proxy", "type") or config.app["proxy"]["server"] != self.dialog.get_value("proxy", "server") or config.app["proxy"]["port"] != self.dialog.get_value("proxy", "port") or config.app["proxy"]["user"] != self.dialog.get_value("proxy", "user") or config.app["proxy"]["password"] != self.dialog.get_value("proxy", "password"):
|
if config.app["proxy"]["type"]!=self.dialog.get_value("proxy", "type") or config.app["proxy"]["server"] != self.dialog.get_value("proxy", "server") or config.app["proxy"]["port"] != self.dialog.get_value("proxy", "port") or config.app["proxy"]["user"] != self.dialog.get_value("proxy", "user") or config.app["proxy"]["password"] != self.dialog.get_value("proxy", "password"):
|
||||||
if self.is_started == True:
|
if self.is_started == True:
|
||||||
self.needs_restart = True
|
self.needs_restart = True
|
||||||
|
@@ -8,8 +8,8 @@ class autocompletionScanDialog(widgetUtils.BaseDialog):
|
|||||||
super(autocompletionScanDialog, self).__init__(parent=None, id=-1, title=_(u"Autocomplete users' settings"))
|
super(autocompletionScanDialog, self).__init__(parent=None, id=-1, title=_(u"Autocomplete users' settings"))
|
||||||
panel = wx.Panel(self)
|
panel = wx.Panel(self)
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
self.followers = wx.CheckBox(panel, -1, _("Add followers to database"))
|
self.followers = wx.CheckBox(panel, -1, _("Add &followers to database"))
|
||||||
self.friends = wx.CheckBox(panel, -1, _("Add following to database"))
|
self.friends = wx.CheckBox(panel, -1, _("Add f&ollowing to database"))
|
||||||
sizer.Add(self.followers, 0, wx.ALL, 5)
|
sizer.Add(self.followers, 0, wx.ALL, 5)
|
||||||
sizer.Add(self.friends, 0, wx.ALL, 5)
|
sizer.Add(self.friends, 0, wx.ALL, 5)
|
||||||
ok = wx.Button(panel, wx.ID_OK)
|
ok = wx.Button(panel, wx.ID_OK)
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
3226
src/locales/bg/LC_MESSAGES/twblue.po
Normal file
3226
src/locales/bg/LC_MESSAGES/twblue.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
from future import standard_library
|
|
||||||
standard_library.install_aliases()
|
|
||||||
from builtins import str
|
|
||||||
import winreg
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from platform_utils import paths
|
|
||||||
|
|
||||||
RUN_REGKEY = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
|
|
||||||
|
|
||||||
def is_installed(app_subkey):
|
|
||||||
"""Checks if the currently running copy is installed or portable variant. Requires the name of the application subkey found under the uninstall section in Windows registry."""
|
|
||||||
|
|
||||||
try:
|
|
||||||
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%s" % app_subkey)
|
|
||||||
inst_dir = winreg.QueryValueEx(key,"InstallLocation")[0]
|
|
||||||
except WindowsError:
|
|
||||||
return False
|
|
||||||
winreg.CloseKey(key)
|
|
||||||
try:
|
|
||||||
return os.stat(inst_dir) == os.stat(paths.app_path())
|
|
||||||
except WindowsError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def getAutoStart(app_name):
|
|
||||||
"""Queries if the automatic startup should be set for the application or not, depending on it's current state."""
|
|
||||||
|
|
||||||
try:
|
|
||||||
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_REGKEY)
|
|
||||||
val = winreg.QueryValueEx(key, str(app_name))[0]
|
|
||||||
return os.stat(val) == os.stat(sys.argv[0])
|
|
||||||
except (WindowsError, OSError):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def setAutoStart(app_name, enable=True):
|
|
||||||
"""Configures automatic startup for the application, if the enable argument is set to True. If set to False, deletes the application AutoStart value."""
|
|
||||||
if getAutoStart(app_name) == enable:
|
|
||||||
return
|
|
||||||
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_REGKEY, 0, winreg.KEY_WRITE)
|
|
||||||
if enable:
|
|
||||||
winreg.SetValueEx(key, str(app_name), None, winreg.REG_SZ, paths.get_executable())
|
|
||||||
else:
|
|
||||||
winreg.DeleteValue(key, str(app_name))
|
|
@@ -3,9 +3,11 @@ from sessions.mastodon import session
|
|||||||
class Session(session.Session):
|
class Session(session.Session):
|
||||||
# disable version check so Mastodon.py won't throw exceptions.
|
# disable version check so Mastodon.py won't throw exceptions.
|
||||||
version_check_mode = "none"
|
version_check_mode = "none"
|
||||||
|
name = "GoToSocial"
|
||||||
|
|
||||||
def get_lists(self):
|
def get_lists(self):
|
||||||
""" Gets the lists that the user is subscribed to and stores them in the database. Returns None."""
|
""" Gets the lists that the user is subscribed to and stores them in the database. Returns None."""
|
||||||
self.db["lists"] = []
|
self.db["lists"] = []
|
||||||
|
|
||||||
def get_muted_users(self):
|
def get_muted_users(self):
|
||||||
self.db["muted_users"] = []
|
self.db["muted_users"] = []
|
||||||
|
@@ -17,6 +17,9 @@ def compose_post(post, db, settings, relative_times, show_screen_names, safe=Tru
|
|||||||
text = _("Boosted from @{}: {}").format(post.reblog.account.acct, templates.process_text(post.reblog, safe=safe))
|
text = _("Boosted from @{}: {}").format(post.reblog.account.acct, templates.process_text(post.reblog, safe=safe))
|
||||||
else:
|
else:
|
||||||
text = templates.process_text(post, safe=safe)
|
text = templates.process_text(post, safe=safe)
|
||||||
|
filtered = utils.evaluate_filters(post=post, current_context="home")
|
||||||
|
if filtered != None:
|
||||||
|
text = _("hidden by filter {}").format(filtered)
|
||||||
source = post.get("application", "")
|
source = post.get("application", "")
|
||||||
# "" means remote user, None for legacy apps so we should cover both sides.
|
# "" means remote user, None for legacy apps so we should cover both sides.
|
||||||
if source != None and source != "":
|
if source != None and source != "":
|
||||||
@@ -73,4 +76,7 @@ def compose_notification(notification, db, settings, relative_times, show_screen
|
|||||||
text = _("A poll in which you have voted has expired: {status}").format(status=",".join(compose_post(notification.status, db, settings, relative_times, show_screen_names, safe=safe)))
|
text = _("A poll in which you have voted has expired: {status}").format(status=",".join(compose_post(notification.status, db, settings, relative_times, show_screen_names, safe=safe)))
|
||||||
elif notification.type == "follow_request":
|
elif notification.type == "follow_request":
|
||||||
text = _("{username} wants to follow you.").format(username=user)
|
text = _("{username} wants to follow you.").format(username=user)
|
||||||
|
filtered = utils.evaluate_filters(post=notification, current_context="notifications")
|
||||||
|
if filtered != None:
|
||||||
|
text = _("hidden by filter {}").format(filtered)
|
||||||
return [user, text, ts]
|
return [user, text, ts]
|
@@ -11,7 +11,7 @@ import config
|
|||||||
import config_utils
|
import config_utils
|
||||||
import output
|
import output
|
||||||
import application
|
import application
|
||||||
from mastodon import MastodonError, MastodonAPIError, MastodonNotFoundError, MastodonUnauthorizedError
|
from mastodon import MastodonError, MastodonAPIError, MastodonNotFoundError, MastodonUnauthorizedError, MastodonVersionError
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
from sessions import base
|
from sessions import base
|
||||||
@@ -19,15 +19,17 @@ from sessions.mastodon import utils, streaming
|
|||||||
|
|
||||||
log = logging.getLogger("sessions.mastodonSession")
|
log = logging.getLogger("sessions.mastodonSession")
|
||||||
|
|
||||||
MASTODON_VERSION = "4.0.1"
|
MASTODON_VERSION = "4.3.2"
|
||||||
|
|
||||||
class Session(base.baseSession):
|
class Session(base.baseSession):
|
||||||
version_check_mode = "created"
|
version_check_mode = "created"
|
||||||
|
name = "Mastodon"
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Session, self).__init__(*args, **kwargs)
|
super(Session, self).__init__(*args, **kwargs)
|
||||||
self.config_spec = "mastodon.defaults"
|
self.config_spec = "mastodon.defaults"
|
||||||
self.supported_languages = []
|
self.supported_languages = []
|
||||||
|
self.default_language = None
|
||||||
self.type = "mastodon"
|
self.type = "mastodon"
|
||||||
self.db["pagination_info"] = dict()
|
self.db["pagination_info"] = dict()
|
||||||
self.char_limit = 500
|
self.char_limit = 500
|
||||||
@@ -47,12 +49,15 @@ class Session(base.baseSession):
|
|||||||
credentials = self.api.account_verify_credentials()
|
credentials = self.api.account_verify_credentials()
|
||||||
self.db["user_name"] = credentials["username"]
|
self.db["user_name"] = credentials["username"]
|
||||||
self.db["user_id"] = credentials["id"]
|
self.db["user_id"] = credentials["id"]
|
||||||
|
if hasattr(credentials, "source") and hasattr(credentials.source, "language"):
|
||||||
|
log.info(f"Setting default language on account {credentials.username} to {credentials.source.language}")
|
||||||
|
self.default_language = credentials.source.language
|
||||||
self.settings["mastodon"]["user_name"] = credentials["username"]
|
self.settings["mastodon"]["user_name"] = credentials["username"]
|
||||||
self.logged = True
|
self.logged = True
|
||||||
log.debug("Logged.")
|
log.debug("Logged.")
|
||||||
self.counter = 0
|
self.counter = 0
|
||||||
except MastodonError:
|
except MastodonError:
|
||||||
log.exception("The login attempt failed.")
|
log.exception(f"The login attempt failed on instance {self.settings['mastodon']['instance']}.")
|
||||||
self.logged = False
|
self.logged = False
|
||||||
else:
|
else:
|
||||||
self.logged = False
|
self.logged = False
|
||||||
@@ -112,7 +117,10 @@ class Session(base.baseSession):
|
|||||||
self.db["utc_offset"] = offset
|
self.db["utc_offset"] = offset
|
||||||
instance = self.api.instance()
|
instance = self.api.instance()
|
||||||
if len(self.supported_languages) == 0:
|
if len(self.supported_languages) == 0:
|
||||||
self.supported_languages = instance.languages
|
try:
|
||||||
|
self.supported_languages = self.api.instance_languages()
|
||||||
|
except (Exception, MastodonVersionError):
|
||||||
|
pass
|
||||||
self.get_lists()
|
self.get_lists()
|
||||||
self.get_muted_users()
|
self.get_muted_users()
|
||||||
# determine instance custom characters limit.
|
# determine instance custom characters limit.
|
||||||
@@ -162,6 +170,9 @@ class Session(base.baseSession):
|
|||||||
log.error("Ignoring an older tweet... Last id: {0}, tweet id: {1}".format(last_id, i.id))
|
log.error("Ignoring an older tweet... Last id: {0}, tweet id: {1}".format(last_id, i.id))
|
||||||
continue
|
continue
|
||||||
if utils.find_item(i, self.db[name]) == None:
|
if utils.find_item(i, self.db[name]) == None:
|
||||||
|
filter_status = utils.evaluate_filters(post=i, current_context=utils.get_current_context(name))
|
||||||
|
if filter_status == "hide":
|
||||||
|
continue
|
||||||
if self.settings["general"]["reverse_timelines"] == False: objects.append(i)
|
if self.settings["general"]["reverse_timelines"] == False: objects.append(i)
|
||||||
else: objects.insert(0, i)
|
else: objects.insert(0, i)
|
||||||
num = num+1
|
num = num+1
|
||||||
@@ -206,17 +217,17 @@ class Session(base.baseSession):
|
|||||||
self.sound.play(_sound)
|
self.sound.play(_sound)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def send_post(self, reply_to=None, visibility=None, posts=[]):
|
def send_post(self, reply_to=None, visibility=None, language=None, posts=[]):
|
||||||
""" Convenience function to send a thread. """
|
""" Convenience function to send a thread. """
|
||||||
in_reply_to_id = reply_to
|
in_reply_to_id = reply_to
|
||||||
for obj in posts:
|
for obj in posts:
|
||||||
text = obj.get("text")
|
text = obj.get("text")
|
||||||
if len(obj["attachments"]) == 0:
|
if len(obj["attachments"]) == 0:
|
||||||
try:
|
try:
|
||||||
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, visibility=visibility, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"])
|
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, visibility=visibility, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language)
|
||||||
# If it fails, let's basically send an event with all passed info so we will catch it later.
|
# If it fails, let's basically send an event with all passed info so we will catch it later.
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts)
|
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, lang=language)
|
||||||
return
|
return
|
||||||
if item != None:
|
if item != None:
|
||||||
in_reply_to_id = item["id"]
|
in_reply_to_id = item["id"]
|
||||||
@@ -230,18 +241,18 @@ class Session(base.baseSession):
|
|||||||
for i in obj["attachments"]:
|
for i in obj["attachments"]:
|
||||||
media = self.api_call("media_post", media_file=i["file"], description=i["description"], synchronous=True)
|
media = self.api_call("media_post", media_file=i["file"], description=i["description"], synchronous=True)
|
||||||
media_ids.append(media.id)
|
media_ids.append(media.id)
|
||||||
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, media_ids=media_ids, visibility=visibility, poll=poll, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"])
|
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, media_ids=media_ids, visibility=visibility, poll=poll, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language)
|
||||||
if item != None:
|
if item != None:
|
||||||
in_reply_to_id = item["id"]
|
in_reply_to_id = item["id"]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts)
|
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, lang=language)
|
||||||
return
|
return
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
instance = self.settings["mastodon"]["instance"]
|
instance = self.settings["mastodon"]["instance"]
|
||||||
instance = instance.replace("https://", "")
|
instance = instance.replace("https://", "")
|
||||||
user = self.settings["mastodon"]["user_name"]
|
user = self.settings["mastodon"]["user_name"]
|
||||||
return "Mastodon: {}@{}".format(user, instance)
|
return "{}@{} ({})".format(user, instance, self.name)
|
||||||
|
|
||||||
def start_streaming(self):
|
def start_streaming(self):
|
||||||
if self.settings["general"]["disable_streaming"]:
|
if self.settings["general"]["disable_streaming"]:
|
||||||
|
@@ -9,7 +9,7 @@ from . import utils, compose
|
|||||||
# This will be used for the edit template dialog.
|
# This will be used for the edit template dialog.
|
||||||
# Available variables for post objects.
|
# Available variables for post objects.
|
||||||
# safe_text will be the content warning in case a post contains one, text will always be the full text, no matter if has a content warning or not.
|
# safe_text will be the content warning in case a post contains one, text will always be the full text, no matter if has a content warning or not.
|
||||||
post_variables = ["date", "display_name", "screen_name", "source", "lang", "safe_text", "text", "image_descriptions", "visibility"]
|
post_variables = ["date", "display_name", "screen_name", "source", "lang", "safe_text", "text", "image_descriptions", "visibility", "pinned"]
|
||||||
person_variables = ["display_name", "screen_name", "description", "followers", "following", "favorites", "posts", "created_at"]
|
person_variables = ["display_name", "screen_name", "description", "followers", "following", "favorites", "posts", "created_at"]
|
||||||
conversation_variables = ["users", "last_post"]
|
conversation_variables = ["users", "last_post"]
|
||||||
notification_variables = ["display_name", "screen_name", "text", "date"]
|
notification_variables = ["display_name", "screen_name", "text", "date"]
|
||||||
@@ -57,6 +57,7 @@ def render_post(post, template, settings, relative_times=False, offset_hours=0):
|
|||||||
$text: Toot text. This always displays the full text, even if there is a content warning present.
|
$text: Toot text. This always displays the full text, even if there is a content warning present.
|
||||||
$image_descriptions: Information regarding image descriptions added by twitter users.
|
$image_descriptions: Information regarding image descriptions added by twitter users.
|
||||||
$visibility: post's visibility: public, not listed, followers only or direct.
|
$visibility: post's visibility: public, not listed, followers only or direct.
|
||||||
|
$pinned: Wether the post is pinned or not (if not pinned, this will be blank).
|
||||||
"""
|
"""
|
||||||
global post_variables
|
global post_variables
|
||||||
available_data = dict(source="")
|
available_data = dict(source="")
|
||||||
@@ -75,6 +76,9 @@ def render_post(post, template, settings, relative_times=False, offset_hours=0):
|
|||||||
else:
|
else:
|
||||||
text = process_text(post, safe=False)
|
text = process_text(post, safe=False)
|
||||||
safe_text = process_text(post)
|
safe_text = process_text(post)
|
||||||
|
filtered = utils.evaluate_filters(post=post, current_context="home")
|
||||||
|
if filtered != None:
|
||||||
|
text = _("hidden by filter {}").format(filtered)
|
||||||
visibility_settings = dict(public=_("Public"), unlisted=_("Not listed"), private=_("Followers only"), direct=_("Direct"))
|
visibility_settings = dict(public=_("Public"), unlisted=_("Not listed"), private=_("Followers only"), direct=_("Direct"))
|
||||||
visibility = visibility_settings.get(post.visibility)
|
visibility = visibility_settings.get(post.visibility)
|
||||||
available_data.update(lang=post.language, text=text, safe_text=safe_text, visibility=visibility)
|
available_data.update(lang=post.language, text=text, safe_text=safe_text, visibility=visibility)
|
||||||
@@ -85,6 +89,12 @@ def render_post(post, template, settings, relative_times=False, offset_hours=0):
|
|||||||
else:
|
else:
|
||||||
image_descriptions = process_image_descriptions(post.media_attachments)
|
image_descriptions = process_image_descriptions(post.media_attachments)
|
||||||
available_data.update(image_descriptions=image_descriptions)
|
available_data.update(image_descriptions=image_descriptions)
|
||||||
|
# Process if the post is pinned
|
||||||
|
if post.get("pinned", False):
|
||||||
|
pinned = _("Pinned.")
|
||||||
|
else:
|
||||||
|
pinned = ""
|
||||||
|
available_data.update(pinned=pinned)
|
||||||
result = Template(_(template)).safe_substitute(**available_data)
|
result = Template(_(template)).safe_substitute(**available_data)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -161,6 +171,9 @@ def render_notification(notification, template, post_template, settings, relativ
|
|||||||
text = _("A poll in which you have voted has expired: {status}").format(status=render_post(notification.status, post_template, settings, relative_times, offset_hours))
|
text = _("A poll in which you have voted has expired: {status}").format(status=render_post(notification.status, post_template, settings, relative_times, offset_hours))
|
||||||
elif notification.type == "follow_request":
|
elif notification.type == "follow_request":
|
||||||
text = _("wants to follow you.")
|
text = _("wants to follow you.")
|
||||||
|
filtered = utils.evaluate_filters(post=notification, current_context="notifications")
|
||||||
|
if filtered != None:
|
||||||
|
text = _("hidden by filter {}").format(filtered)
|
||||||
available_data.update(text=text)
|
available_data.update(text=text)
|
||||||
result = Template(_(template)).safe_substitute(**available_data)
|
result = Template(_(template)).safe_substitute(**available_data)
|
||||||
result = result.replace(" . ", "")
|
result = result.replace(" . ", "")
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import re
|
import re
|
||||||
import demoji
|
import demoji
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
url_re = re.compile('<a\s*href=[\'|"](.*?)[\'"].*?>')
|
url_re = re.compile('<a\s*href=[\'|"](.*?)[\'"].*?>')
|
||||||
|
|
||||||
@@ -91,3 +92,59 @@ def demoji_user(name, settings):
|
|||||||
user = re.sub(r":(.*?):", "", user)
|
user = re.sub(r":(.*?):", "", user)
|
||||||
return user
|
return user
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
def get_current_context(buffer: str) -> str:
|
||||||
|
""" Gets the name of a buffer and returns the context it belongs to. useful for filtering. """
|
||||||
|
if buffer == "home_timeline":
|
||||||
|
return "home"
|
||||||
|
elif buffer == "mentions" or buffer == "notifications":
|
||||||
|
return "notifications"
|
||||||
|
|
||||||
|
def evaluate_filters(post: dict, current_context: str) -> str | None:
|
||||||
|
"""
|
||||||
|
Evaluates the 'filtered' attribute of a Mastodon post to determine its visibility,
|
||||||
|
considering the current context, expiration, and matches (keywords or status).
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
post (dict): A dictionary representing a Mastodon post.
|
||||||
|
current_context (str): The context in which the post is displayed
|
||||||
|
(e.g., "home", "notifications", "public", "thread", or "profile").
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- "hide" if any applicable filter indicates the post should be hidden.
|
||||||
|
- A string with the filter's title if an applicable "warn" filter is present.
|
||||||
|
- None if no applicable filters are found, meaning the post should be shown normally.
|
||||||
|
"""
|
||||||
|
filters = post.get("filtered", None)
|
||||||
|
if filters == None:
|
||||||
|
return
|
||||||
|
warn_filter_title = None
|
||||||
|
now = datetime.now(timezone.utc)
|
||||||
|
for result in filters:
|
||||||
|
filter_data = result.get("filter", {})
|
||||||
|
# Check if the filter applies to the current context.
|
||||||
|
filter_contexts = filter_data.get("context", [])
|
||||||
|
if current_context not in filter_contexts:
|
||||||
|
continue # Skip filters not applicable in this context
|
||||||
|
# Check if the filter has expired.
|
||||||
|
expires_at = filter_data.get("expires_at")
|
||||||
|
if expires_at is not None:
|
||||||
|
# If expires_at is a string, attempt to parse it.
|
||||||
|
if isinstance(expires_at, str):
|
||||||
|
try:
|
||||||
|
expiration_dt = datetime.fromisoformat(expires_at)
|
||||||
|
except ValueError:
|
||||||
|
continue # Skip if the date format is invalid
|
||||||
|
else:
|
||||||
|
expiration_dt = expires_at
|
||||||
|
if expiration_dt < now:
|
||||||
|
continue # Skip expired filters
|
||||||
|
action = filter_data.get("filter_action", "")
|
||||||
|
if action == "hide":
|
||||||
|
return "hide"
|
||||||
|
elif action == "warn":
|
||||||
|
title = filter_data.get("title", "")
|
||||||
|
warn_filter_title = title if title else "warn"
|
||||||
|
if warn_filter_title:
|
||||||
|
return warn_filter_title
|
||||||
|
return None
|
@@ -46,4 +46,16 @@ def cant_update_source() -> wx.MessageDialog:
|
|||||||
return dlg.ShowModal()
|
return dlg.ShowModal()
|
||||||
|
|
||||||
def invalid_instance():
|
def invalid_instance():
|
||||||
return wx.MessageDialog(None, _("the provided instance is invalid. Please try again."), _("Invalid instance"), wx.ICON_ERROR).ShowModal()
|
return wx.MessageDialog(None, _("the provided instance is invalid. Please try again."), _("Invalid instance"), wx.ICON_ERROR).ShowModal()
|
||||||
|
|
||||||
|
def error_adding_filter():
|
||||||
|
return wx.MessageDialog(None, _("TWBlue was unable to add or update the filter with the specified settings. Please try again."), _("Error"), wx.ICON_ERROR).ShowModal()
|
||||||
|
|
||||||
|
def error_loading_filters():
|
||||||
|
return wx.MessageDialog(None, _("TWBlue was unable to load your filters from the instance. Please try again."), _("Error"), wx.ICON_ERROR).ShowModal()
|
||||||
|
|
||||||
|
def remove_filter():
|
||||||
|
dlg = wx.MessageDialog(None, _("Do you really want to delete this filter ?"), _("Delete filter"), wx.ICON_QUESTION|wx.YES_NO)
|
||||||
|
return dlg.ShowModal()
|
||||||
|
def error_removing_filters():
|
||||||
|
return wx.MessageDialog(None, _("TWBlue was unable to remove the filter you specified. Please try again."), _("Error"), wx.ICON_ERROR).ShowModal()
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
from __future__ import unicode_literals
|
# -*- coding: utf-8 -*-
|
||||||
import wx
|
import wx
|
||||||
|
|
||||||
class BaseWXDialog(wx.Dialog):
|
class BaseWXDialog(wx.Dialog):
|
||||||
|
@@ -12,47 +12,43 @@ class general(wx.Panel, baseDialog.BaseWXDialog):
|
|||||||
def __init__(self, parent, languages,keymaps):
|
def __init__(self, parent, languages,keymaps):
|
||||||
super(general, self).__init__(parent)
|
super(general, self).__init__(parent)
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
language = wx.StaticText(self, -1, _(u"Language"))
|
language = wx.StaticText(self, -1, _(u"&Language"))
|
||||||
self.language = wx.ListBox(self, -1, choices=languages)
|
self.language = wx.ListBox(self, -1, choices=languages)
|
||||||
self.language.SetSize(self.language.GetBestSize())
|
self.language.SetSize(self.language.GetBestSize())
|
||||||
langBox = wx.BoxSizer(wx.HORIZONTAL)
|
langBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
langBox.Add(language, 0, wx.ALL, 5)
|
langBox.Add(language, 0, wx.ALL, 5)
|
||||||
langBox.Add(self.language, 0, wx.ALL, 5)
|
langBox.Add(self.language, 0, wx.ALL, 5)
|
||||||
sizer.Add(langBox, 0, wx.ALL, 5)
|
sizer.Add(langBox, 0, wx.ALL, 5)
|
||||||
self.autostart = wx.CheckBox(self, -1, _(u"Run {0} at Windows startup").format(application.name,))
|
self.ask_at_exit = wx.CheckBox(self, -1, _(U"&Ask before exiting {0}").format(application.name,))
|
||||||
self.ask_at_exit = wx.CheckBox(self, -1, _(U"ask before exiting {0}").format(application.name,))
|
|
||||||
sizer.Add(self.autostart, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(self.ask_at_exit, 0, wx.ALL, 5)
|
sizer.Add(self.ask_at_exit, 0, wx.ALL, 5)
|
||||||
self.no_streaming = wx.CheckBox(self, -1, _(U"Disable Streaming functions"))
|
self.no_streaming = wx.CheckBox(self, -1, _(U"&Disable Streaming functions"))
|
||||||
sizer.Add(self.no_streaming, 0, wx.ALL, 5)
|
sizer.Add(self.no_streaming, 0, wx.ALL, 5)
|
||||||
updatePeriodBox = wx.BoxSizer(wx.HORIZONTAL)
|
updatePeriodBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
updatePeriodBox.Add(wx.StaticText(self, -1, _(u"Buffer update interval, in minutes")), 0, wx.ALL, 5)
|
updatePeriodBox.Add(wx.StaticText(self, -1, _(u"&Buffer update interval, in minutes")), 0, wx.ALL, 5)
|
||||||
self.update_period = wx.SpinCtrl(self, wx.ID_ANY)
|
self.update_period = wx.SpinCtrl(self, wx.ID_ANY)
|
||||||
self.update_period.SetRange(1, 30)
|
self.update_period.SetRange(1, 30)
|
||||||
self.update_period.SetSize(self.update_period.GetBestSize())
|
self.update_period.SetSize(self.update_period.GetBestSize())
|
||||||
updatePeriodBox.Add(self.update_period, 0, wx.ALL, 5)
|
updatePeriodBox.Add(self.update_period, 0, wx.ALL, 5)
|
||||||
sizer.Add(updatePeriodBox, 0, wx.ALL, 5)
|
sizer.Add(updatePeriodBox, 0, wx.ALL, 5)
|
||||||
self.play_ready_sound = wx.CheckBox(self, -1, _(U"Play a sound when {0} launches").format(application.name,))
|
self.play_ready_sound = wx.CheckBox(self, -1, _(U"Pla&y a sound when {0} launches").format(application.name,))
|
||||||
sizer.Add(self.play_ready_sound, 0, wx.ALL, 5)
|
sizer.Add(self.play_ready_sound, 0, wx.ALL, 5)
|
||||||
self.speak_ready_msg = wx.CheckBox(self, -1, _(U"Speak a message when {0} launches").format(application.name,))
|
self.speak_ready_msg = wx.CheckBox(self, -1, _(U"Sp&eak a message when {0} launches").format(application.name,))
|
||||||
sizer.Add(self.speak_ready_msg, 0, wx.ALL, 5)
|
sizer.Add(self.speak_ready_msg, 0, wx.ALL, 5)
|
||||||
self.use_invisible_shorcuts = wx.CheckBox(self, -1, _(u"Use invisible interface's keyboard shortcuts while GUI is visible"))
|
self.use_invisible_shorcuts = wx.CheckBox(self, -1, _(u"&Use invisible interface's keyboard shortcuts while GUI is visible"))
|
||||||
sizer.Add(self.use_invisible_shorcuts, 0, wx.ALL, 5)
|
sizer.Add(self.use_invisible_shorcuts, 0, wx.ALL, 5)
|
||||||
self.disable_sapi5 = wx.CheckBox(self, -1, _(u"Activate Sapi5 when any other screen reader is not being run"))
|
self.disable_sapi5 = wx.CheckBox(self, -1, _(u"A&ctivate Sapi5 when any other screen reader is not being run"))
|
||||||
sizer.Add(self.disable_sapi5, 0, wx.ALL, 5)
|
sizer.Add(self.disable_sapi5, 0, wx.ALL, 5)
|
||||||
self.hide_gui = wx.CheckBox(self, -1, _(u"Hide GUI on launch"))
|
self.hide_gui = wx.CheckBox(self, -1, _(u"&Hide GUI on launch"))
|
||||||
sizer.Add(self.hide_gui, 0, wx.ALL, 5)
|
sizer.Add(self.hide_gui, 0, wx.ALL, 5)
|
||||||
self.handle_longtweets = wx.CheckBox(self, wx.ID_ANY, _(u"Use Codeofdusk's longtweet handlers (may decrease client performance)"))
|
self.read_long_posts_in_gui = wx.CheckBox(self, wx.ID_ANY, _("&Read long posts in GUI"))
|
||||||
sizer.Add(self.handle_longtweets, 0, wx.ALL, 5)
|
sizer.Add(self.read_long_posts_in_gui, 0, wx.ALL, 5)
|
||||||
self.remember_mention_and_longtweet = wx.CheckBox(self, -1, _(u"Remember state for mention all and long tweet"))
|
|
||||||
sizer.Add(self.remember_mention_and_longtweet, 0, wx.ALL, 5)
|
|
||||||
kmbox = wx.BoxSizer(wx.VERTICAL)
|
kmbox = wx.BoxSizer(wx.VERTICAL)
|
||||||
km_label = wx.StaticText(self, -1, _(u"Keymap"))
|
km_label = wx.StaticText(self, -1, _(u"&Keymap"))
|
||||||
self.km = wx.ComboBox(self, -1, choices=keymaps, style=wx.CB_READONLY)
|
self.km = wx.ComboBox(self, -1, choices=keymaps, style=wx.CB_READONLY)
|
||||||
self.km.SetSize(self.km.GetBestSize())
|
self.km.SetSize(self.km.GetBestSize())
|
||||||
kmbox.Add(km_label, 0, wx.ALL, 5)
|
kmbox.Add(km_label, 0, wx.ALL, 5)
|
||||||
kmbox.Add(self.km, 0, wx.ALL, 5)
|
kmbox.Add(self.km, 0, wx.ALL, 5)
|
||||||
self.check_for_updates = wx.CheckBox(self, -1, _(U"Check for updates when {0} launches").format(application.name,))
|
self.check_for_updates = wx.CheckBox(self, -1, _(U"Check for u&pdates when {0} launches").format(application.name,))
|
||||||
sizer.Add(self.check_for_updates, 0, wx.ALL, 5)
|
sizer.Add(self.check_for_updates, 0, wx.ALL, 5)
|
||||||
sizer.Add(kmbox, 0, wx.ALL, 5)
|
sizer.Add(kmbox, 0, wx.ALL, 5)
|
||||||
self.SetSizer(sizer)
|
self.SetSizer(sizer)
|
||||||
@@ -62,32 +58,32 @@ class proxy(wx.Panel, baseDialog.BaseWXDialog):
|
|||||||
def __init__(self, parent, proxyTypes):
|
def __init__(self, parent, proxyTypes):
|
||||||
super(proxy, self).__init__(parent)
|
super(proxy, self).__init__(parent)
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
type=wx.StaticText(self, wx.ID_ANY, _(u"Proxy type: "))
|
type=wx.StaticText(self, wx.ID_ANY, _(u"Proxy &type: "))
|
||||||
self.type=wx.ComboBox(self, -1, choices=proxyTypes, style=wx.CB_READONLY)
|
self.type=wx.ComboBox(self, -1, choices=proxyTypes, style=wx.CB_READONLY)
|
||||||
self.type.SetSize(self.type.GetBestSize())
|
self.type.SetSize(self.type.GetBestSize())
|
||||||
typeBox = wx.BoxSizer(wx.HORIZONTAL)
|
typeBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
typeBox.Add(type, 0, wx.ALL, 5)
|
typeBox.Add(type, 0, wx.ALL, 5)
|
||||||
typeBox.Add(self.type, 0, wx.ALL, 5)
|
typeBox.Add(self.type, 0, wx.ALL, 5)
|
||||||
sizer.Add(typeBox, 0, wx.ALL, 5)
|
sizer.Add(typeBox, 0, wx.ALL, 5)
|
||||||
lbl = wx.StaticText(self, wx.ID_ANY, _(u"Proxy server: "))
|
lbl = wx.StaticText(self, wx.ID_ANY, _(u"Proxy s&erver: "))
|
||||||
self.server = wx.TextCtrl(self, -1)
|
self.server = wx.TextCtrl(self, -1)
|
||||||
serverBox = wx.BoxSizer(wx.HORIZONTAL)
|
serverBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
serverBox.Add(lbl, 0, wx.ALL, 5)
|
serverBox.Add(lbl, 0, wx.ALL, 5)
|
||||||
serverBox.Add(self.server, 0, wx.ALL, 5)
|
serverBox.Add(self.server, 0, wx.ALL, 5)
|
||||||
sizer.Add(serverBox, 0, wx.ALL, 5)
|
sizer.Add(serverBox, 0, wx.ALL, 5)
|
||||||
lbl = wx.StaticText(self, wx.ID_ANY, _(u"Port: "))
|
lbl = wx.StaticText(self, wx.ID_ANY, _(u"&Port: "))
|
||||||
self.port = wx.SpinCtrl(self, wx.ID_ANY, min=1, max=65535)
|
self.port = wx.SpinCtrl(self, wx.ID_ANY, min=1, max=65535)
|
||||||
portBox = wx.BoxSizer(wx.HORIZONTAL)
|
portBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
portBox.Add(lbl, 0, wx.ALL, 5)
|
portBox.Add(lbl, 0, wx.ALL, 5)
|
||||||
portBox.Add(self.port, 0, wx.ALL, 5)
|
portBox.Add(self.port, 0, wx.ALL, 5)
|
||||||
sizer.Add(portBox, 0, wx.ALL, 5)
|
sizer.Add(portBox, 0, wx.ALL, 5)
|
||||||
lbl = wx.StaticText(self, wx.ID_ANY, _(u"User: "))
|
lbl = wx.StaticText(self, wx.ID_ANY, _(u"&User: "))
|
||||||
self.user = wx.TextCtrl(self, wx.ID_ANY)
|
self.user = wx.TextCtrl(self, wx.ID_ANY)
|
||||||
userBox = wx.BoxSizer(wx.HORIZONTAL)
|
userBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
userBox.Add(lbl, 0, wx.ALL, 5)
|
userBox.Add(lbl, 0, wx.ALL, 5)
|
||||||
userBox.Add(self.user, 0, wx.ALL, 5)
|
userBox.Add(self.user, 0, wx.ALL, 5)
|
||||||
sizer.Add(userBox, 0, wx.ALL, 5)
|
sizer.Add(userBox, 0, wx.ALL, 5)
|
||||||
lbl = wx.StaticText(self, wx.ID_ANY, _(u"Password: "))
|
lbl = wx.StaticText(self, wx.ID_ANY, _(u"P&assword: "))
|
||||||
self.password = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PASSWORD)
|
self.password = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PASSWORD)
|
||||||
passwordBox = wx.BoxSizer(wx.HORIZONTAL)
|
passwordBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
passwordBox.Add(lbl, 0, wx.ALL, 5)
|
passwordBox.Add(lbl, 0, wx.ALL, 5)
|
||||||
@@ -99,9 +95,9 @@ class reporting(wx.Panel, baseDialog.BaseWXDialog):
|
|||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super(reporting, self).__init__(parent)
|
super(reporting, self).__init__(parent)
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
self.speech_reporting = wx.CheckBox(self, wx.ID_ANY, _(U"Enable automatic speech feedback"))
|
self.speech_reporting = wx.CheckBox(self, wx.ID_ANY, _(U"Enable automatic s&peech feedback"))
|
||||||
sizer.Add(self.speech_reporting, 0, wx.ALL, 5)
|
sizer.Add(self.speech_reporting, 0, wx.ALL, 5)
|
||||||
self.braille_reporting = wx.CheckBox(self, wx.ID_ANY, _(U"Enable automatic Braille feedback"))
|
self.braille_reporting = wx.CheckBox(self, wx.ID_ANY, _(U"Enable automatic &Braille feedback"))
|
||||||
sizer.Add(self.braille_reporting, 0, wx.ALL, 5)
|
sizer.Add(self.braille_reporting, 0, wx.ALL, 5)
|
||||||
self.SetSizer(sizer)
|
self.SetSizer(sizer)
|
||||||
|
|
||||||
@@ -112,9 +108,9 @@ class other_buffers(wx.Panel):
|
|||||||
self.buffers = widgets.list(self, _(u"Buffer"), _(u"Name"), _(u"Status"), style=wx.LC_SINGLE_SEL|wx.LC_REPORT)
|
self.buffers = widgets.list(self, _(u"Buffer"), _(u"Name"), _(u"Status"), style=wx.LC_SINGLE_SEL|wx.LC_REPORT)
|
||||||
sizer.Add(self.buffers.list, 0, wx.ALL, 5)
|
sizer.Add(self.buffers.list, 0, wx.ALL, 5)
|
||||||
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
self.toggle_state = wx.Button(self, -1, _(u"Show/hide"))
|
self.toggle_state = wx.Button(self, -1, _(u"S&how/hide"))
|
||||||
self.up = wx.Button(self, -1, _(u"Move up"))
|
self.up = wx.Button(self, -1, _(u"Move &up"))
|
||||||
self.down = wx.Button(self, -1, _(u"Move down"))
|
self.down = wx.Button(self, -1, _(u"Move &down"))
|
||||||
btnSizer.Add(self.toggle_state, 0, wx.ALL, 5)
|
btnSizer.Add(self.toggle_state, 0, wx.ALL, 5)
|
||||||
btnSizer.Add(self.up, 0, wx.ALL, 5)
|
btnSizer.Add(self.up, 0, wx.ALL, 5)
|
||||||
btnSizer.Add(self.down, 0, wx.ALL, 5)
|
btnSizer.Add(self.down, 0, wx.ALL, 5)
|
||||||
@@ -206,19 +202,19 @@ class TranslatorPanel(wx.Panel, baseDialog.BaseWXDialog):
|
|||||||
super(TranslatorPanel, self).__init__(parent)
|
super(TranslatorPanel, self).__init__(parent)
|
||||||
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
lbl_libre_url = wx.StaticText(self, wx.ID_ANY, _(u"LibreTranslate API URL: "))
|
lbl_libre_url = wx.StaticText(self, wx.ID_ANY, _(u"&LibreTranslate API URL: "))
|
||||||
self.libre_api_url = wx.TextCtrl(self, wx.ID_ANY)
|
self.libre_api_url = wx.TextCtrl(self, wx.ID_ANY)
|
||||||
libreUrlBox = wx.BoxSizer(wx.HORIZONTAL)
|
libreUrlBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
libreUrlBox.Add(lbl_libre_url, 0, wx.ALL, 5)
|
libreUrlBox.Add(lbl_libre_url, 0, wx.ALL, 5)
|
||||||
libreUrlBox.Add(self.libre_api_url, 1, wx.ALL | wx.EXPAND, 5)
|
libreUrlBox.Add(self.libre_api_url, 1, wx.ALL | wx.EXPAND, 5)
|
||||||
sizer.Add(libreUrlBox, 0, wx.ALL | wx.EXPAND, 5)
|
sizer.Add(libreUrlBox, 0, wx.ALL | wx.EXPAND, 5)
|
||||||
lbl_libre_api_key = wx.StaticText(self, wx.ID_ANY, _(u"LibreTranslate API Key (optional): "))
|
lbl_libre_api_key = wx.StaticText(self, wx.ID_ANY, _(u"LibreTranslate API &Key (optional): "))
|
||||||
self.libre_api_key = wx.TextCtrl(self, wx.ID_ANY)
|
self.libre_api_key = wx.TextCtrl(self, wx.ID_ANY)
|
||||||
libreApiKeyBox = wx.BoxSizer(wx.HORIZONTAL)
|
libreApiKeyBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
libreApiKeyBox.Add(lbl_libre_api_key, 0, wx.ALL, 5)
|
libreApiKeyBox.Add(lbl_libre_api_key, 0, wx.ALL, 5)
|
||||||
libreApiKeyBox.Add(self.libre_api_key, 1, wx.ALL | wx.EXPAND, 5)
|
libreApiKeyBox.Add(self.libre_api_key, 1, wx.ALL | wx.EXPAND, 5)
|
||||||
sizer.Add(libreApiKeyBox, 0, wx.ALL | wx.EXPAND, 5)
|
sizer.Add(libreApiKeyBox, 0, wx.ALL | wx.EXPAND, 5)
|
||||||
lbl_deepL_api_key = wx.StaticText(self, wx.ID_ANY, _(u"DeepL API Key: "))
|
lbl_deepL_api_key = wx.StaticText(self, wx.ID_ANY, _(u"&DeepL API Key: "))
|
||||||
self.deepL_api_key = wx.TextCtrl(self, wx.ID_ANY)
|
self.deepL_api_key = wx.TextCtrl(self, wx.ID_ANY)
|
||||||
deepLApiKeyBox = wx.BoxSizer(wx.HORIZONTAL)
|
deepLApiKeyBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
deepLApiKeyBox.Add(lbl_deepL_api_key, 0, wx.ALL, 5)
|
deepLApiKeyBox.Add(lbl_deepL_api_key, 0, wx.ALL, 5)
|
||||||
@@ -252,9 +248,9 @@ class configurationDialog(baseDialog.BaseWXDialog):
|
|||||||
def realize(self):
|
def realize(self):
|
||||||
self.sizer.Add(self.notebook, 0, wx.ALL, 5)
|
self.sizer.Add(self.notebook, 0, wx.ALL, 5)
|
||||||
ok_cancel_box = wx.BoxSizer(wx.HORIZONTAL)
|
ok_cancel_box = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
ok = wx.Button(self.panel, wx.ID_OK, _(u"Save"))
|
ok = wx.Button(self.panel, wx.ID_OK, _(u"&Save"))
|
||||||
ok.SetDefault()
|
ok.SetDefault()
|
||||||
cancel = wx.Button(self.panel, wx.ID_CANCEL, _(u"Close"))
|
cancel = wx.Button(self.panel, wx.ID_CANCEL, _(u"&Close"))
|
||||||
self.SetEscapeId(cancel.GetId())
|
self.SetEscapeId(cancel.GetId())
|
||||||
ok_cancel_box.Add(ok, 0, wx.ALL, 5)
|
ok_cancel_box.Add(ok, 0, wx.ALL, 5)
|
||||||
ok_cancel_box.Add(cancel, 0, wx.ALL, 5)
|
ok_cancel_box.Add(cancel, 0, wx.ALL, 5)
|
||||||
|
@@ -14,8 +14,8 @@ class CommunityTimeline(wx.Dialog):
|
|||||||
communitySizer.Add(self.url, 0, wx.ALL, 5)
|
communitySizer.Add(self.url, 0, wx.ALL, 5)
|
||||||
actionSizer = wx.BoxSizer(wx.VERTICAL)
|
actionSizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
label2 = wx.StaticText(panel, -1, _(u"Buffer type"))
|
label2 = wx.StaticText(panel, -1, _(u"Buffer type"))
|
||||||
self.local= wx.RadioButton(panel, -1, _("Local timeline"), style=wx.RB_GROUP)
|
self.local= wx.RadioButton(panel, -1, _("&Local timeline"), style=wx.RB_GROUP)
|
||||||
self.federated= wx.RadioButton(panel, -1, _("Federated Timeline"))
|
self.federated= wx.RadioButton(panel, -1, _("&Federated Timeline"))
|
||||||
hSizer = wx.BoxSizer(wx.HORIZONTAL)
|
hSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
hSizer.Add(label2, 0, wx.ALL, 5)
|
hSizer.Add(label2, 0, wx.ALL, 5)
|
||||||
actionSizer.Add(self.local, 0, wx.ALL, 5)
|
actionSizer.Add(self.local, 0, wx.ALL, 5)
|
||||||
|
@@ -12,49 +12,49 @@ class generalAccount(wx.Panel, baseDialog.BaseWXDialog):
|
|||||||
super(generalAccount, self).__init__(parent)
|
super(generalAccount, self).__init__(parent)
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
userAutocompletionBox = wx.StaticBox(self, label=_("User autocompletion settings"))
|
userAutocompletionBox = wx.StaticBox(self, label=_("User autocompletion settings"))
|
||||||
self.userAutocompletionScan = wx.Button(self, wx.ID_ANY, _("Scan account and add followers and following users to the user autocompletion database"))
|
self.userAutocompletionScan = wx.Button(self, wx.ID_ANY, _("Scan acc&ount and add followers and following users to the user autocompletion database"))
|
||||||
self.userAutocompletionManage = wx.Button(self, wx.ID_ANY, _("Manage autocompletion database"))
|
self.userAutocompletionManage = wx.Button(self, wx.ID_ANY, _("&Manage autocompletion database"))
|
||||||
autocompletionSizer = wx.StaticBoxSizer(userAutocompletionBox, wx.HORIZONTAL)
|
autocompletionSizer = wx.StaticBoxSizer(userAutocompletionBox, wx.HORIZONTAL)
|
||||||
autocompletionSizer.Add(self.userAutocompletionScan, 0, wx.ALL, 5)
|
autocompletionSizer.Add(self.userAutocompletionScan, 0, wx.ALL, 5)
|
||||||
autocompletionSizer.Add(self.userAutocompletionManage, 0, wx.ALL, 5)
|
autocompletionSizer.Add(self.userAutocompletionManage, 0, wx.ALL, 5)
|
||||||
sizer.Add(autocompletionSizer, 0, wx.ALL, 5)
|
sizer.Add(autocompletionSizer, 0, wx.ALL, 5)
|
||||||
self.disable_streaming = wx.CheckBox(self, wx.ID_ANY, _("Disable Streaming API endpoints"))
|
self.disable_streaming = wx.CheckBox(self, wx.ID_ANY, _("&Disable Streaming API endpoints"))
|
||||||
sizer.Add(self.disable_streaming, 0, wx.ALL, 5)
|
sizer.Add(self.disable_streaming, 0, wx.ALL, 5)
|
||||||
self.relative_time = wx.CheckBox(self, wx.ID_ANY, _("Relative timestamps"))
|
self.relative_time = wx.CheckBox(self, wx.ID_ANY, _("&Relative timestamps"))
|
||||||
sizer.Add(self.relative_time, 0, wx.ALL, 5)
|
sizer.Add(self.relative_time, 0, wx.ALL, 5)
|
||||||
self.read_preferences_from_instance = wx.CheckBox(self, wx.ID_ANY, _("Read preferences from instance (default visibility when publishing and displaying sensitive content)"))
|
self.read_preferences_from_instance = wx.CheckBox(self, wx.ID_ANY, _("R&ead preferences from instance (default visibility when publishing and displaying sensitive content)"))
|
||||||
sizer.Add(self.read_preferences_from_instance, 0, wx.ALL, 5)
|
sizer.Add(self.read_preferences_from_instance, 0, wx.ALL, 5)
|
||||||
itemsPerCallBox = wx.BoxSizer(wx.HORIZONTAL)
|
itemsPerCallBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
itemsPerCallBox.Add(wx.StaticText(self, -1, _("Items on each API call")), 0, wx.ALL, 5)
|
itemsPerCallBox.Add(wx.StaticText(self, -1, _("&Items on each API call")), 0, wx.ALL, 5)
|
||||||
self.itemsPerApiCall = wx.SpinCtrl(self, wx.ID_ANY)
|
self.itemsPerApiCall = wx.SpinCtrl(self, wx.ID_ANY)
|
||||||
self.itemsPerApiCall.SetRange(0, 40)
|
self.itemsPerApiCall.SetRange(0, 40)
|
||||||
self.itemsPerApiCall.SetSize(self.itemsPerApiCall.GetBestSize())
|
self.itemsPerApiCall.SetSize(self.itemsPerApiCall.GetBestSize())
|
||||||
itemsPerCallBox.Add(self.itemsPerApiCall, 0, wx.ALL, 5)
|
itemsPerCallBox.Add(self.itemsPerApiCall, 0, wx.ALL, 5)
|
||||||
sizer.Add(itemsPerCallBox, 0, wx.ALL, 5)
|
sizer.Add(itemsPerCallBox, 0, wx.ALL, 5)
|
||||||
self.reverse_timelines = wx.CheckBox(self, wx.ID_ANY, _("Inverted buffers: The newest items will be shown at the beginning while the oldest at the end"))
|
self.reverse_timelines = wx.CheckBox(self, wx.ID_ANY, _("I&nverted buffers: The newest items will be shown at the beginning while the oldest at the end"))
|
||||||
sizer.Add(self.reverse_timelines, 0, wx.ALL, 5)
|
sizer.Add(self.reverse_timelines, 0, wx.ALL, 5)
|
||||||
self.ask_before_boost = wx.CheckBox(self, wx.ID_ANY, _("Ask confirmation before boosting a post"))
|
self.ask_before_boost = wx.CheckBox(self, wx.ID_ANY, _("&Ask confirmation before boosting a post"))
|
||||||
sizer.Add(self.ask_before_boost, 0, wx.ALL, 5)
|
sizer.Add(self.ask_before_boost, 0, wx.ALL, 5)
|
||||||
self.show_screen_names = wx.CheckBox(self, wx.ID_ANY, _("Show screen names instead of full names"))
|
self.show_screen_names = wx.CheckBox(self, wx.ID_ANY, _("S&how screen names instead of full names"))
|
||||||
sizer.Add(self.show_screen_names, 0, wx.ALL, 5)
|
sizer.Add(self.show_screen_names, 0, wx.ALL, 5)
|
||||||
self.hide_emojis = wx.CheckBox(self, wx.ID_ANY, _("hide emojis in usernames"))
|
self.hide_emojis = wx.CheckBox(self, wx.ID_ANY, _("Hide e&mojis in usernames"))
|
||||||
sizer.Add(self.hide_emojis, 0, wx.ALL, 5)
|
sizer.Add(self.hide_emojis, 0, wx.ALL, 5)
|
||||||
PersistSizeLabel = wx.StaticText(self, -1, _("Number of items per buffer to cache in database (0 to disable caching, blank for unlimited)"))
|
PersistSizeLabel = wx.StaticText(self, -1, _("&Number of items per buffer to cache in database (0 to disable caching, blank for unlimited)"))
|
||||||
self.persist_size = wx.TextCtrl(self, -1)
|
self.persist_size = wx.TextCtrl(self, -1)
|
||||||
sizer.Add(PersistSizeLabel, 0, wx.ALL, 5)
|
sizer.Add(PersistSizeLabel, 0, wx.ALL, 5)
|
||||||
sizer.Add(self.persist_size, 0, wx.ALL, 5)
|
sizer.Add(self.persist_size, 0, wx.ALL, 5)
|
||||||
self.load_cache_in_memory = wx.CheckBox(self, wx.NewId(), _("Load cache for items in memory (much faster in big datasets but requires more RAM)"))
|
self.load_cache_in_memory = wx.CheckBox(self, wx.NewId(), _("&Load cache for items in memory (much faster in big datasets but requires more RAM)"))
|
||||||
self.SetSizer(sizer)
|
self.SetSizer(sizer)
|
||||||
|
|
||||||
class templates(wx.Panel, baseDialog.BaseWXDialog):
|
class templates(wx.Panel, baseDialog.BaseWXDialog):
|
||||||
def __init__(self, parent, post_template, conversation_template, person_template):
|
def __init__(self, parent, post_template, conversation_template, person_template):
|
||||||
super(templates, self).__init__(parent)
|
super(templates, self).__init__(parent)
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
self.post = wx.Button(self, wx.ID_ANY, _("Edit template for posts. Current template: {}").format(post_template))
|
self.post = wx.Button(self, wx.ID_ANY, _("Edit template for &posts. Current template: {}").format(post_template))
|
||||||
sizer.Add(self.post, 0, wx.ALL, 5)
|
sizer.Add(self.post, 0, wx.ALL, 5)
|
||||||
self.conversation = wx.Button(self, wx.ID_ANY, _("Edit template for conversations. Current template: {}").format(conversation_template))
|
self.conversation = wx.Button(self, wx.ID_ANY, _("Edit template for c&onversations. Current template: {}").format(conversation_template))
|
||||||
sizer.Add(self.conversation, 0, wx.ALL, 5)
|
sizer.Add(self.conversation, 0, wx.ALL, 5)
|
||||||
self.person = wx.Button(self, wx.ID_ANY, _("Edit template for persons. Current template: {}").format(person_template))
|
self.person = wx.Button(self, wx.ID_ANY, _("Edit template for p&ersons. Current template: {}").format(person_template))
|
||||||
sizer.Add(self.person, 0, wx.ALL, 5)
|
sizer.Add(self.person, 0, wx.ALL, 5)
|
||||||
self.SetSizer(sizer)
|
self.SetSizer(sizer)
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ class sound(wx.Panel):
|
|||||||
def __init__(self, parent, input_devices, output_devices, soundpacks):
|
def __init__(self, parent, input_devices, output_devices, soundpacks):
|
||||||
wx.Panel.__init__(self, parent)
|
wx.Panel.__init__(self, parent)
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
volume = wx.StaticText(self, -1, _(u"Volume"))
|
volume = wx.StaticText(self, -1, _(u"&Volume"))
|
||||||
self.volumeCtrl = wx.Slider(self)
|
self.volumeCtrl = wx.Slider(self)
|
||||||
# Connect a key handler here to handle volume slider being inverted when moving with up and down arrows.
|
# Connect a key handler here to handle volume slider being inverted when moving with up and down arrows.
|
||||||
# see https://github.com/manuelcortez/TWBlue/issues/261
|
# see https://github.com/manuelcortez/TWBlue/issues/261
|
||||||
@@ -73,16 +73,16 @@ class sound(wx.Panel):
|
|||||||
volumeBox.Add(volume, 0, wx.ALL, 5)
|
volumeBox.Add(volume, 0, wx.ALL, 5)
|
||||||
volumeBox.Add(self.volumeCtrl, 0, wx.ALL, 5)
|
volumeBox.Add(self.volumeCtrl, 0, wx.ALL, 5)
|
||||||
sizer.Add(volumeBox, 0, wx.ALL, 5)
|
sizer.Add(volumeBox, 0, wx.ALL, 5)
|
||||||
self.session_mute = wx.CheckBox(self, -1, _(u"Session mute"))
|
self.session_mute = wx.CheckBox(self, -1, _(u"S&ession mute"))
|
||||||
sizer.Add(self.session_mute, 0, wx.ALL, 5)
|
sizer.Add(self.session_mute, 0, wx.ALL, 5)
|
||||||
output_label = wx.StaticText(self, -1, _(u"Output device"))
|
output_label = wx.StaticText(self, -1, _(u"&Output device"))
|
||||||
self.output = wx.ComboBox(self, -1, choices=output_devices, style=wx.CB_READONLY)
|
self.output = wx.ComboBox(self, -1, choices=output_devices, style=wx.CB_READONLY)
|
||||||
self.output.SetSize(self.output.GetBestSize())
|
self.output.SetSize(self.output.GetBestSize())
|
||||||
outputBox = wx.BoxSizer(wx.HORIZONTAL)
|
outputBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
outputBox.Add(output_label, 0, wx.ALL, 5)
|
outputBox.Add(output_label, 0, wx.ALL, 5)
|
||||||
outputBox.Add(self.output, 0, wx.ALL, 5)
|
outputBox.Add(self.output, 0, wx.ALL, 5)
|
||||||
sizer.Add(outputBox, 0, wx.ALL, 5)
|
sizer.Add(outputBox, 0, wx.ALL, 5)
|
||||||
input_label = wx.StaticText(self, -1, _(u"Input device"))
|
input_label = wx.StaticText(self, -1, _(u"&Input device"))
|
||||||
self.input = wx.ComboBox(self, -1, choices=input_devices, style=wx.CB_READONLY)
|
self.input = wx.ComboBox(self, -1, choices=input_devices, style=wx.CB_READONLY)
|
||||||
self.input.SetSize(self.input.GetBestSize())
|
self.input.SetSize(self.input.GetBestSize())
|
||||||
inputBox = wx.BoxSizer(wx.HORIZONTAL)
|
inputBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
@@ -90,15 +90,15 @@ class sound(wx.Panel):
|
|||||||
inputBox.Add(self.input, 0, wx.ALL, 5)
|
inputBox.Add(self.input, 0, wx.ALL, 5)
|
||||||
sizer.Add(inputBox, 0, wx.ALL, 5)
|
sizer.Add(inputBox, 0, wx.ALL, 5)
|
||||||
soundBox = wx.BoxSizer(wx.VERTICAL)
|
soundBox = wx.BoxSizer(wx.VERTICAL)
|
||||||
soundpack_label = wx.StaticText(self, -1, _(u"Sound pack"))
|
soundpack_label = wx.StaticText(self, -1, _(u"Sound &pack"))
|
||||||
self.soundpack = wx.ComboBox(self, -1, choices=soundpacks, style=wx.CB_READONLY)
|
self.soundpack = wx.ComboBox(self, -1, choices=soundpacks, style=wx.CB_READONLY)
|
||||||
self.soundpack.SetSize(self.soundpack.GetBestSize())
|
self.soundpack.SetSize(self.soundpack.GetBestSize())
|
||||||
soundBox.Add(soundpack_label, 0, wx.ALL, 5)
|
soundBox.Add(soundpack_label, 0, wx.ALL, 5)
|
||||||
soundBox.Add(self.soundpack, 0, wx.ALL, 5)
|
soundBox.Add(self.soundpack, 0, wx.ALL, 5)
|
||||||
sizer.Add(soundBox, 0, wx.ALL, 5)
|
sizer.Add(soundBox, 0, wx.ALL, 5)
|
||||||
self.indicate_audio = wx.CheckBox(self, -1, _("Indicate audio or video in posts with sound"))
|
self.indicate_audio = wx.CheckBox(self, -1, _("Indicate &audio or video in posts with sound"))
|
||||||
sizer.Add(self.indicate_audio, 0, wx.ALL, 5)
|
sizer.Add(self.indicate_audio, 0, wx.ALL, 5)
|
||||||
self.indicate_img = wx.CheckBox(self, -1, _("Indicate posts containing images with sound"))
|
self.indicate_img = wx.CheckBox(self, -1, _("Indicate posts containing i&mages with sound"))
|
||||||
sizer.Add(self.indicate_img, 0, wx.ALL, 5)
|
sizer.Add(self.indicate_img, 0, wx.ALL, 5)
|
||||||
self.SetSizer(sizer)
|
self.SetSizer(sizer)
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ class extrasPanel(wx.Panel):
|
|||||||
def __init__(self, parent, ocr_languages=[], translation_languages=[]):
|
def __init__(self, parent, ocr_languages=[], translation_languages=[]):
|
||||||
super(extrasPanel, self).__init__(parent)
|
super(extrasPanel, self).__init__(parent)
|
||||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
OCRBox = wx.StaticBox(self, label=_(u"Language for OCR"))
|
OCRBox = wx.StaticBox(self, label=_(u"&Language for OCR"))
|
||||||
self.ocr_lang = wx.ListBox(self, -1, choices=ocr_languages)
|
self.ocr_lang = wx.ListBox(self, -1, choices=ocr_languages)
|
||||||
self.ocr_lang.SetSize(self.ocr_lang.GetBestSize())
|
self.ocr_lang.SetSize(self.ocr_lang.GetBestSize())
|
||||||
ocrLanguageSizer = wx.StaticBoxSizer(OCRBox, wx.HORIZONTAL)
|
ocrLanguageSizer = wx.StaticBoxSizer(OCRBox, wx.HORIZONTAL)
|
||||||
@@ -167,9 +167,9 @@ class configurationDialog(baseDialog.BaseWXDialog):
|
|||||||
def realize(self):
|
def realize(self):
|
||||||
self.sizer.Add(self.notebook, 0, wx.ALL, 5)
|
self.sizer.Add(self.notebook, 0, wx.ALL, 5)
|
||||||
ok_cancel_box = wx.BoxSizer(wx.HORIZONTAL)
|
ok_cancel_box = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
ok = wx.Button(self.panel, wx.ID_OK, _(u"Save"))
|
ok = wx.Button(self.panel, wx.ID_OK, _(u"&Save"))
|
||||||
ok.SetDefault()
|
ok.SetDefault()
|
||||||
cancel = wx.Button(self.panel, wx.ID_CANCEL, _(u"Close"))
|
cancel = wx.Button(self.panel, wx.ID_CANCEL, _(u"&Close"))
|
||||||
self.SetEscapeId(cancel.GetId())
|
self.SetEscapeId(cancel.GetId())
|
||||||
ok_cancel_box.Add(ok, 0, wx.ALL, 5)
|
ok_cancel_box.Add(ok, 0, wx.ALL, 5)
|
||||||
ok_cancel_box.Add(cancel, 0, wx.ALL, 5)
|
ok_cancel_box.Add(cancel, 0, wx.ALL, 5)
|
||||||
|
0
src/wxUI/dialogs/mastodon/filters/__init__.py
Normal file
0
src/wxUI/dialogs/mastodon/filters/__init__.py
Normal file
146
src/wxUI/dialogs/mastodon/filters/create_filter.py
Normal file
146
src/wxUI/dialogs/mastodon/filters/create_filter.py
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
|
||||||
|
class FilterKeywordPanel(wx.Panel):
|
||||||
|
"""panel to handle filter's keywords. """
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(FilterKeywordPanel, self).__init__(parent)
|
||||||
|
self.keywords = []
|
||||||
|
# Add widgets
|
||||||
|
list_panel = wx.Panel(self)
|
||||||
|
self.keyword_list = wx.ListCtrl(list_panel, wx.ID_ANY, style=wx.LC_REPORT | wx.LC_SINGLE_SEL)
|
||||||
|
self.keyword_list.InsertColumn(0, _("Keyword"))
|
||||||
|
self.keyword_list.InsertColumn(1, _("Whole word"))
|
||||||
|
self.keyword_list.SetColumnWidth(0, wx.LIST_AUTOSIZE_USEHEADER)
|
||||||
|
self.keyword_list.SetColumnWidth(1, wx.LIST_AUTOSIZE_USEHEADER)
|
||||||
|
list_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
list_sizer.Add(self.keyword_list, 1, wx.EXPAND)
|
||||||
|
list_panel.SetSizer(list_sizer)
|
||||||
|
keyword_label = wx.StaticText(self, wx.ID_ANY, label=_("Keyword:"))
|
||||||
|
self.keyword_text = wx.TextCtrl(self, wx.ID_ANY)
|
||||||
|
keyword_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
keyword_sizer.Add(keyword_label, 0, wx.RIGHT, 5)
|
||||||
|
keyword_sizer.Add(self.keyword_text, 0, wx.EXPAND)
|
||||||
|
self.whole_word_checkbox = wx.CheckBox(self, wx.ID_ANY, label=_("Whole word"))
|
||||||
|
self.add_button = wx.Button(self, wx.ID_ANY, label=_("Add"))
|
||||||
|
self.remove_button = wx.Button(self, wx.ID_ANY, label=_("Remove"))
|
||||||
|
input_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
input_sizer.Add(keyword_sizer, 1, wx.RIGHT, 5)
|
||||||
|
input_sizer.Add(self.whole_word_checkbox, 0)
|
||||||
|
button_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
button_sizer.Add(self.add_button, 0, wx.RIGHT, 5)
|
||||||
|
button_sizer.Add(self.remove_button, 0)
|
||||||
|
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
main_sizer.Add(wx.StaticText(self, label=_("Palabras clave a filtrar:")), 0, wx.BOTTOM, 5)
|
||||||
|
main_sizer.Add(list_panel, 1, wx.EXPAND | wx.BOTTOM, 5)
|
||||||
|
main_sizer.Add(input_sizer, 0, wx.EXPAND | wx.BOTTOM, 5)
|
||||||
|
main_sizer.Add(button_sizer, 0, wx.ALIGN_RIGHT)
|
||||||
|
self.SetSizer(main_sizer)
|
||||||
|
|
||||||
|
def add_keyword(self, keyword, whole_word=False):
|
||||||
|
""" Adds a keyword to the list. """
|
||||||
|
index = self.keyword_list.InsertItem(self.keyword_list.GetItemCount(), keyword)
|
||||||
|
self.keyword_list.SetItem(index, 1, _("Yes") if whole_word else _("No"))
|
||||||
|
self.keyword_list.SetColumnWidth(0, wx.LIST_AUTOSIZE)
|
||||||
|
self.keyword_list.SetColumnWidth(1, wx.LIST_AUTOSIZE_USEHEADER)
|
||||||
|
self.keyword_text.Clear()
|
||||||
|
self.whole_word_checkbox.SetValue(False)
|
||||||
|
|
||||||
|
def remove_keyword(self):
|
||||||
|
""" Remove a keyword from the list. """
|
||||||
|
selection = self.keyword_list.GetFirstSelected()
|
||||||
|
if selection != -1:
|
||||||
|
self.keyword_list.DeleteItem(selection)
|
||||||
|
return selection
|
||||||
|
|
||||||
|
def set_keywords(self, keywords):
|
||||||
|
""" Set the list of keyword. """
|
||||||
|
self.keyword_list.DeleteAllItems()
|
||||||
|
for keyword_data in keywords:
|
||||||
|
if isinstance(keyword_data, dict):
|
||||||
|
kw = keyword_data.get('keyword', '')
|
||||||
|
whole_word = keyword_data.get('whole_word', False)
|
||||||
|
self.keywords.append({'keyword': kw, 'whole_word': whole_word})
|
||||||
|
index = self.keyword_list.InsertItem(self.keyword_list.GetItemCount(), kw)
|
||||||
|
self.keyword_list.SetItem(index, 1, _("Yes") if whole_word else _("No"))
|
||||||
|
else:
|
||||||
|
self.keywords.append({'keyword': keyword_data, 'whole_word': False})
|
||||||
|
index = self.keyword_list.InsertItem(self.keyword_list.GetItemCount(), keyword_data)
|
||||||
|
self.keyword_list.SetItem(index, 1, _("No"))
|
||||||
|
self.keyword_list.SetColumnWidth(0, wx.LIST_AUTOSIZE)
|
||||||
|
self.keyword_list.SetColumnWidth(1, wx.LIST_AUTOSIZE_USEHEADER)
|
||||||
|
|
||||||
|
class CreateFilterDialog(wx.Dialog):
|
||||||
|
def __init__(self, parent, title=_("New filter")):
|
||||||
|
super(CreateFilterDialog, self).__init__(parent, title=title, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
|
||||||
|
self.contexts = ["home", "public", "notifications", "thread", "account"]
|
||||||
|
self.context_labels = {
|
||||||
|
"home": _("Home timeline"),
|
||||||
|
"public": _("Public statuses"),
|
||||||
|
"notifications": _("Notifications"),
|
||||||
|
"thread": _("Threads"),
|
||||||
|
"account": _("Profiles")
|
||||||
|
}
|
||||||
|
self.actions = ["hide", "warn"]
|
||||||
|
self.action_labels = {
|
||||||
|
"hide": _("Hide posts"),
|
||||||
|
"warn": _("Set a content warning to posts")
|
||||||
|
}
|
||||||
|
self.expiration_options = [
|
||||||
|
("never", _("Never")),
|
||||||
|
("hours", _("Hours")),
|
||||||
|
("days", _("Days")),
|
||||||
|
("weeks", _("Weeks")),
|
||||||
|
("months", _("months"))
|
||||||
|
]
|
||||||
|
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
name_label = wx.StaticText(self, wx.ID_ANY, label=_("Title:"))
|
||||||
|
self.name_ctrl = wx.TextCtrl(self, wx.ID_ANY)
|
||||||
|
|
||||||
|
name_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
name_sizer.Add(name_label, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5)
|
||||||
|
name_sizer.Add(self.name_ctrl, 1, wx.EXPAND)
|
||||||
|
main_sizer.Add(name_sizer, 0, wx.EXPAND | wx.ALL, 10)
|
||||||
|
static_box = wx.StaticBox(self, wx.ID_ANY, label=_("Apply to:"))
|
||||||
|
context_sizer = wx.StaticBoxSizer(static_box, wx.VERTICAL)
|
||||||
|
self.context_checkboxes = {}
|
||||||
|
context_grid = wx.FlexGridSizer(rows=3, cols=2, vgap=5, hgap=10)
|
||||||
|
for context in self.contexts:
|
||||||
|
checkbox = wx.CheckBox(static_box, wx.ID_ANY, label=self.context_labels[context])
|
||||||
|
self.context_checkboxes[context] = checkbox
|
||||||
|
context_grid.Add(checkbox)
|
||||||
|
context_sizer.Add(context_grid, 0, wx.ALL, 10)
|
||||||
|
main_sizer.Add(context_sizer, 0, wx.EXPAND | wx.ALL, 10)
|
||||||
|
action_label = wx.StaticText(self, wx.ID_ANY, label=_("Action:"))
|
||||||
|
self.action_choice = wx.Choice(self, wx.ID_ANY)
|
||||||
|
for action in self.actions:
|
||||||
|
self.action_choice.Append(self.action_labels[action])
|
||||||
|
action_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
action_sizer.Add(action_label, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5)
|
||||||
|
action_sizer.Add(self.action_choice, 1)
|
||||||
|
main_sizer.Add(action_sizer, 0, wx.EXPAND | wx.ALL, 10)
|
||||||
|
expiration_label = wx.StaticText(self, wx.ID_ANY, label=_("Expires in:"))
|
||||||
|
self.expiration_choice = wx.Choice(self, wx.ID_ANY)
|
||||||
|
for e, label in self.expiration_options:
|
||||||
|
self.expiration_choice.Append(label)
|
||||||
|
self.expiration_value = wx.SpinCtrl(self, wx.ID_ANY, min=1, max=9999, initial=1)
|
||||||
|
self.expiration_value.Enable(False)
|
||||||
|
self.expiration_choice.Bind(wx.EVT_CHOICE, self.on_expiration_changed)
|
||||||
|
expiration_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
expiration_sizer.Add(expiration_label, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5)
|
||||||
|
expiration_sizer.Add(self.expiration_choice, 1, wx.RIGHT, 5)
|
||||||
|
expiration_sizer.Add(self.expiration_value, 0)
|
||||||
|
main_sizer.Add(expiration_sizer, 0, wx.EXPAND | wx.ALL, 10)
|
||||||
|
self.keyword_panel = FilterKeywordPanel(self)
|
||||||
|
main_sizer.Add(self.keyword_panel, 1, wx.EXPAND | wx.ALL, 10)
|
||||||
|
button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL)
|
||||||
|
main_sizer.Add(button_sizer, 0, wx.EXPAND | wx.ALL, 10)
|
||||||
|
self.SetSizer(main_sizer)
|
||||||
|
self.SetSize((450, 550))
|
||||||
|
self.action_choice.SetSelection(0)
|
||||||
|
self.expiration_choice.SetSelection(0)
|
||||||
|
wx.CallAfter(self.name_ctrl.SetFocus)
|
||||||
|
|
||||||
|
def on_expiration_changed(self, event):
|
||||||
|
selection = self.expiration_choice.GetSelection()
|
||||||
|
self.expiration_value.Enable(selection != 0)
|
35
src/wxUI/dialogs/mastodon/filters/manage_filters.py
Normal file
35
src/wxUI/dialogs/mastodon/filters/manage_filters.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
|
||||||
|
class ManageFiltersDialog(wx.Dialog):
|
||||||
|
"""
|
||||||
|
A dialog that displays a list of Mastodon filters and provides controls
|
||||||
|
to add, edit and remove them.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parent, title=_("Filters"), *args, **kwargs):
|
||||||
|
"""Initialize the filters view dialog. """
|
||||||
|
super(ManageFiltersDialog, self).__init__(parent, title=title, *args, **kwargs)
|
||||||
|
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
self.filter_list = wx.ListCtrl(self, style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.BORDER_SUNKEN)
|
||||||
|
self.filter_list.InsertColumn(0, _("Title"), width=150)
|
||||||
|
self.filter_list.InsertColumn(1, _("Keywords"), width=80)
|
||||||
|
self.filter_list.InsertColumn(2, _("Contexts"), width=150)
|
||||||
|
self.filter_list.InsertColumn(3, _("Action"), width=100)
|
||||||
|
self.filter_list.InsertColumn(4, _("Expires"), width=150)
|
||||||
|
main_sizer.Add(self.filter_list, 1, wx.EXPAND | wx.ALL, 10)
|
||||||
|
button_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
self.add_button = wx.Button(self, label=_("Add"))
|
||||||
|
self.edit_button = wx.Button(self, label=_("Edit"))
|
||||||
|
self.remove_button = wx.Button(self, label=_("Remove"))
|
||||||
|
close_button = wx.Button(self, wx.ID_CLOSE)
|
||||||
|
self.edit_button.Disable()
|
||||||
|
self.remove_button.Disable()
|
||||||
|
button_sizer.Add(self.add_button, 0, wx.RIGHT, 5)
|
||||||
|
button_sizer.Add(self.edit_button, 0, wx.RIGHT, 5)
|
||||||
|
button_sizer.Add(self.remove_button, 0, wx.RIGHT, 5)
|
||||||
|
button_sizer.Add((0, 0), 1, wx.EXPAND) # Spacer to push close button to right
|
||||||
|
button_sizer.Add(close_button, 0)
|
||||||
|
self.SetEscapeId(close_button.GetId())
|
||||||
|
main_sizer.Add(button_sizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 10)
|
||||||
|
self.SetSizer(main_sizer)
|
@@ -1,7 +1,8 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
import wx
|
import wx
|
||||||
|
|
||||||
class Post(wx.Dialog):
|
class Post(wx.Dialog):
|
||||||
def __init__(self, caption=_("Post"), text="", *args, **kwds):
|
def __init__(self, caption=_("Post"), text="", languages=[], *args, **kwds):
|
||||||
super(Post, self).__init__(parent=None, id=wx.ID_ANY, *args, **kwds)
|
super(Post, self).__init__(parent=None, id=wx.ID_ANY, *args, **kwds)
|
||||||
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
post_sizer = wx.WrapSizer(wx.VERTICAL)
|
post_sizer = wx.WrapSizer(wx.VERTICAL)
|
||||||
@@ -48,6 +49,12 @@ class Post(wx.Dialog):
|
|||||||
self.visibility = wx.ComboBox(self, wx.ID_ANY, choices=[_("Public"), _("Not listed"), _("Followers only"), _("Direct")], style=wx.CB_DROPDOWN | wx.CB_READONLY | wx.CB_SIMPLE)
|
self.visibility = wx.ComboBox(self, wx.ID_ANY, choices=[_("Public"), _("Not listed"), _("Followers only"), _("Direct")], style=wx.CB_DROPDOWN | wx.CB_READONLY | wx.CB_SIMPLE)
|
||||||
self.visibility.SetSelection(0)
|
self.visibility.SetSelection(0)
|
||||||
visibility_sizer.Add(self.visibility, 0, 0, 0)
|
visibility_sizer.Add(self.visibility, 0, 0, 0)
|
||||||
|
language_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
post_actions_sizer.Add(language_sizer, 0, wx.RIGHT, 20)
|
||||||
|
lang_label = wx.StaticText(self, wx.ID_ANY, _("Language"))
|
||||||
|
language_sizer.Add(lang_label, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5)
|
||||||
|
self.language = wx.ComboBox(self, wx.ID_ANY, choices=languages, style=wx.CB_DROPDOWN | wx.CB_READONLY)
|
||||||
|
language_sizer.Add(self.language, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
||||||
self.add = wx.Button(self, wx.ID_ANY, _("A&dd"))
|
self.add = wx.Button(self, wx.ID_ANY, _("A&dd"))
|
||||||
self.sensitive = wx.CheckBox(self, wx.ID_ANY, _("S&ensitive content"))
|
self.sensitive = wx.CheckBox(self, wx.ID_ANY, _("S&ensitive content"))
|
||||||
self.sensitive.SetValue(False)
|
self.sensitive.SetValue(False)
|
||||||
@@ -164,7 +171,6 @@ class Post(wx.Dialog):
|
|||||||
def unable_to_attach_poll(self, *args, **kwargs):
|
def unable_to_attach_poll(self, *args, **kwargs):
|
||||||
return wx.MessageDialog(self, _("You can add a poll or media files. In order to add your poll, please remove other attachments first."), _("Error adding poll"), wx.ICON_ERROR).ShowModal()
|
return wx.MessageDialog(self, _("You can add a poll or media files. In order to add your poll, please remove other attachments first."), _("Error adding poll"), wx.ICON_ERROR).ShowModal()
|
||||||
|
|
||||||
import wx
|
|
||||||
|
|
||||||
class viewPost(wx.Dialog):
|
class viewPost(wx.Dialog):
|
||||||
def set_title(self, length):
|
def set_title(self, length):
|
||||||
|
@@ -76,27 +76,27 @@ class ShowUserProfile(wx.Dialog):
|
|||||||
mainSizer = wx.GridSizer(2, 5, 5)
|
mainSizer = wx.GridSizer(2, 5, 5)
|
||||||
|
|
||||||
# create widgets
|
# create widgets
|
||||||
nameLabel = wx.StaticText(self.panel, label=_("Name: "))
|
nameLabel = wx.StaticText(self.panel, label=_("&Name: "))
|
||||||
name = self.createTextCtrl(user.display_name, size=(200, 30))
|
name = self.createTextCtrl(user.display_name, size=(200, 30))
|
||||||
mainSizer.Add(nameLabel, wx.SizerFlags().Center())
|
mainSizer.Add(nameLabel, wx.SizerFlags().Center())
|
||||||
mainSizer.Add(name, wx.SizerFlags().Center())
|
mainSizer.Add(name, wx.SizerFlags().Center())
|
||||||
|
|
||||||
urlLabel = wx.StaticText(self.panel, label=_("URL: "))
|
urlLabel = wx.StaticText(self.panel, label=_("&URL: "))
|
||||||
url = self.createTextCtrl(user.url, size=(200, 30))
|
url = self.createTextCtrl(user.url, size=(200, 30))
|
||||||
mainSizer.Add(urlLabel, wx.SizerFlags().Center())
|
mainSizer.Add(urlLabel, wx.SizerFlags().Center())
|
||||||
mainSizer.Add(url, wx.SizerFlags().Center())
|
mainSizer.Add(url, wx.SizerFlags().Center())
|
||||||
|
|
||||||
bioLabel = wx.StaticText(self.panel, label=_("Bio: "))
|
bioLabel = wx.StaticText(self.panel, label=_("&Bio: "))
|
||||||
bio = self.createTextCtrl(html_filter(user.note), (400, 60))
|
bio = self.createTextCtrl(html_filter(user.note), (400, 60))
|
||||||
mainSizer.Add(bioLabel, wx.SizerFlags().Center())
|
mainSizer.Add(bioLabel, wx.SizerFlags().Center())
|
||||||
mainSizer.Add(bio, wx.SizerFlags().Center())
|
mainSizer.Add(bio, wx.SizerFlags().Center())
|
||||||
|
|
||||||
joinLabel = wx.StaticText(self.panel, label=_("Joined at: "))
|
joinLabel = wx.StaticText(self.panel, label=_("&Joined at: "))
|
||||||
joinText = self.createTextCtrl(user.created_at.strftime('%d %B, %Y'), (80, 30))
|
joinText = self.createTextCtrl(user.created_at.strftime('%d %B, %Y'), (80, 30))
|
||||||
mainSizer.Add(joinLabel, wx.SizerFlags().Center())
|
mainSizer.Add(joinLabel, wx.SizerFlags().Center())
|
||||||
mainSizer.Add(joinText, wx.SizerFlags().Center())
|
mainSizer.Add(joinText, wx.SizerFlags().Center())
|
||||||
|
|
||||||
actions = wx.Button(self.panel, label=_("Actions"))
|
actions = wx.Button(self.panel, label=_("&Actions"))
|
||||||
actions.Bind(wx.EVT_BUTTON, self.onAction)
|
actions.Bind(wx.EVT_BUTTON, self.onAction)
|
||||||
mainSizer.Add(actions, wx.SizerFlags().Center())
|
mainSizer.Add(actions, wx.SizerFlags().Center())
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ class ShowUserProfile(wx.Dialog):
|
|||||||
self.fields = []
|
self.fields = []
|
||||||
for num, field in enumerate(user.fields):
|
for num, field in enumerate(user.fields):
|
||||||
labelSizer = wx.BoxSizer(wx.HORIZONTAL)
|
labelSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
labelLabel = wx.StaticText(self.panel, label=_("Field {} - Label: ").format(num + 1))
|
labelLabel = wx.StaticText(self.panel, label=_("Field &{} - Label: ").format(num + 1))
|
||||||
labelSizer.Add(labelLabel, wx.SizerFlags().Center().Border(wx.ALL, 5))
|
labelSizer.Add(labelLabel, wx.SizerFlags().Center().Border(wx.ALL, 5))
|
||||||
labelText = self.createTextCtrl(html_filter(field.name), (230, 30), True)
|
labelText = self.createTextCtrl(html_filter(field.name), (230, 30), True)
|
||||||
labelSizer.Add(labelText, wx.SizerFlags().Expand().Border(wx.ALL, 5))
|
labelSizer.Add(labelText, wx.SizerFlags().Expand().Border(wx.ALL, 5))
|
||||||
@@ -134,40 +134,40 @@ class ShowUserProfile(wx.Dialog):
|
|||||||
|
|
||||||
bullSwitch = {True: _('Yes'), False: _('No'), None: _('No')}
|
bullSwitch = {True: _('Yes'), False: _('No'), None: _('No')}
|
||||||
privateSizer = wx.BoxSizer(wx.HORIZONTAL)
|
privateSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
privateLabel = wx.StaticText(self.panel, label=_("Private account: "))
|
privateLabel = wx.StaticText(self.panel, label=_("&Private account: "))
|
||||||
private = self.createTextCtrl(bullSwitch[user.locked], (30, 30))
|
private = self.createTextCtrl(bullSwitch[user.locked], (30, 30))
|
||||||
privateSizer.Add(privateLabel, wx.SizerFlags().Center())
|
privateSizer.Add(privateLabel, wx.SizerFlags().Center())
|
||||||
privateSizer.Add(private, wx.SizerFlags().Center())
|
privateSizer.Add(private, wx.SizerFlags().Center())
|
||||||
mainSizer.Add(privateSizer, 0, wx.ALL | wx.CENTER)
|
mainSizer.Add(privateSizer, 0, wx.ALL | wx.CENTER)
|
||||||
|
|
||||||
botSizer = wx.BoxSizer(wx.HORIZONTAL)
|
botSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
botLabel = wx.StaticText(self.panel, label=_("Bot account: "))
|
botLabel = wx.StaticText(self.panel, label=_("&Bot account: "))
|
||||||
botText = self.createTextCtrl(bullSwitch[user.bot], (30, 30))
|
botText = self.createTextCtrl(bullSwitch[user.bot], (30, 30))
|
||||||
botSizer.Add(botLabel, wx.SizerFlags().Center())
|
botSizer.Add(botLabel, wx.SizerFlags().Center())
|
||||||
botSizer.Add(botText, wx.SizerFlags().Center())
|
botSizer.Add(botText, wx.SizerFlags().Center())
|
||||||
mainSizer.Add(botSizer, 0, wx.ALL | wx.CENTER)
|
mainSizer.Add(botSizer, 0, wx.ALL | wx.CENTER)
|
||||||
|
|
||||||
discoverSizer = wx.BoxSizer(wx.HORIZONTAL)
|
discoverSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
discoverLabel = wx.StaticText(self.panel, label=_("Discoverable account: "))
|
discoverLabel = wx.StaticText(self.panel, label=_("&Discoverable account: "))
|
||||||
discoverText = self.createTextCtrl(bullSwitch[user.discoverable], (30, 30))
|
discoverText = self.createTextCtrl(bullSwitch[user.discoverable], (30, 30))
|
||||||
discoverSizer.Add(discoverLabel, wx.SizerFlags().Center())
|
discoverSizer.Add(discoverLabel, wx.SizerFlags().Center())
|
||||||
discoverSizer.Add(discoverText, wx.SizerFlags().Center())
|
discoverSizer.Add(discoverText, wx.SizerFlags().Center())
|
||||||
mainSizer.Add(discoverSizer, 0, wx.ALL | wx.CENTER)
|
mainSizer.Add(discoverSizer, 0, wx.ALL | wx.CENTER)
|
||||||
|
|
||||||
posts = wx.Button(self.panel, label=_("{} posts. Click to open posts timeline").format(user.statuses_count))
|
posts = wx.Button(self.panel, label=_("{} p&osts. Click to open posts timeline").format(user.statuses_count))
|
||||||
# posts.SetToolTip(_("Click to open {}'s posts").format(user.display_name))
|
# posts.SetToolTip(_("Click to open {}'s posts").format(user.display_name))
|
||||||
posts.Bind(wx.EVT_BUTTON, self.onPost)
|
posts.Bind(wx.EVT_BUTTON, self.onPost)
|
||||||
mainSizer.Add(posts, wx.SizerFlags().Center())
|
mainSizer.Add(posts, wx.SizerFlags().Center())
|
||||||
|
|
||||||
following = wx.Button(self.panel, label=_("{} following. Click to open Following timeline").format(user.following_count))
|
following = wx.Button(self.panel, label=_("{} &following. Click to open Following timeline").format(user.following_count))
|
||||||
mainSizer.Add(following, wx.SizerFlags().Center())
|
mainSizer.Add(following, wx.SizerFlags().Center())
|
||||||
following.Bind(wx.EVT_BUTTON, self.onFollowing)
|
following.Bind(wx.EVT_BUTTON, self.onFollowing)
|
||||||
|
|
||||||
followers = wx.Button(self.panel, label=_("{} followers. Click to open followers timeline").format(user.followers_count))
|
followers = wx.Button(self.panel, label=_("{} fo&llowers. Click to open followers timeline").format(user.followers_count))
|
||||||
mainSizer.Add(followers, wx.SizerFlags().Center())
|
mainSizer.Add(followers, wx.SizerFlags().Center())
|
||||||
followers.Bind(wx.EVT_BUTTON, self.onFollowers)
|
followers.Bind(wx.EVT_BUTTON, self.onFollowers)
|
||||||
|
|
||||||
close = wx.Button(self.panel, wx.ID_CLOSE, _("Close"))
|
close = wx.Button(self.panel, wx.ID_CLOSE, _("&Close"))
|
||||||
self.SetEscapeId(close.GetId())
|
self.SetEscapeId(close.GetId())
|
||||||
close.SetDefault()
|
close.SetDefault()
|
||||||
wrapperSizer.Add(mainSizer, 0, wx.CENTER)
|
wrapperSizer.Add(mainSizer, 0, wx.CENTER)
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
@@ -39,14 +40,14 @@ class UpdateProfileDialog(wx.Dialog):
|
|||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
|
||||||
# create widgets
|
# create widgets
|
||||||
display_name_label = wx.StaticText(panel, label=_("Display Name"))
|
display_name_label = wx.StaticText(panel, label=_("&Display Name"))
|
||||||
self.display_name = wx.TextCtrl(panel, value=display_name, style= wx.TE_PROCESS_ENTER, size=(200, 30))
|
self.display_name = wx.TextCtrl(panel, value=display_name, style= wx.TE_PROCESS_ENTER, size=(200, 30))
|
||||||
name_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
name_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
name_sizer.Add(display_name_label, wx.SizerFlags().Center())
|
name_sizer.Add(display_name_label, wx.SizerFlags().Center())
|
||||||
name_sizer.Add(self.display_name, wx.SizerFlags().Center())
|
name_sizer.Add(self.display_name, wx.SizerFlags().Center())
|
||||||
sizer.Add(name_sizer, wx.CENTER)
|
sizer.Add(name_sizer, wx.CENTER)
|
||||||
|
|
||||||
bio_label = wx.StaticText(panel, label=_("Bio"))
|
bio_label = wx.StaticText(panel, label=_("&Bio"))
|
||||||
self.bio = wx.TextCtrl(panel, value=note, style=wx.TE_PROCESS_ENTER | wx.TE_MULTILINE, size=(400, 60))
|
self.bio = wx.TextCtrl(panel, value=note, style=wx.TE_PROCESS_ENTER | wx.TE_MULTILINE, size=(400, 60))
|
||||||
bio_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
bio_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
bio_sizer.Add(bio_label, wx.SizerFlags().Center())
|
bio_sizer.Add(bio_label, wx.SizerFlags().Center())
|
||||||
@@ -67,7 +68,7 @@ class UpdateProfileDialog(wx.Dialog):
|
|||||||
self.header_image = wx.StaticBitmap(panel, bitmap=image.ConvertToBitmap())
|
self.header_image = wx.StaticBitmap(panel, bitmap=image.ConvertToBitmap())
|
||||||
|
|
||||||
self.header_image.AcceptsFocusFromKeyboard = return_true
|
self.header_image.AcceptsFocusFromKeyboard = return_true
|
||||||
self.change_header = wx.Button(panel, label=_("Change header"))
|
self.change_header = wx.Button(panel, label=_("Change &header"))
|
||||||
header_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
header_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
header_sizer.Add(header_label, wx.SizerFlags().Center())
|
header_sizer.Add(header_label, wx.SizerFlags().Center())
|
||||||
header_sizer.Add(self.header_image, wx.SizerFlags().Center())
|
header_sizer.Add(self.header_image, wx.SizerFlags().Center())
|
||||||
@@ -88,7 +89,7 @@ class UpdateProfileDialog(wx.Dialog):
|
|||||||
self.avatar_image = wx.StaticBitmap(panel, bitmap=image.ConvertToBitmap())
|
self.avatar_image = wx.StaticBitmap(panel, bitmap=image.ConvertToBitmap())
|
||||||
|
|
||||||
self.avatar_image.AcceptsFocusFromKeyboard = return_true
|
self.avatar_image.AcceptsFocusFromKeyboard = return_true
|
||||||
self.change_avatar = wx.Button(panel, label=_("Change avatar"))
|
self.change_avatar = wx.Button(panel, label=_("Change &avatar"))
|
||||||
avatar_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
avatar_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
avatar_sizer.Add(avatar_label, wx.SizerFlags().Center())
|
avatar_sizer.Add(avatar_label, wx.SizerFlags().Center())
|
||||||
avatar_sizer.Add(self.avatar_image, wx.SizerFlags().Center())
|
avatar_sizer.Add(self.avatar_image, wx.SizerFlags().Center())
|
||||||
@@ -98,7 +99,7 @@ class UpdateProfileDialog(wx.Dialog):
|
|||||||
self.fields = []
|
self.fields = []
|
||||||
for i in range(1, 5):
|
for i in range(1, 5):
|
||||||
field_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
field_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
field_label = wx.StaticText(panel, label=_("Field {}: Label").format(i))
|
field_label = wx.StaticText(panel, label=_("Field &{}: Label").format(i))
|
||||||
field_sizer.Add(field_label, wx.SizerFlags().Center().Border(wx.ALL, 5))
|
field_sizer.Add(field_label, wx.SizerFlags().Center().Border(wx.ALL, 5))
|
||||||
|
|
||||||
label_textctrl = wx.TextCtrl(panel, style=wx.TE_PROCESS_ENTER | wx.TE_MULTILINE, size=(200, 30))
|
label_textctrl = wx.TextCtrl(panel, style=wx.TE_PROCESS_ENTER | wx.TE_MULTILINE, size=(200, 30))
|
||||||
@@ -116,11 +117,11 @@ class UpdateProfileDialog(wx.Dialog):
|
|||||||
sizer.Add(field_sizer, 0, wx.CENTER)
|
sizer.Add(field_sizer, 0, wx.CENTER)
|
||||||
self.fields.append((label_textctrl, content_textctrl))
|
self.fields.append((label_textctrl, content_textctrl))
|
||||||
|
|
||||||
self.locked = wx.CheckBox(panel, label=_("Private account"))
|
self.locked = wx.CheckBox(panel, label=_("&Private account"))
|
||||||
self.locked.SetValue(locked)
|
self.locked.SetValue(locked)
|
||||||
self.bot = wx.CheckBox(panel, label=_("Bot account"))
|
self.bot = wx.CheckBox(panel, label=_("&Bot account"))
|
||||||
self.bot.SetValue(bot)
|
self.bot.SetValue(bot)
|
||||||
self.discoverable = wx.CheckBox(panel, label=_("Discoverable account"))
|
self.discoverable = wx.CheckBox(panel, label=_("&Discoverable account"))
|
||||||
self.discoverable.SetValue(discoverable)
|
self.discoverable.SetValue(discoverable)
|
||||||
sizer.Add(self.locked, wx.SizerFlags().Expand().Border(wx.ALL, 5))
|
sizer.Add(self.locked, wx.SizerFlags().Expand().Border(wx.ALL, 5))
|
||||||
sizer.Add(self.bot, wx.SizerFlags().Expand().Border(wx.ALL, 5))
|
sizer.Add(self.bot, wx.SizerFlags().Expand().Border(wx.ALL, 5))
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
import wx
|
import wx
|
||||||
|
|
||||||
class UserListDialog(wx.Dialog):
|
class UserListDialog(wx.Dialog):
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{"current_version": "2024.05.19",
|
{"current_version": "2024.05.23",
|
||||||
"description": "Initial support for GoToSocial instances, mute conversations, local and public timelines from remote instances, translate using LibreTranslate or DeepL and several bugfixes.",
|
"description": "Read long posts in GUI, display posts with unicode characters and several buffixes.",
|
||||||
"date": "unknown",
|
"date": "unknown",
|
||||||
"downloads":
|
"downloads":
|
||||||
{"Windows32": "https://github.com/MCV-Software/TWBlue/releases/download/release/TWBlue_portable_v2024.01.05.zip",
|
{"Windows32": "https://github.com/MCV-Software/TWBlue/releases/download/v2024.05.23/TWBlue_portable_v2024.05.23.zip",
|
||||||
"Windows64": "https://github.com/MCV-Software/TWBlue/releases/download/release/TWBlue_portable_v2024.01.05.zip"}
|
"Windows64": "https://github.com/MCV-Software/TWBlue/releases/download/v2024.05.23/TWBlue_portable_v2024.05.23.zip"}
|
||||||
}
|
}
|
Reference in New Issue
Block a user