mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 19:32:10 +02:00
Compare commits
86 Commits
Author | SHA1 | Date | |
---|---|---|---|
21e49505d1
|
|||
e0025e02d1
|
|||
4a9590c1d9
|
|||
7de0c50387
|
|||
ccb87351c1
|
|||
273e7266eb
|
|||
427975e5ce
|
|||
adb304b6a2
|
|||
3a89a28f8b
|
|||
cff93dcc97
|
|||
dfde38ea3b
|
|||
a8e7f065cf
|
|||
5ebfc67e48
|
|||
57b03baad9
|
|||
c91f1d0e5e
|
|||
6a421292b3
|
|||
ceae748503
|
|||
008de87e55
|
|||
e7479ef9e3
|
|||
13f8f12ac8
|
|||
fd634379d7
|
|||
e854315a81
|
|||
86136d7692
|
|||
db251bfdfd
|
|||
9d39f26d54
|
|||
9cd813c02c
|
|||
8e1f87e062
|
|||
dda954285c
|
|||
ed4f7b6b72
|
|||
7239dcf4d2
|
|||
901cca268e
|
|||
b909341988
|
|||
3e607ae0fe
|
|||
587060f73c
|
|||
3ea6f6ac18
|
|||
c162761464
|
|||
994f9635ef
|
|||
ab8752845d
|
|||
a94ee2fe4b
|
|||
763c999b09
|
|||
b18cd2658c
|
|||
d9782f554f
|
|||
f1d9663709
|
|||
bcf77052a5
|
|||
85d15b32e9 | |||
3d3b695c85 | |||
88b7cac298 | |||
c7efa631e1 | |||
aeb90cbb1a | |||
ae1c59847f | |||
651d9be57c | |||
eeb32db6fb | |||
daa0780644 | |||
8502f9105f | |||
16ced3d827 | |||
1c16187346 | |||
2fe058d9cb | |||
cefdadd53a | |||
c21c10df63 | |||
b4d359d30c | |||
1e8c62ac25 | |||
c578f36644 | |||
c973d0cff4 | |||
0c3d9ae46a | |||
a834e8b6a2 | |||
9f5580d983 | |||
e94e3cecf8 | |||
1991f7f50f | |||
9eb4e623e7 | |||
ad28d2279f | |||
1e3de31fc3 | |||
f85bd41b96 | |||
563124b68c | |||
63de08c635 | |||
8a0a215443 | |||
f1b7cd633e | |||
458eeeccda | |||
464e758b94 | |||
4c61047e9b | |||
1bf9e7fb56 | |||
ca69554f37 | |||
cf7029037e | |||
418388b0ab | |||
a0f8689d4f | |||
95007bdd26 | |||
ab91540deb |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,5 +1,3 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
*.txt text eof=lf
|
||||
* text=auto eof=lf
|
||||
*.cs diff=csharp
|
||||
|
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1,4 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: chylex
|
||||
patreon: chylex
|
||||
ko_fi: chylex
|
||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@@ -5,8 +5,14 @@
|
||||
bld/*
|
||||
!bld/*.iss
|
||||
!bld/*.bat
|
||||
!bld/*.ps1
|
||||
!bld/Redist
|
||||
!bld/Resources
|
||||
|
||||
# Rider
|
||||
.idea/.idea.TweetDuck/.idea/dictionaries
|
||||
.idea/.idea.TweetDuck/.idea/misc.xml
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
@@ -140,4 +146,3 @@ _UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
|
8
.idea/.idea.TweetDuck/.idea/.gitignore
generated
vendored
Normal file
8
.idea/.idea.TweetDuck/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Rider ignored files
|
||||
/projectSettingsUpdater.xml
|
||||
/modules.xml
|
||||
/contentModel.xml
|
||||
/.idea.TweetDuck.iml
|
450
.idea/.idea.TweetDuck/.idea/codeStyles/Project.xml
generated
Normal file
450
.idea/.idea.TweetDuck/.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,450 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="AUTODETECT_INDENTS" value="false" />
|
||||
<option name="OTHER_INDENT_OPTIONS">
|
||||
<value>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<option name="RIGHT_MARGIN" value="999" />
|
||||
<option name="FORMATTER_TAGS_ENABLED" value="true" />
|
||||
<CssCodeStyleSettings>
|
||||
<option name="HEX_COLOR_LOWER_CASE" value="true" />
|
||||
</CssCodeStyleSettings>
|
||||
<DB2CodeStyleSettings version="6">
|
||||
<option name="USE_GENERIC_STYLE" value="true" />
|
||||
</DB2CodeStyleSettings>
|
||||
<DerbyCodeStyleSettings version="6">
|
||||
<option name="USE_GENERIC_STYLE" value="true" />
|
||||
</DerbyCodeStyleSettings>
|
||||
<GoCodeStyleSettings>
|
||||
<option name="MOVE_ALL_STDLIB_IMPORTS_IN_ONE_GROUP" value="true" />
|
||||
<option name="GROUP_STDLIB_IMPORTS" value="true" />
|
||||
<option name="WRAP_COMP_LIT" value="5" />
|
||||
<option name="WRAP_FUNC_PARAMS" value="5" />
|
||||
<option name="WRAP_FUNC_RESULT" value="5" />
|
||||
</GoCodeStyleSettings>
|
||||
<H2CodeStyleSettings version="6">
|
||||
<option name="USE_GENERIC_STYLE" value="true" />
|
||||
</H2CodeStyleSettings>
|
||||
<H2CodeStyleSettings version="6">
|
||||
<option name="USE_GENERIC_STYLE" value="true" />
|
||||
</H2CodeStyleSettings>
|
||||
<HSQLCodeStyleSettings version="6">
|
||||
<option name="USE_GENERIC_STYLE" value="true" />
|
||||
</HSQLCodeStyleSettings>
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_ALIGN_TEXT" value="true" />
|
||||
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||
<option name="HTML_ELEMENTS_TO_REMOVE_NEW_LINE_BEFORE" value="" />
|
||||
<option name="HTML_DO_NOT_INDENT_CHILDREN_OF" value="" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACKETS" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="USE_EXPLICIT_JS_EXTENSION" value="TRUE" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
<option name="USE_CHAINED_CALLS_GROUP_INDENTS" value="true" />
|
||||
<option name="SPACE_BEFORE_ASYNC_ARROW_LPAREN" value="false" />
|
||||
<option name="IMPORT_SORT_MODULE_NAME" value="true" />
|
||||
</JSCodeStyleSettings>
|
||||
<JSON>
|
||||
<option name="OBJECT_WRAPPING" value="5" />
|
||||
<option name="ARRAY_WRAPPING" value="5" />
|
||||
</JSON>
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="INSERT_INNER_CLASS_IMPORTS" value="true" />
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
||||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
||||
<value />
|
||||
</option>
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
<value>
|
||||
<package name="" withSubpackages="true" static="false" />
|
||||
<package name="javax" withSubpackages="true" static="false" />
|
||||
<package name="java" withSubpackages="true" static="false" />
|
||||
<package name="" withSubpackages="true" static="true" />
|
||||
</value>
|
||||
</option>
|
||||
</JavaCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||
<option name="IMPORT_NESTED_CLASSES" value="true" />
|
||||
<option name="WRAP_ELVIS_EXPRESSIONS" value="0" />
|
||||
<option name="ALLOW_TRAILING_COMMA" value="true" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<LessCodeStyleSettings>
|
||||
<option name="HEX_COLOR_LOWER_CASE" value="true" />
|
||||
</LessCodeStyleSettings>
|
||||
<MSSQLCodeStyleSettings version="6">
|
||||
<option name="USE_GENERIC_STYLE" value="true" />
|
||||
</MSSQLCodeStyleSettings>
|
||||
<MySQLCodeStyleSettings version="6">
|
||||
<option name="USE_GENERIC_STYLE" value="true" />
|
||||
</MySQLCodeStyleSettings>
|
||||
<Objective-C>
|
||||
<option name="INDENT_DIRECTIVE_AS_CODE" value="true" />
|
||||
<option name="KEEP_STRUCTURES_IN_ONE_LINE" value="true" />
|
||||
<option name="KEEP_CASE_EXPRESSIONS_IN_ONE_LINE" value="true" />
|
||||
<option name="SPACE_BEFORE_INIT_LIST" value="true" />
|
||||
<option name="SPACE_AFTER_DICTIONARY_LITERAL_COLON" value="false" />
|
||||
</Objective-C>
|
||||
<OracleCodeStyleSettings version="6">
|
||||
<option name="USE_GENERIC_STYLE" value="true" />
|
||||
</OracleCodeStyleSettings>
|
||||
<PHPCodeStyleSettings>
|
||||
<option name="ALIGN_KEY_VALUE_PAIRS" value="true" />
|
||||
<option name="CONCAT_SPACES" value="false" />
|
||||
<option name="COMMA_AFTER_LAST_ARRAY_ELEMENT" value="true" />
|
||||
<option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" />
|
||||
<option name="LOWER_CASE_BOOLEAN_CONST" value="true" />
|
||||
<option name="LOWER_CASE_NULL_CONST" value="true" />
|
||||
<option name="ELSE_IF_STYLE" value="COMBINE" />
|
||||
<option name="VARIABLE_NAMING_STYLE" value="SNAKE_CASE" />
|
||||
<option name="KEEP_BLANK_LINES_AFTER_LBRACE" value="0" />
|
||||
<option name="SPACE_BEFORE_CLOSURE_LEFT_PARENTHESIS" value="false" />
|
||||
<option name="FORCE_SHORT_DECLARATION_ARRAY_STYLE" value="true" />
|
||||
<option name="NEW_LINE_AFTER_PHP_OPENING_TAG" value="true" />
|
||||
<option name="SPACE_AROUND_ASSIGNMENT_IN_DECLARE" value="true" />
|
||||
</PHPCodeStyleSettings>
|
||||
<PostgresCodeStyleSettings version="6">
|
||||
<option name="USE_GENERIC_STYLE" value="true" />
|
||||
</PostgresCodeStyleSettings>
|
||||
<Properties>
|
||||
<option name="KEEP_BLANK_LINES" value="true" />
|
||||
</Properties>
|
||||
<Python>
|
||||
<option name="SPACE_AROUND_EQ_IN_NAMED_PARAMETER" value="true" />
|
||||
<option name="SPACE_AROUND_EQ_IN_KEYWORD_ARGUMENT" value="true" />
|
||||
<option name="NEW_LINE_AFTER_COLON" value="true" />
|
||||
<option name="DICT_WRAPPING" value="5" />
|
||||
<option name="DICT_NEW_LINE_AFTER_LEFT_BRACE" value="true" />
|
||||
<option name="DICT_NEW_LINE_BEFORE_RIGHT_BRACE" value="true" />
|
||||
</Python>
|
||||
<RsCodeStyleSettings>
|
||||
<option name="ALIGN_RET_TYPE" value="false" />
|
||||
<option name="ALIGN_TYPE_PARAMS" value="true" />
|
||||
<option name="ALLOW_ONE_LINE_MATCH" value="true" />
|
||||
<option name="SPACE_AROUND_ASSOC_TYPE_BINDING" value="true" />
|
||||
</RsCodeStyleSettings>
|
||||
<Ruby>
|
||||
<option name="INDENT_PRIVATE_METHODS" value="true" />
|
||||
<option name="INDENT_PROTECTED_METHODS" value="true" />
|
||||
<option name="INDENT_PUBLIC_METHODS" value="true" />
|
||||
<option name="INDENT_WHEN_CASES" value="true" />
|
||||
<option name="CHAIN_CALLS_ALIGNMENT" value="2" />
|
||||
</Ruby>
|
||||
<SQLiteCodeStyleSettings version="6">
|
||||
<option name="USE_GENERIC_STYLE" value="true" />
|
||||
</SQLiteCodeStyleSettings>
|
||||
<ScssCodeStyleSettings>
|
||||
<option name="HEX_COLOR_LOWER_CASE" value="true" />
|
||||
</ScssCodeStyleSettings>
|
||||
<SqlCodeStyleSettings version="6">
|
||||
<option name="KEYWORD_CASE" value="2" />
|
||||
<option name="TYPE_CASE" value="2" />
|
||||
<option name="CUSTOM_TYPE_CASE" value="2" />
|
||||
<option name="SUBQUERY_CONTENT" value="1" />
|
||||
<option name="SUBQUERY_CLOSING" value="1" />
|
||||
<option name="INSERT_TABLE_EL_LINE" value="0" />
|
||||
<option name="INSERT_EL_WRAP" value="2" />
|
||||
<option name="SET_EL_WRAP" value="2" />
|
||||
<option name="SET_ALIGN_EQUAL_SIGN" value="false" />
|
||||
<option name="FROM_EL_WRAP" value="2" />
|
||||
<option name="FROM_ALIGN_JOIN_TABLES" value="true" />
|
||||
<option name="FROM_INDENT_JOIN" value="false" />
|
||||
<option name="FROM_ONLY_JOIN_INDENT" value="2" />
|
||||
<option name="WHERE_EL_WRAP" value="2" />
|
||||
<option name="TABLE_OPENING" value="1" />
|
||||
<option name="TABLE_CONTENT" value="2" />
|
||||
<option name="TABLE_CLOSING" value="3" />
|
||||
<option name="TABLE_DEFAULTS_ALIGN" value="false" />
|
||||
<option name="TABLE_NULLABILITIES_ALIGN" value="false" />
|
||||
<option name="CONSTRAINT_WRAP_1" value="false" />
|
||||
<option name="CONSTRAINT_WRAP_3" value="true" />
|
||||
<option name="CONSTRAINT_WRAP_4" value="true" />
|
||||
<option name="VIEW_INDENT_QUERY" value="true" />
|
||||
<option name="EXPR_CASE_WHEN_WRAP" value="false" />
|
||||
</SqlCodeStyleSettings>
|
||||
<SybaseCodeStyleSettings version="6">
|
||||
<option name="USE_GENERIC_STYLE" value="true" />
|
||||
</SybaseCodeStyleSettings>
|
||||
<TypeScriptCodeStyleSettings version="0">
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
<option name="USE_CHAINED_CALLS_GROUP_INDENTS" value="true" />
|
||||
<option name="SPACE_BEFORE_ASYNC_ARROW_LPAREN" value="false" />
|
||||
</TypeScriptCodeStyleSettings>
|
||||
<XML>
|
||||
<option name="XML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||
</XML>
|
||||
<codeStyleSettings language="CMake">
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="CSS">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Groovy">
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="RIGHT_MARGIN" value="999" />
|
||||
<option name="BLANK_LINES_AFTER_PACKAGE" value="0" />
|
||||
<option name="BLANK_LINES_BEFORE_IMPORTS" value="0" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
|
||||
<option name="SPACE_AFTER_TYPE_CAST" value="false" />
|
||||
<option name="SPACE_BEFORE_SYNCHRONIZED_PARENTHESES" value="false" />
|
||||
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
||||
<option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" />
|
||||
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JSON">
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
<option name="SPACE_WITHIN_BRACKETS" value="true" />
|
||||
<option name="SPACE_WITHIN_BRACES" value="true" />
|
||||
<indentOptions>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="LESS">
|
||||
<indentOptions>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Lua">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="ObjectiveC">
|
||||
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<option name="LINE_COMMENT_ADD_SPACE" value="true" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="PHP">
|
||||
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
|
||||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
|
||||
<option name="CLASS_BRACE_STYLE" value="1" />
|
||||
<option name="METHOD_BRACE_STYLE" value="1" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="SPECIAL_ELSE_IF_TREATMENT" value="true" />
|
||||
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
|
||||
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
|
||||
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
||||
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Puppet">
|
||||
<indentOptions>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Python">
|
||||
<indentOptions>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="RHTML">
|
||||
<indentOptions>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Rust">
|
||||
<option name="RIGHT_MARGIN" value="140" />
|
||||
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="SASS">
|
||||
<indentOptions>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="SCSS">
|
||||
<indentOptions>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="SQL">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Shell Script">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="4" />
|
||||
<option name="TAB_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TOML">
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TypeScript">
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="go">
|
||||
<option name="CALL_PARAMETERS_WRAP" value="5" />
|
||||
<indentOptions>
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<option name="LINE_COMMENT_ADD_SPACE" value="true" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="METHOD_ANNOTATION_WRAP" value="0" />
|
||||
<option name="CLASS_ANNOTATION_WRAP" value="0" />
|
||||
<option name="FIELD_ANNOTATION_WRAP" value="0" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="liquid">
|
||||
<indentOptions>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="ruby">
|
||||
<option name="SPACE_WITHIN_BRACES" value="true" />
|
||||
<indentOptions>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="yaml">
|
||||
<indentOptions>
|
||||
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
5
.idea/.idea.TweetDuck/.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/.idea.TweetDuck/.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
6
.idea/.idea.TweetDuck/.idea/encodings.xml
generated
Normal file
6
.idea/.idea.TweetDuck/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
8
.idea/.idea.TweetDuck/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.TweetDuck/.idea/indexLayout.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
</project>
|
770
.idea/.idea.TweetDuck/.idea/inspectionProfiles/Project.xml
generated
Normal file
770
.idea/.idea.TweetDuck/.idea/inspectionProfiles/Project.xml
generated
Normal file
@@ -0,0 +1,770 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project" />
|
||||
<inspection_tool class="AccessToNonThreadSafeStaticFieldFromInstance" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="nonThreadSafeClasses">
|
||||
<value />
|
||||
</option>
|
||||
<option name="nonThreadSafeTypes" value="" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="AccessToStaticFieldLockedOnInstance" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AddOperatorModifier" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AmbiguousFieldAccess" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AmbiguousMethodCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AnonymousInnerClassMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ArrayEquality" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AssertEqualsCalledOnArray" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AssertsWithoutMessagesTestNG" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AssignmentOrReturnOfFieldWithMutableType" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AssignmentToCatchBlockParameter" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AssignmentToLambdaParameter" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AssignmentToMethodParameter" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreTransformationOfOriginalParameter" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="AssignmentToStaticFieldFromInstanceMethod" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AssignmentUsedAsCondition" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AutoBoxing" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreAddedToCollection" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredTypes" value="java.util.stream.Stream,java.util.stream.IntStream,java.util.stream.LongStream,java.util.stream.DoubleStream,net.minecraft.client.Minecraft,net.minecraft.client.MainWindow" />
|
||||
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,net.minecraft.client.MinecraftClient,getInstance|getWindow" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="AutoUnboxing" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AwaitNotInLoop" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AwaitWithoutCorrespondingSignal" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="BadOddness" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="BigDecimalEquals" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="BigDecimalLegacyMethod" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="BooleanExpressionMayBeConditional" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CallToNativeMethodWhileLocked" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CallableParameterUseCaseInTypeContextInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CascadeStringReplacementInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="USE_SHORT_ARRAYS_SYNTAX" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="CastConflictsWithInstanceof" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CastToIncompatibleInterface" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ChainedEquality" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ChannelResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="insideTryAllowed" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ClassIndependentOfModule" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ClassLoaderInstantiation" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ClassMayBeInterface" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="reportClassesWithNonAbstractMethods" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ClassMethodNameMatchesFieldNameInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="ClassNameDiffersFromFileName" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ClassNewInstance" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ClassOnlyUsedInOneModule" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ClassOnlyUsedInOnePackage" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ClassOverridesFieldOfSuperClassInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="REPORT_PRIVATE_REDEFINITION" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ClassUnconnectedToPackage" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ClassWithOnlyPrivateConstructors" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CloneableClassInSecureContext" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CollectionContainsUrl" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CollectionsFieldAccessReplaceableByMethodCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ComparableImplementedButEqualsNotOverridden" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ComparatorNotSerializable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CompareToUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ComposeMissingKeys" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="ConditionSignal" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ConditionalExpression" enabled="true" level="INFORMATION" enabled_by_default="true">
|
||||
<option name="ignoreSimpleAssignmentsAndReturns" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ConditionalExpressionWithIdenticalBranchesJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ConfusingElse" enabled="false" level="WEAK WARNING" enabled_by_default="false">
|
||||
<option name="reportWhenNoStatementFollow" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ConfusingMainMethod" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ConfusingOctalEscape" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ConfusingPlusesOrMinusesJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ConstantJUnitAssertArgument" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ConstantMathCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ConstantTestNGAssertArgument" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ControlFlowStatementWithoutBraces" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ConvertJavadoc" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ConvertLambdaToReference" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ConvertOldAnnotations" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CssConvertColorToHexInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CssConvertColorToRgbInspection" enabled="true" level="INFORMATION" enabled_by_default="true" />
|
||||
<inspection_tool class="CssMissingSemicolon" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CssReplaceWithShorthandUnsafely" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CustomClassloader" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CustomSecurityManager" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CyclicClassDependency" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CyclicPackageDependency" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="DateToString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="DefaultNotLastCaseInSwitch" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="DisallowWritingIntoStaticPropertiesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="DisconnectedForeachInstructionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="DisjointPackage" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="DivideByZeroJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="DocumentWriteJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="DoubleBraceInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="DoubleCheckedLocking" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreOnVolatileVariables" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="DriverManagerGetConnection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="DuplicateBooleanBranch" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="DuplicateConditionJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="DynamicallyGeneratedCodeJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ES6ConvertIndexedForToForOf" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ES6ConvertLetToConst" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ES6ConvertToForOf" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ES6ShorthandObjectProperty" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ES6TopLevelAwaitExpression" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="EmptyDirectory" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="EmptyStatementBody" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_reportEmptyBlocks" value="true" />
|
||||
<option name="commentsAreContent" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="EmptySynchronizedStatement" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="EnumSwitchStatementWhichMissesCases" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="ignoreSwitchStatementsWithDefault" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="EqualsCalledOnEnumConstant" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="EqualsHashCodeCalledOnUrl" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="EqualsUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ErrorRethrown" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ExceptionNameDoesntEndWithException" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ExceptionPackage" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ExpectedExceptionNeverThrownTestNG" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ExplicitArgumentCanBeLambda" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ExtendsThread" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ExtendsThrowable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ExternalizableWithSerializationMethods" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="FallthruInSwitchStatement" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="FieldAccessedSynchronizedAndUnsynchronized" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="countGettersAndSetters" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="FieldDeclarationSideOnly" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="FieldHidesSuperclassField" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_ignoreInvisibleFields" value="false" />
|
||||
<option name="ignoreStaticFields" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="FieldMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="Finalize" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreTrivialFinalizers" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="FinalizeNotProtected" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="FixedTimeStartWithInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="FloatingPointEquality" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="FoldInitializerAndIfToElvis" enabled="false" level="INFO" enabled_by_default="false" />
|
||||
<inspection_tool class="ForLoopThatDoesntUseLoopVariableJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ForgottenDebugOutputInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="configuration">
|
||||
<list>
|
||||
<option value="\Codeception\Util\Debug::debug" />
|
||||
<option value="\Codeception\Util\Debug::pause" />
|
||||
<option value="\Doctrine\Common\Util\Debug::dump" />
|
||||
<option value="\Doctrine\Common\Util\Debug::export" />
|
||||
<option value="\Illuminate\Support\Debug\Dumper::dump" />
|
||||
<option value="\Symfony\Component\Debug\Debug::enable" />
|
||||
<option value="\Symfony\Component\Debug\DebugClassLoader::enable" />
|
||||
<option value="\Symfony\Component\Debug\ErrorHandler::register" />
|
||||
<option value="\Symfony\Component\Debug\ExceptionHandler::register" />
|
||||
<option value="\TYPO3\CMS\Core\Utility\DebugUtility::debug" />
|
||||
<option value="\Zend\Debug\Debug::dump" />
|
||||
<option value="\Zend\Di\Display\Console::export" />
|
||||
<option value="dd" />
|
||||
<option value="debug_print_backtrace" />
|
||||
<option value="debug_zval_dump" />
|
||||
<option value="dpm" />
|
||||
<option value="dpq" />
|
||||
<option value="dsm" />
|
||||
<option value="dump" />
|
||||
<option value="dvm" />
|
||||
<option value="error_log" />
|
||||
<option value="kpr" />
|
||||
<option value="phpinfo" />
|
||||
<option value="print_r" />
|
||||
<option value="var_dump" />
|
||||
<option value="var_export" />
|
||||
<option value="wp_die" />
|
||||
<option value="xdebug_break" />
|
||||
<option value="xdebug_call_class" />
|
||||
<option value="xdebug_call_file" />
|
||||
<option value="xdebug_call_function" />
|
||||
<option value="xdebug_call_line" />
|
||||
<option value="xdebug_code_coverage_started" />
|
||||
<option value="xdebug_debug_zval" />
|
||||
<option value="xdebug_debug_zval_stdout" />
|
||||
<option value="xdebug_dump_superglobals" />
|
||||
<option value="xdebug_enable" />
|
||||
<option value="xdebug_get_code_coverage" />
|
||||
<option value="xdebug_get_collected_errors" />
|
||||
<option value="xdebug_get_declared_vars" />
|
||||
<option value="xdebug_get_function_stack" />
|
||||
<option value="xdebug_get_headers" />
|
||||
<option value="xdebug_get_monitored_functions" />
|
||||
<option value="xdebug_get_profiler_filename" />
|
||||
<option value="xdebug_get_stack_depth" />
|
||||
<option value="xdebug_get_tracefile_name" />
|
||||
<option value="xdebug_is_enabled" />
|
||||
<option value="xdebug_memory_usage" />
|
||||
<option value="xdebug_peak_memory_usage" />
|
||||
<option value="xdebug_print_function_stack" />
|
||||
<option value="xdebug_start_code_coverage" />
|
||||
<option value="xdebug_start_error_collection" />
|
||||
<option value="xdebug_start_function_monitor" />
|
||||
<option value="xdebug_start_trace" />
|
||||
<option value="xdebug_stop_code_coverage" />
|
||||
<option value="xdebug_stop_error_collection" />
|
||||
<option value="xdebug_stop_function_monitor" />
|
||||
<option value="xdebug_stop_trace" />
|
||||
<option value="xdebug_time_index" />
|
||||
<option value="xdebug_var_dump" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="migratedIntoUserSpace" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="FunctionNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_regex" value="[a-z][A-Za-z]*" />
|
||||
<option name="m_minLength" value="0" />
|
||||
<option name="m_maxLength" value="99" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="FunctionWithInconsistentReturnsJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="HashCodeUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="HibernateResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="insideTryAllowed" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="HtmlMissingClosingTag" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="HtmlPresentationalElement" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="HtmlRequiredTitleAttribute" enabled="true" level="INFORMATION" enabled_by_default="true" />
|
||||
<inspection_tool class="IOResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredTypesString" value="java.io.ByteArrayOutputStream,java.io.ByteArrayInputStream,java.io.StringBufferInputStream,java.io.CharArrayWriter,java.io.CharArrayReader,java.io.StringWriter,java.io.StringReader" />
|
||||
<option name="insideTryAllowed" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="IfStatementWithIdenticalBranchesJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ImplicitDefaultCharsetUsage" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="InconsistentLanguageLevel" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="IncrementDecrementOperationEquivalentInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="InnerClassOnInterface" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_ignoreInnerInterfaces" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="InnerClassReferencedViaSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="InnerClassVariableHidesOuterClassVariable" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_ignoreInvisibleFields" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="InsertLiteralUnderscores" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="InstanceofCatchParameter" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="InstanceofIncompatibleInterface" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="InstanceofThis" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="InterfaceMayBeAnnotatedFunctional" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="IsEmptyFunctionUsageInspection" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="SUGGEST_TO_USE_COUNT_CHECK" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="IsNullFunctionUsageInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="IteratorNextDoesNotThrowNoSuchElementException" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JDBCExecuteWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JDBCPrepareStatementWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JDBCResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="insideTryAllowed" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="JNDIResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="insideTryAllowed" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="JSArrowFunctionBracesCanBeRemoved" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JSClassNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_minLength" value="0" />
|
||||
<option name="m_maxLength" value="99" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="JSConstructorReturnsPrimitive" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JSEqualityComparisonWithCoercion.TS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="mySeverity" value="Always" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="JSJoinVariableDeclarationAndAssignment" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JSMissingSwitchBranches" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JSNonASCIINames" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myAllowOnlyAscii" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="JSNonStrictModeUsed" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JSOctalInteger" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="myReportNonStrictEs5" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="JSUndeclaredVariable" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="myCheckGlobalDefinitions" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="JSUnusedGlobalSymbols" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myReportUnusedDefinitions" value="true" />
|
||||
<option name="myReportUnusedProperties" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="JUnitDatapoint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JUnitRule" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JUnitTestNG" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JavadocHtmlLint" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="JoinDeclarationAndAssignmentJava" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="LambdaCanBeMethodCall" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="LengthOneStringInIndexOf" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="LengthOneStringsInConcatenation" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="LoadLibraryWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="REPORT_VARIABLES" value="true" />
|
||||
<option name="REPORT_PARAMETERS" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="LocalVariableDeclarationSideOnly" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="LocalVariableNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_regex" value="[a-z][A-Za-z]*" />
|
||||
<option name="m_minLength" value="0" />
|
||||
<option name="m_maxLength" value="99" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="MalformedSetUpTearDown" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="MapReplaceableByEnumMap" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="MethodCallSideOnly" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="MethodMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_onlyPrivateOrFinal" value="false" />
|
||||
<option name="m_ignoreEmptyMethods" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="MethodMayBeSynchronized" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="MethodOverloadsParentMethod" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="reportIncompatibleParameters" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="MethodOverridesInaccessibleMethodOfSuper" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="MethodOverridesStaticMethod" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="MethodSideOnly" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="MisorderedAssertEqualsArgumentsTestNG" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="MisorderedAssertEqualsParameters" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="MisorderedModifiersInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="MissingOverrideAnnotation" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreObjectMethods" value="false" />
|
||||
<option name="ignoreAnonymousClassMethods" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="MissortedModifiers" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_requireAnnotationsFirst" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="MultipleTopLevelClassesInFile" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NakedNotify" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NegatedConditional" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_ignoreNegatedNullComparison" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="NestedAssignment" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NestedClassSideOnly" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="NestedConditionalExpressionJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NestedSwitchStatement" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NestedSynchronizedStatement" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NestedTernaryOperatorInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="NewExpressionSideOnly" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="NonBlockStatementBodyJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NonExceptionNameEndsWithException" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NonFinalClone" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NonFinalFieldInEnum" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NonFinalFieldOfException" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NonFinalUtilityClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NonReproducibleMathCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NonSerializableFieldInSerializableClass" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignorableAnnotations">
|
||||
<value />
|
||||
</option>
|
||||
<option name="ignoreAnonymousInnerClasses" value="false" />
|
||||
<option name="superClassString" value="java.awt.Component" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="NonSerializableObjectBoundToHttpSession" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NonSerializableObjectPassedToObjectStream" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NonSerializableWithSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NonSerializableWithSerializationMethods" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NonShortCircuitBoolean" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NonSynchronizedMethodOverridesSynchronizedMethod" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NonThreadSafeLazyInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NotOptimalIfConditionsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="REPORT_DUPLICATE_CONDITIONS" value="false" />
|
||||
<option name="SUGGEST_OPTIMIZING_CONDITIONS" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="NotifyCalledOnCondition" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NotifyWithoutCorrespondingWait" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NullThrown" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NumericToString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="OCInconsistentNaming" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ObjectAllocationIgnoredJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ObjectInstantiationInEqualsHashCode" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ObjectNotify" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ObjectToString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ObsoleteCollection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreRequiredObsoleteCollectionTypes" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="OctalAndDecimalIntegersMixed" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="OffsetOperationsInspection" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="OneTimeUseVariablesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ALLOW_LONG_STATEMENTS" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="OverloadedVarargsMethod" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PackageInMultipleModules" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ParameterHidingMemberVariable" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_ignoreInvisibleFields" value="true" />
|
||||
<option name="m_ignoreStaticMethodParametersHidingInstanceFields" value="true" />
|
||||
<option name="m_ignoreForConstructors" value="true" />
|
||||
<option name="m_ignoreForPropertySetters" value="true" />
|
||||
<option name="m_ignoreForAbstractMethods" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ParameterNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_regex" value="[a-z][A-Za-z]*" />
|
||||
<option name="m_minLength" value="0" />
|
||||
<option name="m_maxLength" value="99" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ParameterizedParametersStaticCollection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpAssignmentInConditionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpAssignmentReplaceableWithOperatorAssignmentInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpAssignmentReplaceableWithPrefixExpressionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpCSValidationInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false">
|
||||
<option name="EXTENSIONS" value="php,js,css,inc" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpClassNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_minLength" value="0" />
|
||||
<option name="m_maxLength" value="0" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpClosureCanBeConvertedToShortArrowFunctionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpCompoundNamespaceDepthInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpConstantNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_minLength" value="0" />
|
||||
<option name="m_maxLength" value="0" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpConstantReassignmentInspection" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpDivisionByZeroInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpDocMissingThrowsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="SKIP_ON_EMPTY_PHPDOC" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpFunctionNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_minLength" value="0" />
|
||||
<option name="m_maxLength" value="0" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpInconsistentReturnPointsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ALLOW_RETURN_NULL_IN_VOID" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpLongTypeFormInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpLoopCanBeConvertedToArrayFillInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpLoopCanBeConvertedToArrayFilterInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpLoopCanBeConvertedToArrayMapInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpMethodNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_minLength" value="0" />
|
||||
<option name="m_maxLength" value="0" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpMethodOrClassCallIsNotCaseSensitiveInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpMissingParentCallMagicInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpMissingReturnTypeInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpMissingStrictTypesDeclarationInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpMissingVisibilityInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpNewClassMissingParameterListInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpNonCanonicalElementsOrderInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpOverridingMethodVisibilityInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpPropertyNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_regex" value="[a-z][_a-z\d]*" />
|
||||
<option name="m_minLength" value="0" />
|
||||
<option name="m_maxLength" value="0" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpRedundantClosingTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpSeparateElseIfInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpShortOpenTagInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpSingleStatementWithBracesInspection" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpStatementHasEmptyBodyInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myCommentsCountAsContent" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpStatementWithoutBracesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpStaticAsDynamicMethodCallInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="SHOW_FOR_MAGIC" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpTraditionalSyntaxArrayLiteralInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpTraitsUseListInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpUndefinedCallbackInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpUndefinedClassConstantInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="DOWNGRADE_SEVERITY" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpUndefinedMethodInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="DOWNGRADE_SEVERITY" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpUnused" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="SHOW_UNUSED_BY_ENTRIES" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpUnusedParameterInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="DONT_REPORT_ANONYMOUS" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpVarUsageInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PhpVariableNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_minLength" value="0" />
|
||||
<option name="m_maxLength" value="0" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PhpVariableVariableInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PointlessBitwiseExpressionJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_ignoreExpressionsContainingConstants" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ProblematicVarargsMethodOverride" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PropertyCanBeStaticInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ProtectedMemberInFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PublicFieldAccessedInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PublicStaticArrayField" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PublicStaticCollectionField" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyAugmentAssignmentInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyClassicStyleClassInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyMissingTypeHintsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="m_onlyWhenTypesAreKnown" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="RawTypeCanBeGeneric" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ReadObjectAndWriteObjectPrivate" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ReadObjectInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ReadResolveAndWriteReplaceProtected" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="RedundantElseClauseInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="RedundantImplements" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreSerializable" value="true" />
|
||||
<option name="ignoreCloneable" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="RedundantObjectTypeCheck" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="RedundantSuppression" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="IGNORE_ALL" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ReferencingObjectsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="RegExpOctalEscape" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ReplaceAssignmentWithOperatorAssignment" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreLazyOperators" value="true" />
|
||||
<option name="ignoreObscureOperators" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ReplaceAssignmentWithOperatorAssignmentJS" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ReplaceCollectionCountWithSize" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ReplaceGuardClauseWithFunctionCall" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ReplaceStringFormatWithLiteral" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ReplaceSubstringWithDropLast" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ReplaceSubstringWithIndexingOperation" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ReplaceSubstringWithSubstringAfter" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ReplaceSubstringWithSubstringBefore" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ReplaceSubstringWithTake" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ResultOfObjectAllocationIgnored" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ResultSetIndexZero" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ReturnOfInnerClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="RsSimplifyBooleanExpression" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="RuntimeExec" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="RuntimeExecWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SafeLock" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SecurityAdvisoriesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="optionConfiguration">
|
||||
<list>
|
||||
<option value="barryvdh/laravel-debugbar" />
|
||||
<option value="behat/behat" />
|
||||
<option value="brianium/paratest" />
|
||||
<option value="codeception/codeception" />
|
||||
<option value="codedungeon/phpunit-result-printer" />
|
||||
<option value="composer/composer" />
|
||||
<option value="doctrine/coding-standard" />
|
||||
<option value="filp/whoops" />
|
||||
<option value="friendsofphp/php-cs-fixer" />
|
||||
<option value="humbug/humbug" />
|
||||
<option value="infection/infection" />
|
||||
<option value="jakub-onderka/php-parallel-lint" />
|
||||
<option value="johnkary/phpunit-speedtrap" />
|
||||
<option value="kalessil/production-dependencies-guard" />
|
||||
<option value="mikey179/vfsStream" />
|
||||
<option value="mockery/mockery" />
|
||||
<option value="mybuilder/phpunit-accelerator" />
|
||||
<option value="orchestra/testbench" />
|
||||
<option value="pdepend/pdepend" />
|
||||
<option value="phan/phan" />
|
||||
<option value="phing/phing" />
|
||||
<option value="phpcompatibility/php-compatibility" />
|
||||
<option value="phpmd/phpmd" />
|
||||
<option value="phpro/grumphp" />
|
||||
<option value="phpspec/phpspec" />
|
||||
<option value="phpspec/prophecy" />
|
||||
<option value="phpstan/phpstan" />
|
||||
<option value="phpunit/phpunit" />
|
||||
<option value="povils/phpmnd" />
|
||||
<option value="roave/security-advisories" />
|
||||
<option value="satooshi/php-coveralls" />
|
||||
<option value="sebastian/phpcpd" />
|
||||
<option value="slevomat/coding-standard" />
|
||||
<option value="spatie/phpunit-watcher" />
|
||||
<option value="squizlabs/php_codesniffer" />
|
||||
<option value="sstalle/php7cc" />
|
||||
<option value="symfony/debug" />
|
||||
<option value="symfony/maker-bundle" />
|
||||
<option value="symfony/phpunit-bridge" />
|
||||
<option value="symfony/var-dumper" />
|
||||
<option value="vimeo/psalm" />
|
||||
<option value="wimg/php-compatibility" />
|
||||
<option value="wp-coding-standards/wpcs" />
|
||||
<option value="yiisoft/yii2-coding-standards" />
|
||||
<option value="yiisoft/yii2-debug" />
|
||||
<option value="yiisoft/yii2-gii" />
|
||||
<option value="zendframework/zend-coding-standard" />
|
||||
<option value="zendframework/zend-debug" />
|
||||
<option value="zendframework/zend-test" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SerialPersistentFieldsWithWrongSignature" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SerialVersionUIDNotStaticFinal" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SerializableHasSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreAnonymousInnerClasses" value="false" />
|
||||
<option name="superClassString" value="java.awt.Component" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SerializableHasSerializationMethods" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreAnonymousInnerClasses" value="false" />
|
||||
<option name="superClassString" value="java.awt.Component" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SerializableInnerClassHasSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreAnonymousInnerClasses" value="false" />
|
||||
<option name="superClassString" value="java.awt.Component" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SerializableInnerClassWithNonSerializableOuterClass" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreAnonymousInnerClasses" value="false" />
|
||||
<option name="superClassString" value="java.awt.Component" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SerializableStoresNonSerializable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SerializableWithUnconstructableAncestor" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SetReplaceableByEnumSet" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SeveralTargetsMessage" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SharedThreadLocalRandom" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ShortEchoTagCanBeUsedInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SignalWithoutCorrespondingAwait" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SimpleDateFormatWithoutLocale" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SimplifiableAnnotation" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SimplifiableIfStatement" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SingleStatementInBlock" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="SizeReplaceableByIsEmpty" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SleepWhileHoldingLock" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SocketResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="insideTryAllowed" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SqlGotoInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SqlRedundantOrderingDirectionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlWithoutWhereInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myDontWarnForLimit" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="StrTrUsageAsStrReplaceInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="StringBufferToStringInConcatenation" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="StringConcatenationInFormatCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="StringConcatenationInMessageFormatCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="StringConcatenationMissingWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="StringEqualsEmptyString" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="SUPPRESS_FOR_VALUES_WHICH_COULD_BE_NULL" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="StringReplaceableByStringBuffer" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="onlyWarnOnLoop" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="StringToUpperWithoutLocale" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SubStrUsedAsStrPosInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SubtractionInCompareTo" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SuspiciousArrayCast" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SuspiciousIndentAfterControlStatement" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SuspiciousLiteralUnderscore" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SwitchStatementWithConfusingDeclaration" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SynchronizationOnStaticField" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SynchronizeOnLock" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SynchronizeOnThis" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SynchronizedMethod" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_includeNativeMethods" value="true" />
|
||||
<option name="ignoreSynchronizedSuperMethods" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SynchronizedOnLiteralObject" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SystemGC" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SystemGetenv" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SystemProperties" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SystemRunFinalizersOnExit" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SystemSetSecurityManager" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="TestCaseInProductCode" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="TestCaseWithConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="TestCaseWithNoTestMethods" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreSupers" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="TestMethodInProductCode" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="TestMethodWithoutAssertion" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="TextLabelInSwitchStatementJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ThreadLocalNotStaticFinal" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ThreadPriority" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ThreadStartInConstruction" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ThreadStopSuspendResume" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ThreadYield" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ThrowCaughtLocally" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreRethrownExceptions" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ThrowRawExceptionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="TimeToString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="TooBroadScope" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="m_allowConstructorAsInitializer" value="false" />
|
||||
<option name="m_onlyLookAtBlocks" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="TransientFieldInNonSerializableClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="TransientFieldNotInitialized" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="TrivialStringConcatenation" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnNecessaryDoubleQuotesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnaryPlus" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnclearBinaryExpression" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="UnconditionalWait" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnconstrainedVariableType" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UndeclaredTests" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnknownInspectionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="UnnecessarilyQualifiedStaticUsage" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_ignoreStaticFieldAccesses" value="false" />
|
||||
<option name="m_ignoreStaticMethodCalls" value="false" />
|
||||
<option name="m_ignoreStaticAccessFromStaticContext" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="UnnecessarilyQualifiedStaticallyImportedElement" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnnecessaryBoxing" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="onlyReportSuperfluouslyBoxed" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="UnnecessaryConstantArrayCreationExpression" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnnecessaryConstructor" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreAnnotations" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="UnnecessaryLocalVariable" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_ignoreImmediatelyReturnedVariables" value="true" />
|
||||
<option name="m_ignoreAnnotatedVariables" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="UnnecessaryLocalVariableJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_ignoreImmediatelyReturnedVariables" value="true" />
|
||||
<option name="m_ignoreAnnotatedVariables" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="UnnecessaryParentheses" enabled="true" level="INFORMATION" enabled_by_default="true">
|
||||
<option name="ignoreClarifyingParentheses" value="true" />
|
||||
<option name="ignoreParenthesesOnConditionals" value="false" />
|
||||
<option name="ignoreParenthesesOnLambdaParameter" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="UnnecessaryToStringCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnnecessaryUnaryMinus" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnnecessaryUnboxing" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="onlyReportSuperfluouslyUnboxed" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="UnsetConstructsCanBeMergedInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="UnterminatedStatementJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreSemicolonAtEndOfBlock" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="UnusedCatchParameterJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_ignoreCatchBlocksWithComments" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="UnusedProperty" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="UseOfAWTPeerClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UseOfJDBCDriverClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UseOfObsoleteAssert" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UseOfObsoleteDateTimeApi" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UseOfProcessBuilder" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UseOfPropertiesAsHashtable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UseOfSunClasses" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UsingInclusionReturnValueInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UtilityClassWithPublicConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UtilityClassWithoutPrivateConstructor" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignorableAnnotations">
|
||||
<value />
|
||||
</option>
|
||||
<option name="ignoreClassesWithOnlyMain" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="VariableNotUsedInsideIf" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="VariableUseSideOnly" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="VoidExpressionJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="VolatileArrayField" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="WaitCalledOnCondition" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="WaitNotInLoop" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="WaitNotifyNotInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="WaitOrAwaitWithoutTimeout" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="WaitWhileHoldingTwoLocks" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="WaitWithoutCorrespondingNotify" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="WhileLoopSpinsOnField" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreNonEmtpyLoops" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ZeroLengthArrayInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
6
.idea/.idea.TweetDuck/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/.idea.TweetDuck/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="PROJECT_PROFILE" value="Project" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
6
.idea/.idea.TweetDuck/.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/.idea.TweetDuck/.idea/jsLibraryMappings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<file url="PROJECT" libraries="{@types/jquery}" />
|
||||
</component>
|
||||
</project>
|
20
.idea/.idea.TweetDuck/.idea/runConfigurations/TweetDuck.xml
generated
Normal file
20
.idea/.idea.TweetDuck/.idea/runConfigurations/TweetDuck.xml
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="TweetDuck" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/bin/x86/Debug/TweetDuck.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="-datafolder TweetDuckDebug -nogdpr" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/bin/x86/Debug" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/TweetDuck.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="Console" />
|
||||
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.2" />
|
||||
<method v="2">
|
||||
<option name="Build" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
11
.idea/.idea.TweetDuck/.idea/vcs.xml
generated
Normal file
11
.idea/.idea.TweetDuck/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GitSharedSettings">
|
||||
<option name="FORCE_PUSH_PROHIBITED_PATTERNS">
|
||||
<list />
|
||||
</option>
|
||||
</component>
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@@ -1,58 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Application;
|
||||
|
||||
namespace TweetDuck.Application{
|
||||
class LockHandler : IAppLockHandler{
|
||||
private const int WaitRetryDelay = 250;
|
||||
private const int RestoreFailTimeout = 2000;
|
||||
private const int CloseNaturallyTimeout = 10000;
|
||||
private const int CloseKillTimeout = 5000;
|
||||
|
||||
bool IAppLockHandler.RestoreProcess(Process process){
|
||||
if (process.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
||||
NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)process.Id, 0);
|
||||
|
||||
if (WindowsUtils.TrySleepUntil(() => CheckProcessExited(process) || (process.MainWindowHandle != IntPtr.Zero && process.Responding), RestoreFailTimeout, WaitRetryDelay)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IAppLockHandler.CloseProcess(Process process){
|
||||
try{
|
||||
if (process.CloseMainWindow()){
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseNaturallyTimeout, WaitRetryDelay);
|
||||
}
|
||||
|
||||
if (!process.HasExited){
|
||||
process.Kill();
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseKillTimeout, WaitRetryDelay);
|
||||
}
|
||||
|
||||
if (process.HasExited){
|
||||
process.Dispose();
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
}catch(Exception ex) when (ex is InvalidOperationException || ex is Win32Exception){
|
||||
bool hasExited = CheckProcessExited(process);
|
||||
process.Dispose();
|
||||
return hasExited;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CheckProcessExited(Process process){
|
||||
process.Refresh();
|
||||
return process.HasExited;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,9 +1,21 @@
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using TweetLib.Core.Application;
|
||||
|
||||
namespace TweetDuck.Application {
|
||||
class SystemHandler : IAppSystemHandler {
|
||||
void IAppSystemHandler.OpenAssociatedProgram(string path) {
|
||||
try {
|
||||
using (Process.Start(new ProcessStartInfo {
|
||||
FileName = path,
|
||||
ErrorDialog = true
|
||||
})) {}
|
||||
} catch (Exception e) {
|
||||
Program.Reporter.HandleException("Error Opening Program", "Could not open the associated program for " + path, true, e);
|
||||
}
|
||||
}
|
||||
|
||||
void IAppSystemHandler.OpenFileExplorer(string path) {
|
||||
if (File.Exists(path)) {
|
||||
using (Process.Start("explorer.exe", "/select,\"" + path.Replace('/', '\\') + "\"")) {}
|
||||
|
@@ -1,6 +1,9 @@
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using CefSharp;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Browser;
|
||||
using TweetLib.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Browser.Adapters {
|
||||
sealed class CefScriptExecutor : IScriptExecutor {
|
||||
@@ -11,7 +14,7 @@ namespace TweetDuck.Browser.Adapters{
|
||||
}
|
||||
|
||||
public void RunFunction(string name, params object[] args) {
|
||||
browser.ExecuteScriptAsync(name, args);
|
||||
browser.ExecuteJsAsync(name, args);
|
||||
}
|
||||
|
||||
public void RunScript(string identifier, string script) {
|
||||
@@ -19,9 +22,9 @@ namespace TweetDuck.Browser.Adapters{
|
||||
RunScript(frame, script, identifier);
|
||||
}
|
||||
|
||||
public bool RunFile(string file){
|
||||
public void RunBootstrap(string moduleNamespace) {
|
||||
using IFrame frame = browser.GetMainFrame();
|
||||
return RunFile(frame, file);
|
||||
RunBootstrap(frame, moduleNamespace);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
@@ -32,10 +35,44 @@ namespace TweetDuck.Browser.Adapters{
|
||||
}
|
||||
}
|
||||
|
||||
public static bool RunFile(IFrame frame, string file){
|
||||
string script = Program.Resources.Load(file);
|
||||
RunScript(frame, script, "root:" + Path.GetFileNameWithoutExtension(file));
|
||||
return script != null;
|
||||
public static void RunBootstrap(IFrame frame, string moduleNamespace) {
|
||||
string script = GetBootstrapScript(moduleNamespace, includeStylesheets: true);
|
||||
|
||||
if (script != null) {
|
||||
RunScript(frame, script, "bootstrap");
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetBootstrapScript(string moduleNamespace, bool includeStylesheets) {
|
||||
string script = FileUtils.ReadFileOrNull(Path.Combine(Program.ResourcesPath, "bootstrap.js"));
|
||||
|
||||
if (script == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string path = Path.Combine(Program.ResourcesPath, moduleNamespace);
|
||||
var files = new DirectoryInfo(path).GetFiles();
|
||||
|
||||
var moduleNames = new List<string>();
|
||||
var stylesheetNames = new List<string>();
|
||||
|
||||
foreach (var file in files) {
|
||||
var ext = Path.GetExtension(file.Name);
|
||||
|
||||
var targetList = ext switch {
|
||||
".js" => moduleNames,
|
||||
".css" => includeStylesheets ? stylesheetNames : null,
|
||||
_ => null
|
||||
};
|
||||
|
||||
targetList?.Add(Path.GetFileNameWithoutExtension(file.Name));
|
||||
}
|
||||
|
||||
script = script.Replace("{{namespace}}", moduleNamespace);
|
||||
script = script.Replace("{{modules}}", string.Join("|", moduleNames));
|
||||
script = script.Replace("{{stylesheets}}", string.Join("|", stylesheetNames));
|
||||
|
||||
return script;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,8 @@ using TweetLib.Core.Utils;
|
||||
namespace TweetDuck.Browser.Bridge {
|
||||
static class PropertyBridge {
|
||||
public enum Environment {
|
||||
Browser, Notification
|
||||
Browser,
|
||||
Notification
|
||||
}
|
||||
|
||||
public static string GenerateScript(Environment environment) {
|
||||
|
@@ -23,7 +23,7 @@ namespace TweetDuck.Browser.Bridge{
|
||||
private readonly FormBrowser form;
|
||||
private readonly FormNotificationMain notification;
|
||||
|
||||
protected TweetDeckBridge(FormBrowser form, FormNotificationMain notification){
|
||||
private TweetDeckBridge(FormBrowser form, FormNotificationMain notification) {
|
||||
this.form = form;
|
||||
this.notification = notification;
|
||||
}
|
||||
@@ -33,6 +33,10 @@ namespace TweetDuck.Browser.Bridge{
|
||||
public sealed class Browser : TweetDeckBridge {
|
||||
public Browser(FormBrowser form, FormNotificationMain notification) : base(form, notification) {}
|
||||
|
||||
public void OnModulesLoaded(string moduleNamespace) {
|
||||
form.InvokeAsyncSafe(() => form.OnModulesLoaded(moduleNamespace));
|
||||
}
|
||||
|
||||
public void OpenContextMenu() {
|
||||
form.InvokeAsyncSafe(form.OpenContextMenu);
|
||||
}
|
||||
@@ -41,8 +45,8 @@ namespace TweetDuck.Browser.Bridge{
|
||||
form.InvokeAsyncSafe(form.OpenProfileImport);
|
||||
}
|
||||
|
||||
public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){
|
||||
form.InvokeAsyncSafe(() => form.OnIntroductionClosed(showGuide, allowDataCollection));
|
||||
public void OnIntroductionClosed(bool showGuide) {
|
||||
form.InvokeAsyncSafe(() => form.OnIntroductionClosed(showGuide));
|
||||
}
|
||||
|
||||
public void LoadNotificationLayout(string fontSize, string headLayout) {
|
||||
@@ -56,8 +60,8 @@ namespace TweetDuck.Browser.Bridge{
|
||||
ContextMenuBase.CurrentInfo.SetLink(type, url);
|
||||
}
|
||||
|
||||
public void SetRightClickedChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
|
||||
ContextMenuBase.CurrentInfo.SetChirp(tweetUrl, quoteUrl, chirpAuthors, chirpImages);
|
||||
public void SetRightClickedChirp(string columnId, string chirpId, string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages) {
|
||||
ContextMenuBase.CurrentInfo.SetChirp(columnId, chirpId, tweetUrl, quoteUrl, chirpAuthors, chirpImages);
|
||||
}
|
||||
|
||||
public void DisplayTooltip(string text) {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Controls;
|
||||
using TweetLib.Core.Features.Updates;
|
||||
using TweetLib.Core.Systems.Updates;
|
||||
|
||||
namespace TweetDuck.Browser.Bridge {
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
|
@@ -15,8 +15,8 @@ namespace TweetDuck.Browser.Data{
|
||||
link = string.IsNullOrEmpty(url) ? null : new LinkInfo(type, url);
|
||||
}
|
||||
|
||||
public void SetChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
|
||||
chirp = string.IsNullOrEmpty(tweetUrl) ? (ChirpInfo?)null : new ChirpInfo(tweetUrl, quoteUrl, chirpAuthors, chirpImages);
|
||||
public void SetChirp(string columnId, string chirpId, string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages) {
|
||||
chirp = string.IsNullOrEmpty(tweetUrl) ? (ChirpInfo?) null : new ChirpInfo(columnId, chirpId, tweetUrl, quoteUrl, chirpAuthors, chirpImages);
|
||||
}
|
||||
|
||||
public ContextData Reset() {
|
||||
@@ -52,7 +52,10 @@ namespace TweetDuck.Browser.Data{
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChirpInfo{
|
||||
public readonly struct ChirpInfo {
|
||||
public string ColumnId { get; }
|
||||
public string ChirpId { get; }
|
||||
|
||||
public string TweetUrl { get; }
|
||||
public string QuoteUrl { get; }
|
||||
|
||||
@@ -62,7 +65,9 @@ namespace TweetDuck.Browser.Data{
|
||||
private readonly string chirpAuthors;
|
||||
private readonly string chirpImages;
|
||||
|
||||
public ChirpInfo(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
|
||||
public ChirpInfo(string columnId, string chirpId, string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages) {
|
||||
this.ColumnId = columnId;
|
||||
this.ChirpId = chirpId;
|
||||
this.TweetUrl = tweetUrl;
|
||||
this.QuoteUrl = quoteUrl;
|
||||
this.chirpAuthors = chirpAuthors;
|
||||
@@ -92,7 +97,7 @@ namespace TweetDuck.Browser.Data{
|
||||
|
||||
public ChirpInfo Chirp { get; }
|
||||
|
||||
public ContextData(ContextType types, string linkUrl, string unsafeLinkUrl, string mediaUrl, ChirpInfo chirp){
|
||||
private ContextData(ContextType types, string linkUrl, string unsafeLinkUrl, string mediaUrl, ChirpInfo chirp) {
|
||||
Types = types;
|
||||
LinkUrl = linkUrl;
|
||||
UnsafeLinkUrl = unsafeLinkUrl;
|
||||
|
50
Browser/Data/ResourceHandlers.cs
Normal file
50
Browser/Data/ResourceHandlers.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using CefSharp;
|
||||
|
||||
namespace TweetDuck.Browser.Data {
|
||||
sealed class ResourceHandlers {
|
||||
private readonly ConcurrentDictionary<string, Func<IResourceHandler>> handlers = new ConcurrentDictionary<string, Func<IResourceHandler>>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public bool HasHandler(IRequest request) {
|
||||
return handlers.ContainsKey(request.Url);
|
||||
}
|
||||
|
||||
public IResourceHandler GetHandler(IRequest request) {
|
||||
return handlers.TryGetValue(request.Url, out var factory) ? factory() : null;
|
||||
}
|
||||
|
||||
public bool Register(string url, Func<IResourceHandler> factory) {
|
||||
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)) {
|
||||
handlers.AddOrUpdate(uri.AbsoluteUri, factory, (key, prev) => factory);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Register(ResourceLink link) {
|
||||
return Register(link.Url, link.Factory);
|
||||
}
|
||||
|
||||
public bool Unregister(string url) {
|
||||
return handlers.TryRemove(url, out _);
|
||||
}
|
||||
|
||||
public bool Unregister(ResourceLink link) {
|
||||
return Unregister(link.Url);
|
||||
}
|
||||
|
||||
public static Func<IResourceHandler> ForString(string str) {
|
||||
return () => ResourceHandler.FromString(str);
|
||||
}
|
||||
|
||||
public static Func<IResourceHandler> ForString(string str, string mimeType) {
|
||||
return () => ResourceHandler.FromString(str, mimeType: mimeType);
|
||||
}
|
||||
|
||||
public static Func<IResourceHandler> ForBytes(byte[] bytes, string mimeType) {
|
||||
return () => ResourceHandler.FromByteArray(bytes, mimeType);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,13 +1,14 @@
|
||||
using CefSharp;
|
||||
using System;
|
||||
using CefSharp;
|
||||
|
||||
namespace TweetDuck.Browser.Data {
|
||||
sealed class ResourceLink {
|
||||
public string Url { get; }
|
||||
public IResourceHandler Handler { get; }
|
||||
public Func<IResourceHandler> Factory { get; }
|
||||
|
||||
public ResourceLink(string url, IResourceHandler handler){
|
||||
public ResourceLink(string url, Func<IResourceHandler> factory) {
|
||||
this.Url = url;
|
||||
this.Handler = handler;
|
||||
this.Factory = factory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,15 +15,16 @@ using TweetDuck.Controls;
|
||||
using TweetDuck.Dialogs;
|
||||
using TweetDuck.Dialogs.Settings;
|
||||
using TweetDuck.Management;
|
||||
using TweetDuck.Management.Analytics;
|
||||
using TweetDuck.Plugins;
|
||||
using TweetDuck.Resources;
|
||||
using TweetDuck.Updates;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
using TweetLib.Core.Features.Plugins.Events;
|
||||
using TweetLib.Core.Features.Updates;
|
||||
using TweetLib.Core.Systems.Updates;
|
||||
|
||||
namespace TweetDuck.Browser {
|
||||
sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{
|
||||
sealed partial class FormBrowser : Form {
|
||||
private static UserConfig Config => Program.Config.User;
|
||||
|
||||
public bool IsWaiting {
|
||||
@@ -43,16 +44,15 @@ namespace TweetDuck.Browser{
|
||||
}
|
||||
}
|
||||
|
||||
public string UpdateInstallerPath { get; private set; }
|
||||
public UpdateInstaller UpdateInstaller { get; private set; }
|
||||
private bool ignoreUpdateCheckError;
|
||||
|
||||
public AnalyticsFile AnalyticsFile => analytics?.File ?? AnalyticsFile.Dummy;
|
||||
|
||||
#pragma warning disable IDE0069 // Disposable fields should be disposed
|
||||
private readonly TweetDeckBrowser browser;
|
||||
private readonly FormNotificationTweet notification;
|
||||
#pragma warning restore IDE0069 // Disposable fields should be disposed
|
||||
|
||||
private readonly ResourceProvider resourceProvider;
|
||||
private readonly PluginManager plugins;
|
||||
private readonly UpdateHandler updates;
|
||||
private readonly ContextMenu contextMenu;
|
||||
@@ -63,17 +63,19 @@ namespace TweetDuck.Browser{
|
||||
|
||||
private TweetScreenshotManager notificationScreenshotManager;
|
||||
private VideoPlayer videoPlayer;
|
||||
private AnalyticsManager analytics;
|
||||
|
||||
public FormBrowser(){
|
||||
public FormBrowser(ResourceProvider resourceProvider, PluginSchemeFactory pluginScheme) {
|
||||
InitializeComponent();
|
||||
|
||||
Text = Program.BrandName;
|
||||
|
||||
this.resourceProvider = resourceProvider;
|
||||
|
||||
this.plugins = new PluginManager(Program.Config.Plugins, Program.PluginPath, Program.PluginDataPath);
|
||||
this.plugins.Reloaded += plugins_Reloaded;
|
||||
this.plugins.Executed += plugins_Executed;
|
||||
this.plugins.Reload();
|
||||
pluginScheme.Setup(plugins);
|
||||
|
||||
this.notification = new FormNotificationTweet(this, plugins);
|
||||
this.notification.Show();
|
||||
@@ -108,10 +110,6 @@ namespace TweetDuck.Browser{
|
||||
UpdateFormIcon();
|
||||
}
|
||||
|
||||
if (Config.AllowDataCollection){
|
||||
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
|
||||
}
|
||||
|
||||
RestoreWindow();
|
||||
}
|
||||
|
||||
@@ -124,7 +122,6 @@ namespace TweetDuck.Browser{
|
||||
|
||||
notificationScreenshotManager?.Dispose();
|
||||
videoPlayer?.Dispose();
|
||||
analytics?.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
@@ -144,6 +141,8 @@ namespace TweetDuck.Browser{
|
||||
|
||||
private void RestoreWindow() {
|
||||
Config.BrowserWindow.Restore(this, true);
|
||||
browser.PrepareSize(ClientSize);
|
||||
|
||||
prevState = WindowState;
|
||||
isLoaded = true;
|
||||
}
|
||||
@@ -212,6 +211,7 @@ namespace TweetDuck.Browser{
|
||||
}
|
||||
|
||||
timerResize.Stop();
|
||||
browser.PrepareSize(ClientSize); // needed to pre-size browser control when launched in maximized state
|
||||
|
||||
if (Location != ControlExtensions.InvisibleLocation) {
|
||||
Config.BrowserWindow.Save(this);
|
||||
@@ -231,14 +231,13 @@ namespace TweetDuck.Browser{
|
||||
}
|
||||
|
||||
private void FormBrowser_FormClosed(object sender, FormClosedEventArgs e) {
|
||||
if (isLoaded && UpdateInstallerPath == null){
|
||||
if (isLoaded && UpdateInstaller == null) {
|
||||
updateBridge.Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
private void Config_MuteToggled(object sender, EventArgs e) {
|
||||
UpdateFormIcon();
|
||||
AnalyticsFile.NotificationMutes.Trigger();
|
||||
}
|
||||
|
||||
private void Config_TrayBehaviorChanged(object sender, EventArgs e) {
|
||||
@@ -258,9 +257,7 @@ namespace TweetDuck.Browser{
|
||||
|
||||
private void plugins_Reloaded(object sender, PluginErrorEventArgs e) {
|
||||
if (e.HasErrors) {
|
||||
this.InvokeAsyncSafe(() => { // TODO not needed but makes code consistent...
|
||||
FormMessage.Error("Error Loading Plugins", "The following plugins will not be available until the issues are resolved:\n\n" + string.Join("\n\n", e.Errors), FormMessage.OK);
|
||||
});
|
||||
}
|
||||
|
||||
if (isLoaded) {
|
||||
@@ -270,9 +267,7 @@ namespace TweetDuck.Browser{
|
||||
|
||||
private void plugins_Executed(object sender, PluginErrorEventArgs e) {
|
||||
if (e.HasErrors) {
|
||||
this.InvokeAsyncSafe(() => {
|
||||
FormMessage.Error("Error Executing Plugins", "Failed to execute the following plugins:\n\n" + string.Join("\n\n", e.Errors), FormMessage.OK);
|
||||
});
|
||||
this.InvokeAsyncSafe(() => { FormMessage.Error("Error Executing Plugins", "Failed to execute the following plugins:\n\n" + string.Join("\n\n", e.Errors), FormMessage.OK); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +304,7 @@ namespace TweetDuck.Browser{
|
||||
UpdateDownloadStatus status = update.DownloadStatus;
|
||||
|
||||
if (status == UpdateDownloadStatus.Done) {
|
||||
UpdateInstallerPath = update.InstallerPath;
|
||||
UpdateInstaller = new UpdateInstaller(update.InstallerPath);
|
||||
ForceClose();
|
||||
}
|
||||
else if (status != UpdateDownloadStatus.Canceled && FormMessage.Error("Update Has Failed", "Could not automatically download the update: " + (update.DownloadError?.Message ?? "unknown error") + "\n\nWould you like to open the website and try downloading the update manually?", FormMessage.Yes, FormMessage.No)) {
|
||||
@@ -367,7 +362,6 @@ namespace TweetDuck.Browser{
|
||||
}
|
||||
else {
|
||||
browser.OnMouseClickExtra(m.WParam);
|
||||
AnalyticsFile.BrowserExtraMouseButtons.Trigger();
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -378,6 +372,10 @@ namespace TweetDuck.Browser{
|
||||
|
||||
// bridge methods
|
||||
|
||||
public void OnModulesLoaded(string moduleNamespace) {
|
||||
browser.OnModulesLoaded(moduleNamespace);
|
||||
}
|
||||
|
||||
public void PauseNotification() {
|
||||
notification.PauseNotification();
|
||||
}
|
||||
@@ -391,18 +389,25 @@ namespace TweetDuck.Browser{
|
||||
}
|
||||
|
||||
public void ReloadToTweetDeck() {
|
||||
Program.Resources.OnReloadTriggered();
|
||||
#if DEBUG
|
||||
ResourceHotSwap.Run();
|
||||
resourceProvider.ClearCache();
|
||||
#else
|
||||
if (ModifierKeys.HasFlag(Keys.Shift)) {
|
||||
resourceProvider.ClearCache();
|
||||
}
|
||||
#endif
|
||||
|
||||
ignoreUpdateCheckError = false;
|
||||
browser.ReloadToTweetDeck();
|
||||
AnalyticsFile.BrowserReloads.Trigger();
|
||||
}
|
||||
|
||||
public void AddSearchColumn(string query) {
|
||||
browser.AddSearchColumn(query);
|
||||
}
|
||||
|
||||
public void TriggerTweetScreenshot(){
|
||||
browser.TriggerTweetScreenshot();
|
||||
public void TriggerTweetScreenshot(string columnId, string chirpId) {
|
||||
browser.TriggerTweetScreenshot(columnId, chirpId);
|
||||
}
|
||||
|
||||
public void ReloadColumns() {
|
||||
@@ -415,7 +420,6 @@ namespace TweetDuck.Browser{
|
||||
|
||||
public void ApplyROT13() {
|
||||
browser.ApplyROT13();
|
||||
AnalyticsFile.UsedROT13.Trigger();
|
||||
}
|
||||
|
||||
public void OpenDevTools() {
|
||||
@@ -424,15 +428,10 @@ namespace TweetDuck.Browser{
|
||||
|
||||
// callback handlers
|
||||
|
||||
public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){
|
||||
public void OnIntroductionClosed(bool showGuide) {
|
||||
if (Config.FirstRun) {
|
||||
Config.FirstRun = false;
|
||||
Config.AllowDataCollection = allowDataCollection;
|
||||
Config.Save();
|
||||
|
||||
if (allowDataCollection && analytics == null){
|
||||
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (showGuide) {
|
||||
@@ -452,7 +451,7 @@ namespace TweetDuck.Browser{
|
||||
if (!FormManager.TryBringToFront<FormSettings>()) {
|
||||
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
|
||||
|
||||
FormSettings form = new FormSettings(this, plugins, updates, analytics, startTab);
|
||||
FormSettings form = new FormSettings(this, plugins, updates, startTab);
|
||||
|
||||
form.FormClosed += (sender, args) => {
|
||||
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck) {
|
||||
@@ -466,16 +465,6 @@ namespace TweetDuck.Browser{
|
||||
trayIcon.HasNotifications = false;
|
||||
}
|
||||
|
||||
if (Config.AllowDataCollection){
|
||||
if (analytics == null){
|
||||
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
|
||||
}
|
||||
}
|
||||
else if (analytics != null){
|
||||
analytics.Dispose();
|
||||
analytics = null;
|
||||
}
|
||||
|
||||
BrowserCache.RefreshTimer();
|
||||
|
||||
if (form.ShouldReloadBrowser) {
|
||||
@@ -490,21 +479,18 @@ namespace TweetDuck.Browser{
|
||||
form.Dispose();
|
||||
};
|
||||
|
||||
AnalyticsFile.OpenOptions.Trigger();
|
||||
ShowChildForm(form);
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenAbout() {
|
||||
if (!FormManager.TryBringToFront<FormAbout>()) {
|
||||
AnalyticsFile.OpenAbout.Trigger();
|
||||
ShowChildForm(new FormAbout());
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenPlugins() {
|
||||
if (!FormManager.TryBringToFront<FormPlugins>()) {
|
||||
AnalyticsFile.OpenPlugins.Trigger();
|
||||
ShowChildForm(new FormPlugins(plugins));
|
||||
}
|
||||
}
|
||||
@@ -527,9 +513,7 @@ namespace TweetDuck.Browser{
|
||||
}
|
||||
}
|
||||
|
||||
public void OnTweetSound(){
|
||||
AnalyticsFile.SoundNotifications.Trigger();
|
||||
}
|
||||
public void OnTweetSound() {}
|
||||
|
||||
public void PlayVideo(string videoUrl, string tweetUrl, string username, IJavascriptCallback callShowOverlay) {
|
||||
string playerPath = Config.VideoPlayerPath;
|
||||
@@ -557,8 +541,6 @@ namespace TweetDuck.Browser{
|
||||
Program.Reporter.HandleException("Error Opening Video Player", "Could not open the video player.", true, e);
|
||||
}
|
||||
}
|
||||
|
||||
AnalyticsFile.VideoPlays.Trigger();
|
||||
}
|
||||
|
||||
public void StopVideo() {
|
||||
@@ -584,16 +566,11 @@ namespace TweetDuck.Browser{
|
||||
|
||||
notification.FinishCurrentNotification();
|
||||
browser.ShowTweetDetail(columnId, chirpId, fallbackUrl);
|
||||
AnalyticsFile.TweetDetails.Trigger();
|
||||
}
|
||||
|
||||
public void OnTweetScreenshotReady(string html, int width) {
|
||||
if (notificationScreenshotManager == null){
|
||||
notificationScreenshotManager = new TweetScreenshotManager(this, plugins);
|
||||
}
|
||||
|
||||
notificationScreenshotManager ??= new TweetScreenshotManager(this, plugins);
|
||||
notificationScreenshotManager.Trigger(html, width);
|
||||
AnalyticsFile.TweetScreenshots.Trigger();
|
||||
}
|
||||
|
||||
public void DisplayTooltip(string text) {
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
@@ -10,7 +11,6 @@ using TweetDuck.Configuration;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Dialogs;
|
||||
using TweetDuck.Management;
|
||||
using TweetDuck.Management.Analytics;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Features.Twitter;
|
||||
using TweetLib.Core.Utils;
|
||||
@@ -37,12 +37,6 @@ namespace TweetDuck.Browser.Handling{
|
||||
|
||||
protected ContextInfo.ContextData Context { get; private set; }
|
||||
|
||||
private readonly AnalyticsFile.IProvider analytics;
|
||||
|
||||
protected ContextMenuBase(AnalyticsFile.IProvider analytics){
|
||||
this.analytics = analytics;
|
||||
}
|
||||
|
||||
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model) {
|
||||
if (!TwitterUrls.IsTweetDeck(frame.Url) || browser.IsLoading) {
|
||||
Context = CurrentInfo.Reset();
|
||||
@@ -114,7 +108,6 @@ namespace TweetDuck.Browser.Handling{
|
||||
Match match = TwitterUrls.RegexAccount.Match(url);
|
||||
|
||||
SetClipboardText(control, match.Success ? match.Groups[1].Value : url);
|
||||
control.InvokeAsyncSafe(analytics.AnalyticsFile.CopiedUsernames.Trigger);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -129,9 +122,7 @@ namespace TweetDuck.Browser.Handling{
|
||||
case MenuCopyImage: {
|
||||
string url = Context.MediaUrl;
|
||||
|
||||
control.InvokeAsyncSafe(() => {
|
||||
TwitterUtils.CopyImage(url, ImageQuality);
|
||||
});
|
||||
control.InvokeAsyncSafe(() => { TwitterUtils.CopyImage(url, ImageQuality); });
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -141,7 +132,6 @@ namespace TweetDuck.Browser.Handling{
|
||||
|
||||
control.InvokeAsyncSafe(() => {
|
||||
TwitterUtils.ViewImage(url, ImageQuality);
|
||||
analytics.AnalyticsFile.ViewedImages.Trigger();
|
||||
});
|
||||
|
||||
break;
|
||||
@@ -155,11 +145,9 @@ namespace TweetDuck.Browser.Handling{
|
||||
control.InvokeAsyncSafe(() => {
|
||||
if (isVideo) {
|
||||
TwitterUtils.DownloadVideo(url, username);
|
||||
analytics.AnalyticsFile.DownloadedVideos.Trigger();
|
||||
}
|
||||
else {
|
||||
TwitterUtils.DownloadImage(url, username, ImageQuality);
|
||||
analytics.AnalyticsFile.DownloadedImages.Trigger();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -172,7 +160,6 @@ namespace TweetDuck.Browser.Handling{
|
||||
|
||||
control.InvokeAsyncSafe(() => {
|
||||
TwitterUtils.DownloadImages(urls, username, ImageQuality);
|
||||
analytics.AnalyticsFile.DownloadedImages.Trigger();
|
||||
});
|
||||
|
||||
break;
|
||||
@@ -181,7 +168,6 @@ namespace TweetDuck.Browser.Handling{
|
||||
case MenuReadApplyROT13:
|
||||
string selection = parameters.SelectionText;
|
||||
control.InvokeAsyncSafe(() => FormMessage.Information("ROT13", StringUtils.ConvertRot13(selection), FormMessage.OK));
|
||||
control.InvokeAsyncSafe(analytics.AnalyticsFile.UsedROT13.Trigger);
|
||||
return true;
|
||||
|
||||
case MenuSearchInBrowser:
|
||||
@@ -191,7 +177,7 @@ namespace TweetDuck.Browser.Handling{
|
||||
break;
|
||||
|
||||
case MenuOpenDevTools:
|
||||
browserControl.OpenDevToolsCustom();
|
||||
browserControl.OpenDevToolsCustom(new Point(parameters.XCoord, parameters.YCoord));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -223,7 +209,7 @@ namespace TweetDuck.Browser.Handling{
|
||||
}
|
||||
|
||||
protected static void AddDebugMenuItems(IMenuModel model) {
|
||||
if (BrowserUtils.HasDevTools){
|
||||
if (Config.DevToolsInContextMenu) {
|
||||
AddSeparator(model);
|
||||
model.AddItem(MenuOpenDevTools, "Open dev tools");
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ namespace TweetDuck.Browser.Handling{
|
||||
|
||||
private readonly FormBrowser form;
|
||||
|
||||
public ContextMenuBrowser(FormBrowser form) : base(form){
|
||||
public ContextMenuBrowser(FormBrowser form) {
|
||||
this.form = form;
|
||||
}
|
||||
|
||||
@@ -89,8 +89,6 @@ namespace TweetDuck.Browser.Handling{
|
||||
}
|
||||
|
||||
RemoveSeparatorIfLast(model);
|
||||
|
||||
form.InvokeAsyncSafe(form.AnalyticsFile.BrowserContextMenus.Trigger);
|
||||
}
|
||||
|
||||
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags) {
|
||||
@@ -128,7 +126,10 @@ namespace TweetDuck.Browser.Handling{
|
||||
return true;
|
||||
|
||||
case MenuScreenshotTweet:
|
||||
form.InvokeAsyncSafe(form.TriggerTweetScreenshot);
|
||||
var chirp = Context.Chirp;
|
||||
|
||||
form.InvokeAsyncSafe(() => form.TriggerTweetScreenshot(chirp.ColumnId, chirp.ChirpId));
|
||||
|
||||
return true;
|
||||
|
||||
case MenuOpenQuotedTweetUrl:
|
||||
@@ -165,7 +166,6 @@ namespace TweetDuck.Browser.Handling{
|
||||
|
||||
menu.Popup += (sender, args) => {
|
||||
menu.MenuItems[1].Checked = Config.MuteNotifications;
|
||||
form.AnalyticsFile.BrowserContextMenus.Trigger();
|
||||
};
|
||||
|
||||
return menu;
|
||||
|
@@ -1,10 +1,7 @@
|
||||
using CefSharp;
|
||||
using TweetDuck.Management.Analytics;
|
||||
|
||||
namespace TweetDuck.Browser.Handling {
|
||||
sealed class ContextMenuGuide : ContextMenuBase {
|
||||
public ContextMenuGuide(AnalyticsFile.IProvider analytics) : base(analytics){}
|
||||
|
||||
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model) {
|
||||
model.Clear();
|
||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||
|
@@ -13,7 +13,7 @@ namespace TweetDuck.Browser.Handling{
|
||||
private readonly FormNotificationBase form;
|
||||
private readonly bool enableCustomMenu;
|
||||
|
||||
public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu) : base(form){
|
||||
public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu) {
|
||||
this.form = form;
|
||||
this.enableCustomMenu = enableCustomMenu;
|
||||
}
|
||||
@@ -52,7 +52,6 @@ namespace TweetDuck.Browser.Handling{
|
||||
|
||||
form.InvokeAsyncSafe(() => {
|
||||
form.ContextMenuOpen = true;
|
||||
form.AnalyticsFile.NotificationContextMenus.Trigger();
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using CefSharp;
|
||||
using CefSharp.Enums;
|
||||
using TweetDuck.Utils;
|
||||
|
||||
namespace TweetDuck.Browser.Handling {
|
||||
sealed class DragHandlerBrowser : IDragHandler {
|
||||
@@ -12,7 +13,7 @@ namespace TweetDuck.Browser.Handling{
|
||||
|
||||
public bool OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask) {
|
||||
void TriggerDragStart(string type, string data = null) {
|
||||
browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", type, data);
|
||||
browserControl.ExecuteJsAsync("window.TDGF_onGlobalDragStart", type, data);
|
||||
}
|
||||
|
||||
requestHandler.BlockNextUserNavUrl = dragData.LinkUrl; // empty if not a link
|
||||
@@ -30,6 +31,6 @@ namespace TweetDuck.Browser.Handling{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnDraggableRegionsChanged(IWebBrowser browserControl, IBrowser browser, IList<DraggableRegion> regions){}
|
||||
public void OnDraggableRegionsChanged(IWebBrowser browserControl, IBrowser browser, IFrame frame, IList<DraggableRegion> regions) {}
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,9 @@ using CefSharp;
|
||||
namespace TweetDuck.Browser.Handling.Filters {
|
||||
abstract class ResponseFilterBase : IResponseFilter {
|
||||
private enum State {
|
||||
Reading, Writing, Done
|
||||
Reading,
|
||||
Writing,
|
||||
Done
|
||||
}
|
||||
|
||||
private readonly Encoding encoding;
|
||||
|
35
Browser/Handling/General/CustomLifeSpanHandler.cs
Normal file
35
Browser/Handling/General/CustomLifeSpanHandler.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using CefSharp;
|
||||
using CefSharp.Handler;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Utils;
|
||||
|
||||
namespace TweetDuck.Browser.Handling.General {
|
||||
sealed class CustomLifeSpanHandler : LifeSpanHandler {
|
||||
private static bool IsPopupAllowed(string url) {
|
||||
return url.StartsWith("https://twitter.com/teams/authorize?");
|
||||
}
|
||||
|
||||
public static bool HandleLinkClick(IWebBrowser browserControl, WindowOpenDisposition targetDisposition, string targetUrl) {
|
||||
switch (targetDisposition) {
|
||||
case WindowOpenDisposition.NewBackgroundTab:
|
||||
case WindowOpenDisposition.NewForegroundTab:
|
||||
case WindowOpenDisposition.NewPopup when !IsPopupAllowed(targetUrl):
|
||||
case WindowOpenDisposition.NewWindow:
|
||||
browserControl.AsControl().InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(targetUrl));
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser) {
|
||||
newBrowser = null;
|
||||
return HandleLinkClick(browserControl, targetDisposition, targetUrl);
|
||||
}
|
||||
|
||||
protected override bool DoClose(IWebBrowser browserControl, IBrowser browser) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,6 +3,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using TweetLib.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Browser.Handling.General {
|
||||
sealed class FileDialogHandler : IDialogHandler {
|
||||
@@ -10,13 +11,14 @@ namespace TweetDuck.Browser.Handling.General{
|
||||
if (mode == CefFileDialogMode.Open || mode == CefFileDialogMode.OpenMultiple) {
|
||||
string allFilters = string.Join(";", acceptFilters.SelectMany(ParseFileType).Where(filter => !string.IsNullOrEmpty(filter)).Select(filter => "*" + filter));
|
||||
|
||||
using(OpenFileDialog dialog = new OpenFileDialog{
|
||||
using OpenFileDialog dialog = new OpenFileDialog {
|
||||
AutoUpgradeEnabled = true,
|
||||
DereferenceLinks = true,
|
||||
Multiselect = mode == CefFileDialogMode.OpenMultiple,
|
||||
Title = "Open Files",
|
||||
Filter = $"All Supported Formats ({allFilters})|{allFilters}|All Files (*.*)|*.*"
|
||||
}){
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == DialogResult.OK) {
|
||||
string ext = Path.GetExtension(dialog.FileName)?.ToLower();
|
||||
callback.Continue(acceptFilters.FindIndex(filter => ParseFileType(filter).Contains(ext)), dialog.FileNames.ToList());
|
||||
@@ -26,8 +28,6 @@ namespace TweetDuck.Browser.Handling.General{
|
||||
}
|
||||
|
||||
callback.Dispose();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
@@ -38,7 +38,7 @@ namespace TweetDuck.Browser.Handling.General{
|
||||
|
||||
private static IEnumerable<string> ParseFileType(string type) {
|
||||
if (string.IsNullOrEmpty(type)) {
|
||||
return new string[0];
|
||||
return StringUtils.EmptyArray;
|
||||
}
|
||||
|
||||
if (type[0] == '.') {
|
||||
@@ -55,7 +55,7 @@ namespace TweetDuck.Browser.Handling.General{
|
||||
}
|
||||
|
||||
System.Diagnostics.Debugger.Break();
|
||||
return new string[0];
|
||||
return StringUtils.EmptyArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -82,7 +82,8 @@ namespace TweetDuck.Browser.Handling.General{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IJsDialogHandler.OnJSBeforeUnload(IWebBrowser browserControl, IBrowser browser, string message, bool isReload, IJsDialogCallback callback){
|
||||
bool IJsDialogHandler.OnBeforeUnloadDialog(IWebBrowser browserControl, IBrowser browser, string messageText, bool isReload, IJsDialogCallback callback) {
|
||||
callback.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -1,38 +0,0 @@
|
||||
using CefSharp;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Utils;
|
||||
|
||||
namespace TweetDuck.Browser.Handling.General{
|
||||
sealed class LifeSpanHandler : ILifeSpanHandler{
|
||||
private static bool IsPopupAllowed(string url){
|
||||
return url.StartsWith("https://twitter.com/teams/authorize?");
|
||||
}
|
||||
|
||||
public static bool HandleLinkClick(IWebBrowser browserControl, WindowOpenDisposition targetDisposition, string targetUrl){
|
||||
switch(targetDisposition){
|
||||
case WindowOpenDisposition.NewBackgroundTab:
|
||||
case WindowOpenDisposition.NewForegroundTab:
|
||||
case WindowOpenDisposition.NewPopup when !IsPopupAllowed(targetUrl):
|
||||
case WindowOpenDisposition.NewWindow:
|
||||
browserControl.AsControl().InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(targetUrl));
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){
|
||||
newBrowser = null;
|
||||
return HandleLinkClick(browserControl, targetDisposition, targetUrl);
|
||||
}
|
||||
|
||||
public void OnAfterCreated(IWebBrowser browserControl, IBrowser browser){}
|
||||
|
||||
public bool DoClose(IWebBrowser browserControl, IBrowser browser){
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnBeforeClose(IWebBrowser browserControl, IBrowser browser){}
|
||||
}
|
||||
}
|
@@ -1,31 +1,12 @@
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Dialogs;
|
||||
using TweetDuck.Utils;
|
||||
|
||||
namespace TweetDuck.Browser.Handling {
|
||||
class KeyboardHandlerBase : IKeyboardHandler {
|
||||
protected virtual bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){
|
||||
protected virtual bool HandleRawKey(IWebBrowser browserControl, Keys key, CefEventFlags modifiers) {
|
||||
if (modifiers == (CefEventFlags.ControlDown | CefEventFlags.ShiftDown) && key == Keys.I) {
|
||||
if (BrowserUtils.HasDevTools){
|
||||
browserControl.OpenDevToolsCustom();
|
||||
}
|
||||
else{
|
||||
browserControl.AsControl().InvokeSafe(() => {
|
||||
string extraMessage;
|
||||
|
||||
if (Program.IsPortable){
|
||||
extraMessage = "Please download the portable installer, select the folder with your current installation of TweetDuck Portable, and tick 'Install dev tools' during the installation process.";
|
||||
}
|
||||
else{
|
||||
extraMessage = "Please download the installer, and tick 'Install dev tools' during the installation process. The installer will automatically find and update your current installation of TweetDuck.";
|
||||
}
|
||||
|
||||
FormMessage.Information("Dev Tools", "You do not have dev tools installed. " + extraMessage, FormMessage.OK);
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -33,8 +14,8 @@ namespace TweetDuck.Browser.Handling{
|
||||
}
|
||||
|
||||
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut) {
|
||||
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){
|
||||
return HandleRawKey(browserControl, browser, (Keys)windowsKeyCode, modifiers);
|
||||
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("devtools://")) {
|
||||
return HandleRawKey(browserControl, (Keys) windowsKeyCode, modifiers);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@@ -9,12 +9,8 @@ namespace TweetDuck.Browser.Handling{
|
||||
this.form = form;
|
||||
}
|
||||
|
||||
protected override bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){
|
||||
if (base.HandleRawKey(browserControl, browser, key, modifiers)){
|
||||
return true;
|
||||
}
|
||||
|
||||
return form.ProcessBrowserKey(key);
|
||||
protected override bool HandleRawKey(IWebBrowser browserControl, Keys key, CefEventFlags modifiers) {
|
||||
return base.HandleRawKey(browserControl, key, modifiers) || form.ProcessBrowserKey(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,29 +11,22 @@ namespace TweetDuck.Browser.Handling{
|
||||
this.notification = notification;
|
||||
}
|
||||
|
||||
private void TriggerKeyboardShortcutAnalytics(){
|
||||
notification.InvokeAsyncSafe(notification.AnalyticsFile.NotificationKeyboardShortcuts.Trigger);
|
||||
}
|
||||
|
||||
protected override bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){
|
||||
if (base.HandleRawKey(browserControl, browser, key, modifiers)){
|
||||
protected override bool HandleRawKey(IWebBrowser browserControl, Keys key, CefEventFlags modifiers) {
|
||||
if (base.HandleRawKey(browserControl, key, modifiers)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case Keys.Enter:
|
||||
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
||||
TriggerKeyboardShortcutAnalytics();
|
||||
return true;
|
||||
|
||||
case Keys.Escape:
|
||||
notification.InvokeAsyncSafe(notification.HideNotification);
|
||||
TriggerKeyboardShortcutAnalytics();
|
||||
return true;
|
||||
|
||||
case Keys.Space:
|
||||
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
|
||||
TriggerKeyboardShortcutAnalytics();
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@@ -1,79 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using CefSharp;
|
||||
using CefSharp;
|
||||
using CefSharp.Handler;
|
||||
using TweetDuck.Browser.Handling.General;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Browser.Handling {
|
||||
class RequestHandlerBase : DefaultRequestHandler{
|
||||
private static readonly Regex TweetDeckResourceUrl = new Regex(@"/dist/(.*?)\.(.*?)\.(css|js)$");
|
||||
private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(4);
|
||||
|
||||
public static void LoadResourceRewriteRules(string rules){
|
||||
if (string.IsNullOrEmpty(rules)){
|
||||
return;
|
||||
}
|
||||
|
||||
TweetDeckHashes.Clear();
|
||||
|
||||
foreach(string rule in rules.Replace(" ", "").ToLower().Split(',')){
|
||||
var (key, hash) = StringUtils.SplitInTwo(rule, '=') ?? throw new ArgumentException("A rule must have one '=' character: " + rule);
|
||||
|
||||
if (hash.All(chr => char.IsDigit(chr) || (chr >= 'a' && chr <= 'f'))){
|
||||
TweetDeckHashes.Add(key, hash);
|
||||
}
|
||||
else{
|
||||
throw new ArgumentException("Invalid hash characters: " + rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RequestHandlerBase : RequestHandler {
|
||||
private readonly bool autoReload;
|
||||
|
||||
public RequestHandlerBase(bool autoReload) {
|
||||
this.autoReload = autoReload;
|
||||
}
|
||||
|
||||
public override bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){
|
||||
return LifeSpanHandler.HandleLinkClick(browserControl, targetDisposition, targetUrl);
|
||||
protected override bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture) {
|
||||
return CustomLifeSpanHandler.HandleLinkClick(browserControl, targetDisposition, targetUrl);
|
||||
}
|
||||
|
||||
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
|
||||
if (BrowserUtils.HasDevTools){
|
||||
NameValueCollection headers = request.Headers;
|
||||
headers.Remove("x-devtools-emulate-network-conditions-client-id");
|
||||
request.Headers = headers;
|
||||
}
|
||||
|
||||
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
||||
}
|
||||
|
||||
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
||||
if ((request.ResourceType == ResourceType.Script || request.ResourceType == ResourceType.Stylesheet) && TweetDeckHashes.Count > 0){
|
||||
string url = request.Url;
|
||||
Match match = TweetDeckResourceUrl.Match(url);
|
||||
|
||||
if (match.Success && TweetDeckHashes.TryGetValue($"{match.Groups[1]}.{match.Groups[3]}", out string hash)){
|
||||
if (match.Groups[2].Value == hash){
|
||||
Program.Reporter.LogVerbose("[RequestHandlerBase] Accepting " + url);
|
||||
}
|
||||
else{
|
||||
Program.Reporter.LogVerbose("[RequestHandlerBase] Replacing " + url + " hash with " + hash);
|
||||
request.Url = TweetDeckResourceUrl.Replace(url, $"/dist/$1.{hash}.$3");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnResourceResponse(browserControl, browser, frame, request, response);
|
||||
}
|
||||
|
||||
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
|
||||
protected override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status) {
|
||||
if (autoReload) {
|
||||
browser.Reload();
|
||||
}
|
||||
|
@@ -1,42 +1,13 @@
|
||||
using System.Collections.Specialized;
|
||||
using CefSharp;
|
||||
using TweetDuck.Browser.Handling.Filters;
|
||||
using TweetDuck.Utils;
|
||||
using CefSharp;
|
||||
using TweetLib.Core.Features.Twitter;
|
||||
|
||||
namespace TweetDuck.Browser.Handling {
|
||||
sealed class RequestHandlerBrowser : RequestHandlerBase {
|
||||
private const string UrlVendorResource = "/dist/vendor";
|
||||
private const string UrlLoadingSpinner = "/backgrounds/spinner_blue";
|
||||
|
||||
public string BlockNextUserNavUrl { get; set; }
|
||||
|
||||
public RequestHandlerBrowser() : base(true) {}
|
||||
|
||||
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
|
||||
if (request.ResourceType == ResourceType.MainFrame){
|
||||
if (request.Url.EndsWith("//twitter.com/")){
|
||||
request.Url = TwitterUrls.TweetDeck; // redirect plain twitter.com requests, fixes bugs with login 2FA
|
||||
}
|
||||
}
|
||||
else if (request.ResourceType == ResourceType.Script){
|
||||
string url = request.Url;
|
||||
|
||||
if (url.Contains("analytics.")){
|
||||
callback.Dispose();
|
||||
return CefReturnValue.Cancel;
|
||||
}
|
||||
else if (url.Contains(UrlVendorResource)){
|
||||
NameValueCollection headers = request.Headers;
|
||||
headers["Accept-Encoding"] = "identity";
|
||||
request.Headers = headers;
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
||||
}
|
||||
|
||||
public override bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect){
|
||||
protected override bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect) {
|
||||
if (userGesture && request.TransitionType == TransitionType.LinkClicked) {
|
||||
bool block = request.Url == BlockNextUserNavUrl;
|
||||
BlockNextUserNavUrl = string.Empty;
|
||||
@@ -48,22 +19,5 @@ namespace TweetDuck.Browser.Handling{
|
||||
|
||||
return base.OnBeforeBrowse(browserControl, browser, frame, request, userGesture, isRedirect);
|
||||
}
|
||||
|
||||
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
||||
if (request.ResourceType == ResourceType.Image && request.Url.Contains(UrlLoadingSpinner)){
|
||||
request.Url = TwitterUtils.LoadingSpinner.Url;
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnResourceResponse(browserControl, browser, frame, request, response);
|
||||
}
|
||||
|
||||
public override IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
||||
if (request.ResourceType == ResourceType.Script && request.Url.Contains(UrlVendorResource) && int.TryParse(response.ResponseHeaders["Content-Length"], out int totalBytes)){
|
||||
return new ResponseFilterVendor(totalBytes);
|
||||
}
|
||||
|
||||
return base.GetResourceResponseFilter(browserControl, browser, frame, request, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,43 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using CefSharp;
|
||||
using TweetDuck.Browser.Data;
|
||||
|
||||
namespace TweetDuck.Browser.Handling{
|
||||
sealed class ResourceHandlerFactory : IResourceHandlerFactory{
|
||||
public bool HasHandlers => !handlers.IsEmpty;
|
||||
|
||||
private readonly ConcurrentDictionary<string, IResourceHandler> handlers = new ConcurrentDictionary<string, IResourceHandler>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public IResourceHandler GetResourceHandler(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request){
|
||||
try{
|
||||
return handlers.TryGetValue(request.Url, out IResourceHandler handler) ? handler : null;
|
||||
}finally{
|
||||
request.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// registration
|
||||
|
||||
public bool RegisterHandler(string url, IResourceHandler handler){
|
||||
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
|
||||
handlers.AddOrUpdate(uri.AbsoluteUri, handler, (key, prev) => handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool RegisterHandler(ResourceLink link){
|
||||
return RegisterHandler(link.Url, link.Handler);
|
||||
}
|
||||
|
||||
public bool UnregisterHandler(string url){
|
||||
return handlers.TryRemove(url, out IResourceHandler _);
|
||||
}
|
||||
|
||||
public bool UnregisterHandler(ResourceLink link){
|
||||
return UnregisterHandler(link.Url);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,9 @@
|
||||
using System.Collections.Specialized;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using CefSharp;
|
||||
using CefSharp.Callback;
|
||||
|
||||
namespace TweetDuck.Browser.Handling {
|
||||
sealed class ResourceHandlerNotification : IResourceHandler {
|
||||
@@ -20,8 +22,14 @@ namespace TweetDuck.Browser.Handling{
|
||||
}
|
||||
}
|
||||
|
||||
bool IResourceHandler.ProcessRequest(IRequest request, ICallback callback){
|
||||
callback.Continue();
|
||||
bool IResourceHandler.Open(IRequest request, out bool handleRequest, ICallback callback) {
|
||||
callback.Dispose();
|
||||
handleRequest = true;
|
||||
|
||||
if (dataIn != null) {
|
||||
dataIn.Position = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -31,31 +39,39 @@ namespace TweetDuck.Browser.Handling{
|
||||
response.MimeType = "text/html";
|
||||
response.StatusCode = 200;
|
||||
response.StatusText = "OK";
|
||||
response.ResponseHeaders = headers;
|
||||
responseLength = dataIn?.Length ?? -1;
|
||||
response.Headers = headers;
|
||||
responseLength = dataIn?.Length ?? 0;
|
||||
}
|
||||
|
||||
bool IResourceHandler.Read(Stream dataOut, out int bytesRead, IResourceReadCallback callback) {
|
||||
callback?.Dispose(); // TODO unnecessary null check once ReadResponse is removed
|
||||
|
||||
try {
|
||||
byte[] buffer = new byte[Math.Min(dataIn.Length - dataIn.Position, dataOut.Length)];
|
||||
int length = buffer.Length;
|
||||
|
||||
dataIn.Read(buffer, 0, length);
|
||||
dataOut.Write(buffer, 0, length);
|
||||
bytesRead = length;
|
||||
} catch { // catch IOException, possibly NullReferenceException if dataIn is null
|
||||
bytesRead = 0;
|
||||
}
|
||||
|
||||
return bytesRead > 0;
|
||||
}
|
||||
|
||||
bool IResourceHandler.Skip(long bytesToSkip, out long bytesSkipped, IResourceSkipCallback callback) {
|
||||
bytesSkipped = -2; // ERR_FAILED
|
||||
callback.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IResourceHandler.ProcessRequest(IRequest request, ICallback callback) {
|
||||
return ((IResourceHandler) this).Open(request, out bool _, callback);
|
||||
}
|
||||
|
||||
bool IResourceHandler.ReadResponse(Stream dataOut, out int bytesRead, ICallback callback) {
|
||||
callback.Dispose();
|
||||
|
||||
try{
|
||||
int length = (int)dataIn.Length;
|
||||
|
||||
dataIn.CopyTo(dataOut, length);
|
||||
bytesRead = length;
|
||||
return true;
|
||||
}catch{ // catch IOException, possibly NullReferenceException if dataIn is null
|
||||
bytesRead = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IResourceHandler.CanGetCookie(Cookie cookie){
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IResourceHandler.CanSetCookie(Cookie cookie){
|
||||
return true;
|
||||
return ((IResourceHandler) this).Read(dataOut, out bytesRead, null);
|
||||
}
|
||||
|
||||
void IResourceHandler.Cancel() {}
|
||||
|
101
Browser/Handling/ResourceProvider.cs
Normal file
101
Browser/Handling/ResourceProvider.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using CefSharp;
|
||||
using TweetLib.Core.Browser;
|
||||
using IOFile = System.IO.File;
|
||||
|
||||
namespace TweetDuck.Browser.Handling {
|
||||
internal sealed class ResourceProvider : IResourceProvider<IResourceHandler> {
|
||||
private readonly Dictionary<string, ICachedResource> cache = new Dictionary<string, ICachedResource>();
|
||||
|
||||
public IResourceHandler Status(HttpStatusCode code, string message) {
|
||||
return CreateStatusHandler(code, message);
|
||||
}
|
||||
|
||||
public IResourceHandler File(string path) {
|
||||
string key = new Uri(path).LocalPath;
|
||||
|
||||
if (cache.TryGetValue(key, out var cachedResource)) {
|
||||
return cachedResource.GetResource();
|
||||
}
|
||||
|
||||
cachedResource = FileWithCaching(path);
|
||||
cache[key] = cachedResource;
|
||||
return cachedResource.GetResource();
|
||||
}
|
||||
|
||||
private ICachedResource FileWithCaching(string path) {
|
||||
try {
|
||||
return new CachedFile(System.IO.File.ReadAllBytes(path), Path.GetExtension(path));
|
||||
} catch (FileNotFoundException) {
|
||||
return new CachedStatus(HttpStatusCode.NotFound, "File not found.");
|
||||
} catch (DirectoryNotFoundException) {
|
||||
return new CachedStatus(HttpStatusCode.NotFound, "Directory not found.");
|
||||
} catch (Exception e) {
|
||||
return new CachedStatus(HttpStatusCode.InternalServerError, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearCache() {
|
||||
cache.Clear();
|
||||
}
|
||||
|
||||
private static ResourceHandler CreateHandler(byte[] bytes) {
|
||||
var handler = ResourceHandler.FromStream(new MemoryStream(bytes), autoDisposeStream: true);
|
||||
handler.Headers.Set("Access-Control-Allow-Origin", "*");
|
||||
return handler;
|
||||
}
|
||||
|
||||
private static IResourceHandler CreateFileContentsHandler(byte[] bytes, string extension) {
|
||||
if (bytes.Length == 0) {
|
||||
return CreateStatusHandler(HttpStatusCode.NoContent, "File is empty."); // FromByteArray crashes CEF internals with no contents
|
||||
}
|
||||
else {
|
||||
var handler = CreateHandler(bytes);
|
||||
handler.MimeType = Cef.GetMimeType(extension);
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
||||
private static IResourceHandler CreateStatusHandler(HttpStatusCode code, string message) {
|
||||
var handler = CreateHandler(Encoding.UTF8.GetBytes(message));
|
||||
handler.StatusCode = (int) code;
|
||||
return handler;
|
||||
}
|
||||
|
||||
private interface ICachedResource {
|
||||
IResourceHandler GetResource();
|
||||
}
|
||||
|
||||
private sealed class CachedFile : ICachedResource {
|
||||
private readonly byte[] bytes;
|
||||
private readonly string extension;
|
||||
|
||||
public CachedFile(byte[] bytes, string extension) {
|
||||
this.bytes = bytes;
|
||||
this.extension = extension;
|
||||
}
|
||||
|
||||
public IResourceHandler GetResource() {
|
||||
return CreateFileContentsHandler(bytes, extension);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class CachedStatus : ICachedResource {
|
||||
private readonly HttpStatusCode code;
|
||||
private readonly string message;
|
||||
|
||||
public CachedStatus(HttpStatusCode code, string message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public IResourceHandler GetResource() {
|
||||
return CreateStatusHandler(code, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
Browser/Handling/ResourceRequestHandler.cs
Normal file
35
Browser/Handling/ResourceRequestHandler.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using CefSharp;
|
||||
using TweetDuck.Browser.Data;
|
||||
|
||||
namespace TweetDuck.Browser.Handling {
|
||||
abstract class ResourceRequestHandler : CefSharp.Handler.ResourceRequestHandler {
|
||||
private class SelfFactoryImpl : IResourceRequestHandlerFactory {
|
||||
private readonly ResourceRequestHandler me;
|
||||
|
||||
public SelfFactoryImpl(ResourceRequestHandler me) {
|
||||
this.me = me;
|
||||
}
|
||||
|
||||
bool IResourceRequestHandlerFactory.HasHandlers => true;
|
||||
|
||||
[SuppressMessage("ReSharper", "RedundantAssignment")]
|
||||
IResourceRequestHandler IResourceRequestHandlerFactory.GetResourceRequestHandler(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling) {
|
||||
disableDefaultHandling = me.ResourceHandlers.HasHandler(request);
|
||||
return me;
|
||||
}
|
||||
}
|
||||
|
||||
public IResourceRequestHandlerFactory SelfFactory { get; }
|
||||
public ResourceHandlers ResourceHandlers { get; }
|
||||
|
||||
protected ResourceRequestHandler() {
|
||||
this.SelfFactory = new SelfFactoryImpl(this);
|
||||
this.ResourceHandlers = new ResourceHandlers();
|
||||
}
|
||||
|
||||
protected override IResourceHandler GetResourceHandler(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request) {
|
||||
return ResourceHandlers.GetHandler(request);
|
||||
}
|
||||
}
|
||||
}
|
66
Browser/Handling/ResourceRequestHandlerBase.cs
Normal file
66
Browser/Handling/ResourceRequestHandlerBase.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using CefSharp;
|
||||
using TweetLib.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Browser.Handling {
|
||||
class ResourceRequestHandlerBase : ResourceRequestHandler {
|
||||
private static readonly Regex TweetDeckResourceUrl = new Regex(@"/dist/(.*?)\.(.*?)\.(css|js)$");
|
||||
private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(4);
|
||||
|
||||
public static void LoadResourceRewriteRules(string rules) {
|
||||
if (string.IsNullOrEmpty(rules)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TweetDeckHashes.Clear();
|
||||
|
||||
foreach (string rule in rules.Replace(" ", "").ToLower().Split(',')) {
|
||||
var (key, hash) = StringUtils.SplitInTwo(rule, '=') ?? throw new ArgumentException("A rule must have one '=' character: " + rule);
|
||||
|
||||
if (hash.All(chr => char.IsDigit(chr) || (chr >= 'a' && chr <= 'f'))) {
|
||||
TweetDeckHashes.Add(key, hash);
|
||||
}
|
||||
else {
|
||||
throw new ArgumentException("Invalid hash characters: " + rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback) {
|
||||
if (request.ResourceType == ResourceType.CspReport) {
|
||||
callback.Dispose();
|
||||
return CefReturnValue.Cancel;
|
||||
}
|
||||
|
||||
NameValueCollection headers = request.Headers;
|
||||
headers.Remove("x-devtools-emulate-network-conditions-client-id");
|
||||
request.Headers = headers;
|
||||
|
||||
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
||||
}
|
||||
|
||||
protected override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response) {
|
||||
if ((request.ResourceType == ResourceType.Script || request.ResourceType == ResourceType.Stylesheet) && TweetDeckHashes.Count > 0) {
|
||||
string url = request.Url;
|
||||
Match match = TweetDeckResourceUrl.Match(url);
|
||||
|
||||
if (match.Success && TweetDeckHashes.TryGetValue($"{match.Groups[1]}.{match.Groups[3]}", out string hash)) {
|
||||
if (match.Groups[2].Value == hash) {
|
||||
Program.Reporter.LogVerbose("[RequestHandlerBase] Accepting " + url);
|
||||
}
|
||||
else {
|
||||
Program.Reporter.LogVerbose("[RequestHandlerBase] Replacing " + url + " hash with " + hash);
|
||||
request.Url = TweetDeckResourceUrl.Replace(url, $"/dist/$1.{hash}.$3");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnResourceResponse(browserControl, browser, frame, request, response);
|
||||
}
|
||||
}
|
||||
}
|
52
Browser/Handling/ResourceRequestHandlerBrowser.cs
Normal file
52
Browser/Handling/ResourceRequestHandlerBrowser.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using CefSharp;
|
||||
using TweetDuck.Browser.Handling.Filters;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Features.Twitter;
|
||||
|
||||
namespace TweetDuck.Browser.Handling {
|
||||
class ResourceRequestHandlerBrowser : ResourceRequestHandlerBase {
|
||||
private const string UrlVendorResource = "/dist/vendor";
|
||||
private const string UrlLoadingSpinner = "/backgrounds/spinner_blue";
|
||||
private const string UrlVersionCheck = "/web/dist/version.json";
|
||||
|
||||
protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback) {
|
||||
if (request.ResourceType == ResourceType.MainFrame) {
|
||||
if (request.Url.EndsWith("//twitter.com/")) {
|
||||
request.Url = TwitterUrls.TweetDeck; // redirect plain twitter.com requests, fixes bugs with login 2FA
|
||||
}
|
||||
}
|
||||
else if (request.ResourceType == ResourceType.Image) {
|
||||
if (request.Url.Contains(UrlLoadingSpinner)) {
|
||||
request.Url = TwitterUtils.LoadingSpinner.Url;
|
||||
}
|
||||
}
|
||||
else if (request.ResourceType == ResourceType.Script) {
|
||||
string url = request.Url;
|
||||
|
||||
if (url.Contains("analytics.")) {
|
||||
callback.Dispose();
|
||||
return CefReturnValue.Cancel;
|
||||
}
|
||||
else if (url.Contains(UrlVendorResource)) {
|
||||
request.SetHeaderByName("Accept-Encoding", "identity", overwrite: true);
|
||||
}
|
||||
}
|
||||
else if (request.ResourceType == ResourceType.Xhr) {
|
||||
if (request.Url.Contains(UrlVersionCheck)) {
|
||||
callback.Dispose();
|
||||
return CefReturnValue.Cancel;
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
||||
}
|
||||
|
||||
protected override IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response) {
|
||||
if (request.ResourceType == ResourceType.Script && request.Url.Contains(UrlVendorResource) && int.TryParse(response.Headers["Content-Length"], out int totalBytes)) {
|
||||
return new ResponseFilterVendor(totalBytes);
|
||||
}
|
||||
|
||||
return base.GetResourceResponseFilter(browserControl, browser, frame, request, response);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using TweetDuck.Controls;
|
||||
using TweetLib.Core.Features.Notifications;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
using TweetLib.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Browser.Notification.Example {
|
||||
sealed class FormNotificationExample : FormNotificationMain {
|
||||
@@ -32,7 +34,7 @@ namespace TweetDuck.Browser.Notification.Example{
|
||||
public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false) {
|
||||
browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||
|
||||
string exampleTweetHTML = Program.Resources.LoadSilent("pages/example.html")?.Replace("{avatar}", AppLogo.Url) ?? string.Empty;
|
||||
string exampleTweetHTML = FileUtils.ReadFileOrNull(Path.Combine(Program.ResourcesPath, "notification/example/example.html"))?.Replace("{avatar}", AppLogo.Url) ?? string.Empty;
|
||||
|
||||
#if DEBUG
|
||||
exampleTweetHTML = exampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
|
||||
|
@@ -1,20 +1,20 @@
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using TweetDuck.Browser.Data;
|
||||
using TweetDuck.Browser.Handling;
|
||||
using TweetDuck.Browser.Handling.General;
|
||||
using TweetDuck.Configuration;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Management.Analytics;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Features.Notifications;
|
||||
using TweetLib.Core.Features.Twitter;
|
||||
|
||||
namespace TweetDuck.Browser.Notification {
|
||||
abstract partial class FormNotificationBase : Form, AnalyticsFile.IProvider{
|
||||
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png"));
|
||||
abstract partial class FormNotificationBase : Form {
|
||||
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandlers.ForBytes(Properties.Resources.avatar, "image/png"));
|
||||
|
||||
protected const string BlankURL = TwitterUrls.TweetDeck + "/?blank";
|
||||
|
||||
public static string FontSize = null;
|
||||
public static string HeadLayout = null;
|
||||
@@ -74,9 +74,7 @@ namespace TweetDuck.Browser.Notification{
|
||||
protected virtual bool CanDragWindow => true;
|
||||
|
||||
public new Point Location {
|
||||
get{
|
||||
return base.Location;
|
||||
}
|
||||
get { return base.Location; }
|
||||
|
||||
set {
|
||||
Visible = (base.Location = value) != ControlExtensions.InvisibleLocation;
|
||||
@@ -95,14 +93,12 @@ namespace TweetDuck.Browser.Notification{
|
||||
}
|
||||
}
|
||||
|
||||
public AnalyticsFile AnalyticsFile => owner.AnalyticsFile;
|
||||
|
||||
protected override bool ShowWithoutActivation => true;
|
||||
|
||||
protected float DpiScale { get; }
|
||||
protected double SizeScale => DpiScale * Config.ZoomLevel / 100.0;
|
||||
|
||||
protected readonly FormBrowser owner;
|
||||
private readonly FormBrowser owner;
|
||||
|
||||
#pragma warning disable IDE0069 // Disposable fields should be disposed
|
||||
protected readonly ChromiumWebBrowser browser;
|
||||
@@ -130,16 +126,19 @@ namespace TweetDuck.Browser.Notification{
|
||||
this.owner = owner;
|
||||
this.owner.FormClosed += owner_FormClosed;
|
||||
|
||||
ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory();
|
||||
resourceHandlerFactory.RegisterHandler(TwitterUrls.TweetDeck, this.resourceHandler);
|
||||
resourceHandlerFactory.RegisterHandler(AppLogo);
|
||||
var resourceRequestHandler = new ResourceRequestHandlerBase();
|
||||
var resourceHandlers = resourceRequestHandler.ResourceHandlers;
|
||||
|
||||
this.browser = new ChromiumWebBrowser("about:blank"){
|
||||
resourceHandlers.Register(BlankURL, ResourceHandlers.ForString(string.Empty));
|
||||
resourceHandlers.Register(TwitterUrls.TweetDeck, () => this.resourceHandler);
|
||||
resourceHandlers.Register(AppLogo);
|
||||
|
||||
this.browser = new ChromiumWebBrowser(BlankURL) {
|
||||
MenuHandler = new ContextMenuNotification(this, enableContextMenu),
|
||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||
LifeSpanHandler = new LifeSpanHandler(),
|
||||
LifeSpanHandler = new CustomLifeSpanHandler(),
|
||||
RequestHandler = new RequestHandlerBase(false),
|
||||
ResourceHandlerFactory = resourceHandlerFactory
|
||||
ResourceRequestHandlerFactory = resourceRequestHandler.SelfFactory
|
||||
};
|
||||
|
||||
this.browser.Dock = DockStyle.None;
|
||||
@@ -185,7 +184,7 @@ namespace TweetDuck.Browser.Notification{
|
||||
// notification methods
|
||||
|
||||
public virtual void HideNotification() {
|
||||
browser.Load("about:blank");
|
||||
browser.Load(BlankURL);
|
||||
DisplayTooltip(null);
|
||||
|
||||
Location = ControlExtensions.InvisibleLocation;
|
||||
|
@@ -8,10 +8,10 @@ using TweetDuck.Browser.Handling;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Plugins;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Data;
|
||||
using TweetLib.Core.Features.Notifications;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
using TweetLib.Core.Features.Plugins.Enums;
|
||||
using TweetLib.Core.Features.Twitter;
|
||||
|
||||
namespace TweetDuck.Browser.Notification {
|
||||
abstract partial class FormNotificationMain : FormNotificationBase {
|
||||
@@ -31,9 +31,7 @@ namespace TweetDuck.Browser.Notification{
|
||||
private int? prevFontSize;
|
||||
|
||||
public virtual bool RequiresResize {
|
||||
get{
|
||||
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Config.DisplayNotificationTimer || prevFontSize != FontSizeLevel;
|
||||
}
|
||||
get { return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Config.DisplayNotificationTimer || prevFontSize != FontSizeLevel; }
|
||||
|
||||
set {
|
||||
if (value) {
|
||||
@@ -72,12 +70,11 @@ namespace TweetDuck.Browser.Notification{
|
||||
this.timerBarHeight = BrowserUtils.Scale(4, DpiScale);
|
||||
|
||||
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
|
||||
browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge.Notification(owner, this));
|
||||
browser.RegisterJsBridge("$TD", new TweetDeckBridge.Notification(owner, this));
|
||||
|
||||
browser.LoadingStateChanged += Browser_LoadingStateChanged;
|
||||
browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
||||
|
||||
plugins.Register(PluginEnvironment.Notification, new PluginDispatcher(browser));
|
||||
plugins.Register(PluginEnvironment.Notification, new PluginDispatcher(browser, url => TwitterUrls.IsTweetDeck(url) && url != BlankURL));
|
||||
|
||||
mouseHookDelegate = MouseHookProc;
|
||||
Disposed += (sender, args) => StopMouseHook(true);
|
||||
@@ -113,7 +110,15 @@ namespace TweetDuck.Browser.Notification{
|
||||
int eventType = wParam.ToInt32();
|
||||
|
||||
if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser) {
|
||||
browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Config.NotificationScrollSpeed * 0.01), CefEventFlags.None);
|
||||
int delta = BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Config.NotificationScrollSpeed * 0.01);
|
||||
|
||||
if (Config.EnableSmoothScrolling) {
|
||||
browser.ExecuteJsAsync("window.TDGF_scrollSmoothly", (int) Math.Round(-delta / 0.6));
|
||||
}
|
||||
else {
|
||||
browser.SendMouseWheelEvent(0, 0, 0, delta, CefEventFlags.None);
|
||||
}
|
||||
|
||||
return NativeMethods.HOOK_HANDLED;
|
||||
}
|
||||
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)) {
|
||||
@@ -127,7 +132,6 @@ namespace TweetDuck.Browser.Notification{
|
||||
}
|
||||
|
||||
blockXButtonUp = true;
|
||||
this.InvokeAsyncSafe(AnalyticsFile.NotificationExtraMouseButtons.Trigger);
|
||||
return NativeMethods.HOOK_HANDLED;
|
||||
}
|
||||
else if (eventType == NativeMethods.WM_XBUTTONUP && blockXButtonUp) {
|
||||
@@ -154,7 +158,7 @@ namespace TweetDuck.Browser.Notification{
|
||||
}
|
||||
|
||||
private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e) {
|
||||
if (!e.IsLoading && browser.Address != "about:blank"){
|
||||
if (!e.IsLoading && browser.Address != BlankURL) {
|
||||
this.InvokeSafe(() => {
|
||||
Visible = true; // ensures repaint before moving the window to a visible location
|
||||
timerDisplayDelay.Start();
|
||||
@@ -162,15 +166,6 @@ namespace TweetDuck.Browser.Notification{
|
||||
}
|
||||
}
|
||||
|
||||
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||
IFrame frame = e.Frame;
|
||||
|
||||
if (frame.IsMain && browser.Address != "about:blank"){
|
||||
frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
|
||||
CefScriptExecutor.RunFile(frame, "notification.js");
|
||||
}
|
||||
}
|
||||
|
||||
private void timerDisplayDelay_Tick(object sender, EventArgs e) {
|
||||
OnNotificationReady();
|
||||
timerDisplayDelay.Stop();
|
||||
@@ -242,13 +237,10 @@ namespace TweetDuck.Browser.Notification{
|
||||
}
|
||||
|
||||
protected override string GetTweetHTML(DesktopNotification tweet) {
|
||||
string html = tweet.GenerateHtml(BodyClasses, HeadLayout, Config.CustomNotificationCSS);
|
||||
|
||||
foreach(InjectedHTML injection in plugins.NotificationInjections){
|
||||
html = injection.InjectInto(html);
|
||||
}
|
||||
|
||||
return html;
|
||||
return tweet.GenerateHtml(BodyClasses, HeadLayout, Config.CustomNotificationCSS, plugins.NotificationInjections, new string[] {
|
||||
PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification),
|
||||
CefScriptExecutor.GetBootstrapScript("notification", includeStylesheets: false)
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadTweet(DesktopNotification tweet) {
|
||||
|
@@ -94,7 +94,6 @@ namespace TweetDuck.Browser.Notification{
|
||||
}
|
||||
|
||||
needsTrim |= tweetQueue.Count >= TrimMinimum;
|
||||
AnalyticsFile.DesktopNotifications.Trigger();
|
||||
}
|
||||
|
||||
public override void HideNotification() {
|
||||
|
@@ -1,15 +1,16 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Windows.Forms;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using CefSharp;
|
||||
using CefSharp.DevTools.Page;
|
||||
using TweetDuck.Browser.Adapters;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Dialogs;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Data;
|
||||
using TweetLib.Core.Features.Notifications;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
using TweetLib.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Browser.Notification.Screenshot {
|
||||
sealed class FormNotificationScreenshotable : FormNotificationBase {
|
||||
@@ -23,14 +24,14 @@ namespace TweetDuck.Browser.Notification.Screenshot{
|
||||
|
||||
int realWidth = BrowserUtils.Scale(width, DpiScale);
|
||||
|
||||
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new ScreenshotBridge(this, SetScreenshotHeight, callback));
|
||||
browser.RegisterJsBridge("$TD_NotificationScreenshot", new ScreenshotBridge(this, SetScreenshotHeight, callback));
|
||||
|
||||
browser.LoadingStateChanged += (sender, args) => {
|
||||
if (args.IsLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
string script = Program.Resources.LoadSilent("screenshot.js");
|
||||
string script = FileUtils.ReadFileOrNull(Path.Combine(Program.ResourcesPath, "notification/screenshot/screenshot.js"));
|
||||
|
||||
if (script == null) {
|
||||
this.InvokeAsyncSafe(callback);
|
||||
@@ -38,7 +39,7 @@ namespace TweetDuck.Browser.Notification.Screenshot{
|
||||
}
|
||||
|
||||
using IFrame frame = args.Browser.MainFrame;
|
||||
CefScriptExecutor.RunScript(frame, script.Replace("{width}", realWidth.ToString()).Replace("{frames}", TweetScreenshotManager.WaitFrames.ToString()), "gen:screenshot");
|
||||
CefScriptExecutor.RunScript(frame, script.Replace("{width}", realWidth.ToString()).Replace("1/*FRAMES*/", TweetScreenshotManager.WaitFrames.ToString()), "gen:screenshot");
|
||||
};
|
||||
|
||||
SetNotificationSize(realWidth, 1024);
|
||||
@@ -46,53 +47,45 @@ namespace TweetDuck.Browser.Notification.Screenshot{
|
||||
}
|
||||
|
||||
protected override string GetTweetHTML(DesktopNotification tweet) {
|
||||
string html = tweet.GenerateHtml("td-screenshot", HeadLayout, Config.CustomNotificationCSS);
|
||||
|
||||
foreach(InjectedHTML injection in plugins.NotificationInjections){
|
||||
html = injection.InjectInto(html);
|
||||
}
|
||||
|
||||
return html;
|
||||
return tweet.GenerateHtml("td-screenshot", HeadLayout, Config.CustomNotificationCSS, plugins.NotificationInjections, Array.Empty<string>());
|
||||
}
|
||||
|
||||
private void SetScreenshotHeight(int browserHeight) {
|
||||
this.height = BrowserUtils.Scale(browserHeight, SizeScale);
|
||||
}
|
||||
|
||||
public bool TakeScreenshot(bool ignoreHeightError = false){
|
||||
public Task<Image> TakeScreenshot(bool ignoreHeightError = false) {
|
||||
if (!ignoreHeightError) {
|
||||
if (height == 0) {
|
||||
FormMessage.Error("Screenshot Failed", "Could not detect screenshot size.", FormMessage.OK);
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
else if (height > ClientSize.Height) {
|
||||
FormMessage.Error("Screenshot Failed", $"Screenshot is too large: {height}px > {ClientSize.Height}px", FormMessage.OK);
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!WindowsUtils.IsAeroEnabled){
|
||||
MoveToVisibleLocation(); // TODO make this look nicer I guess
|
||||
return Task.Run(TakeScreenshotImpl);
|
||||
}
|
||||
|
||||
IntPtr context = NativeMethods.GetDC(this.Handle);
|
||||
|
||||
if (context == IntPtr.Zero){
|
||||
FormMessage.Error("Screenshot Failed", "Could not retrieve a graphics context handle for the notification window to take the screenshot.", FormMessage.OK);
|
||||
return false;
|
||||
}
|
||||
else{
|
||||
using Bitmap bmp = new Bitmap(ClientSize.Width, Math.Max(1, height), PixelFormat.Format32bppRgb);
|
||||
|
||||
try{
|
||||
NativeMethods.RenderSourceIntoBitmap(context, bmp);
|
||||
}finally{
|
||||
NativeMethods.ReleaseDC(this.Handle, context);
|
||||
private async Task<Image> TakeScreenshotImpl() {
|
||||
if (this.height == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Clipboard.SetImage(bmp);
|
||||
return true;
|
||||
}
|
||||
Viewport viewport = new Viewport {
|
||||
Width = this.ClientSize.Width,
|
||||
Height = this.height,
|
||||
Scale = 1
|
||||
};
|
||||
|
||||
byte[] data;
|
||||
using (var devToolsClient = browser.GetDevToolsClient()) {
|
||||
data = (await devToolsClient.Page.CaptureScreenshotAsync(CaptureScreenshotFormat.Png, clip: viewport)).Data;
|
||||
}
|
||||
|
||||
return Image.FromStream(new MemoryStream(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,14 +7,15 @@
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Controls;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
|
||||
#if GEN_SCREENSHOT_FRAMES
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using TweetDuck.Core.Utils;
|
||||
using TweetDuck.Utils;
|
||||
#endif
|
||||
|
||||
namespace TweetDuck.Browser.Notification.Screenshot {
|
||||
@@ -86,8 +87,15 @@ namespace TweetDuck.Browser.Notification.Screenshot{
|
||||
}
|
||||
|
||||
timeout.Stop();
|
||||
screenshot.TakeScreenshot();
|
||||
screenshot.TakeScreenshot().ContinueWith(HandleResult, TaskScheduler.FromCurrentSynchronizationContext());
|
||||
}
|
||||
|
||||
private void HandleResult(Task<Image> task) {
|
||||
if (task.IsFaulted) {
|
||||
Program.Reporter.HandleException("Screenshot Failed", "An error occurred while taking a screenshot.", true, task.Exception!.InnerException);
|
||||
}
|
||||
else if (task.IsCompleted) {
|
||||
Clipboard.SetImage(task.Result);
|
||||
#if !NO_HIDE_SCREENSHOTS
|
||||
OnFinished();
|
||||
#else
|
||||
@@ -95,6 +103,7 @@ namespace TweetDuck.Browser.Notification.Screenshot{
|
||||
screenshot.FormClosed += (sender, args) => disposer.Start();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFinished() {
|
||||
#if GEN_SCREENSHOT_FRAMES
|
||||
@@ -132,17 +141,23 @@ namespace TweetDuck.Browser.Notification.Screenshot{
|
||||
}
|
||||
|
||||
private void debugger_Tick(object sender, EventArgs e) {
|
||||
if (frameCounter < 63 && screenshot.TakeScreenshot(true)){
|
||||
try{
|
||||
Clipboard.GetImage()?.Save(Path.Combine(DebugScreenshotPath, "frame_" + (++frameCounter) + ".png"), ImageFormat.Png);
|
||||
}catch{
|
||||
System.Diagnostics.Debug.WriteLine("Failed generating frame " + frameCounter);
|
||||
}
|
||||
if (frameCounter < 63) {
|
||||
int frame = ++frameCounter;
|
||||
screenshot.TakeScreenshot(true).ContinueWith(task => SaveDebugFrame(task, frame), TaskScheduler.FromCurrentSynchronizationContext());
|
||||
}
|
||||
else {
|
||||
debugger.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static void SaveDebugFrame(Task<Image> task, int frame) {
|
||||
if (task.IsFaulted) {
|
||||
System.Diagnostics.Debug.WriteLine("Failed generating frame " + frame + ": " + task.Exception!.InnerException);
|
||||
}
|
||||
else if (task.IsCompleted) {
|
||||
task.Result?.Save(Path.Combine(DebugScreenshotPath, "frame_" + (++frame) + ".png"), ImageFormat.Png);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,9 @@
|
||||
using System.Drawing;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using TweetDuck.Browser.Data;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Dialogs;
|
||||
using TweetDuck.Dialogs.Settings;
|
||||
@@ -11,7 +13,7 @@ namespace TweetDuck.Browser.Notification{
|
||||
static class SoundNotification {
|
||||
public const string SupportedFormats = "*.wav;*.ogg;*.mp3;*.flac;*.opus;*.weba;*.webm";
|
||||
|
||||
public static IResourceHandler CreateFileHandler(string path){
|
||||
public static Func<IResourceHandler> CreateFileHandler(string path) {
|
||||
string mimeType = Path.GetExtension(path) switch {
|
||||
".weba" => "audio/webm",
|
||||
".webm" => "audio/webm",
|
||||
@@ -24,7 +26,7 @@ namespace TweetDuck.Browser.Notification{
|
||||
};
|
||||
|
||||
try {
|
||||
return ResourceHandler.FromFilePath(path, mimeType);
|
||||
return ResourceHandlers.ForBytes(File.ReadAllBytes(path), mimeType);
|
||||
} catch {
|
||||
FormBrowser browser = FormManager.TryFind<FormBrowser>();
|
||||
|
||||
|
@@ -7,7 +7,11 @@ using Res = TweetDuck.Properties.Resources;
|
||||
namespace TweetDuck.Browser {
|
||||
sealed partial class TrayIcon : Component {
|
||||
public enum Behavior { // keep order
|
||||
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
|
||||
Disabled,
|
||||
DisplayOnly,
|
||||
MinimizeToTray,
|
||||
CloseToTray,
|
||||
Combined
|
||||
}
|
||||
|
||||
private static UserConfig Config => Program.Config.User;
|
||||
@@ -16,9 +20,7 @@ namespace TweetDuck.Browser{
|
||||
public event EventHandler ClickClose;
|
||||
|
||||
public bool Visible {
|
||||
get{
|
||||
return notifyIcon.Visible;
|
||||
}
|
||||
get { return notifyIcon.Visible; }
|
||||
|
||||
set {
|
||||
notifyIcon.Visible = value;
|
||||
@@ -28,9 +30,7 @@ namespace TweetDuck.Browser{
|
||||
}
|
||||
|
||||
public bool HasNotifications {
|
||||
get{
|
||||
return hasNotifications;
|
||||
}
|
||||
get { return hasNotifications; }
|
||||
|
||||
set {
|
||||
if (hasNotifications != value) {
|
||||
|
@@ -6,6 +6,7 @@ using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using TweetDuck.Browser.Adapters;
|
||||
using TweetDuck.Browser.Bridge;
|
||||
using TweetDuck.Browser.Data;
|
||||
using TweetDuck.Browser.Handling;
|
||||
using TweetDuck.Browser.Handling.General;
|
||||
using TweetDuck.Browser.Notification;
|
||||
@@ -22,8 +23,7 @@ namespace TweetDuck.Browser{
|
||||
sealed class TweetDeckBrowser : IDisposable {
|
||||
private static UserConfig Config => Program.Config.User;
|
||||
|
||||
private const string ErrorUrl = "http://td/error";
|
||||
private const string TwitterStyleUrl = "https://abs.twimg.com/tduck/css";
|
||||
private const string NamespaceTweetDeck = "tweetdeck";
|
||||
|
||||
public bool Ready { get; private set; }
|
||||
|
||||
@@ -44,13 +44,16 @@ namespace TweetDuck.Browser{
|
||||
}
|
||||
|
||||
private readonly ChromiumWebBrowser browser;
|
||||
private readonly ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory();
|
||||
private readonly ResourceHandlers resourceHandlers;
|
||||
|
||||
private string prevSoundNotificationPath = null;
|
||||
|
||||
public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, TweetDeckBridge tdBridge, UpdateBridge updateBridge) {
|
||||
resourceHandlerFactory.RegisterHandler(FormNotificationBase.AppLogo);
|
||||
resourceHandlerFactory.RegisterHandler(TwitterUtils.LoadingSpinner);
|
||||
var resourceRequestHandler = new ResourceRequestHandlerBrowser();
|
||||
resourceHandlers = resourceRequestHandler.ResourceHandlers;
|
||||
|
||||
resourceHandlers.Register(FormNotificationBase.AppLogo);
|
||||
resourceHandlers.Register(TwitterUtils.LoadingSpinner);
|
||||
|
||||
RequestHandlerBrowser requestHandler = new RequestHandlerBrowser();
|
||||
|
||||
@@ -60,9 +63,9 @@ namespace TweetDuck.Browser{
|
||||
MenuHandler = new ContextMenuBrowser(owner),
|
||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||
KeyboardHandler = new KeyboardHandlerBrowser(owner),
|
||||
LifeSpanHandler = new LifeSpanHandler(),
|
||||
LifeSpanHandler = new CustomLifeSpanHandler(),
|
||||
RequestHandler = requestHandler,
|
||||
ResourceHandlerFactory = resourceHandlerFactory
|
||||
ResourceRequestHandlerFactory = resourceRequestHandler.SelfFactory
|
||||
};
|
||||
|
||||
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||
@@ -70,16 +73,15 @@ namespace TweetDuck.Browser{
|
||||
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
|
||||
this.browser.LoadError += browser_LoadError;
|
||||
|
||||
this.browser.RegisterAsyncJsObject("$TD", tdBridge);
|
||||
this.browser.RegisterAsyncJsObject("$TDU", updateBridge);
|
||||
this.browser.RegisterJsBridge("$TD", tdBridge);
|
||||
this.browser.RegisterJsBridge("$TDU", updateBridge);
|
||||
|
||||
this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
|
||||
this.browser.Dock = DockStyle.None;
|
||||
this.browser.Location = ControlExtensions.InvisibleLocation;
|
||||
this.browser.SetupZoomEvents();
|
||||
|
||||
owner.Controls.Add(browser);
|
||||
plugins.Register(PluginEnvironment.Browser, new PluginDispatcher(browser));
|
||||
plugins.Register(PluginEnvironment.Browser, new PluginDispatcher(browser, TwitterUrls.IsTweetDeck));
|
||||
|
||||
Config.MuteToggled += Config_MuteToggled;
|
||||
Config.SoundNotificationChanged += Config_SoundNotificationInfoChanged;
|
||||
@@ -87,6 +89,12 @@ namespace TweetDuck.Browser{
|
||||
|
||||
// setup and management
|
||||
|
||||
public void PrepareSize(Size size) {
|
||||
if (!Ready) {
|
||||
browser.Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBrowserReady() {
|
||||
if (!Ready) {
|
||||
browser.Location = Point.Empty;
|
||||
@@ -125,14 +133,7 @@ namespace TweetDuck.Browser{
|
||||
if (frame.IsMain) {
|
||||
string url = frame.Url;
|
||||
|
||||
if (TwitterUrls.IsTwitter(url)){
|
||||
string css = Program.Resources.Load("styles/twitter.css");
|
||||
resourceHandlerFactory.RegisterHandler(TwitterStyleUrl, ResourceHandler.FromString(css, mimeType: "text/css"));
|
||||
|
||||
CefScriptExecutor.RunFile(frame, "twitter.js");
|
||||
}
|
||||
|
||||
if (!TwitterUrls.IsTwitterLogin2Factor(url)){
|
||||
if (TwitterUrls.IsTweetDeck(url) || (TwitterUrls.IsTwitter(url) && !TwitterUrls.IsTwitterLogin2Factor(url))) {
|
||||
frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorOverride);
|
||||
}
|
||||
}
|
||||
@@ -145,11 +146,7 @@ namespace TweetDuck.Browser{
|
||||
if (frame.IsMain) {
|
||||
if (TwitterUrls.IsTweetDeck(url)) {
|
||||
UpdateProperties();
|
||||
CefScriptExecutor.RunFile(frame, "code.js");
|
||||
|
||||
InjectBrowserCSS();
|
||||
ReinjectCustomCSS(Config.CustomBrowserCSS);
|
||||
Config_SoundNotificationInfoChanged(null, EventArgs.Empty);
|
||||
CefScriptExecutor.RunBootstrap(frame, NamespaceTweetDeck);
|
||||
|
||||
TweetDeckBridge.ResetStaticProperties();
|
||||
|
||||
@@ -158,15 +155,14 @@ namespace TweetDuck.Browser{
|
||||
}
|
||||
|
||||
if (Config.FirstRun) {
|
||||
CefScriptExecutor.RunFile(frame, "introduction.js");
|
||||
CefScriptExecutor.RunBootstrap(frame, "introduction");
|
||||
}
|
||||
}
|
||||
else if (TwitterUrls.IsTwitter(url)) {
|
||||
CefScriptExecutor.RunBootstrap(frame, "login");
|
||||
}
|
||||
|
||||
CefScriptExecutor.RunFile(frame, "update.js");
|
||||
}
|
||||
|
||||
if (url == ErrorUrl){
|
||||
resourceHandlerFactory.UnregisterHandler(ErrorUrl);
|
||||
CefScriptExecutor.RunBootstrap(frame, "update");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,16 +171,10 @@ namespace TweetDuck.Browser{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
|
||||
string errorPage = Program.Resources.LoadSilent("pages/error.html");
|
||||
|
||||
if (errorPage != null){
|
||||
if (!e.FailedUrl.StartsWith("td://", StringComparison.Ordinal)) {
|
||||
string errorName = Enum.GetName(typeof(CefErrorCode), e.ErrorCode);
|
||||
string errorTitle = StringUtils.ConvertPascalCaseToScreamingSnakeCase(errorName ?? string.Empty);
|
||||
|
||||
resourceHandlerFactory.RegisterHandler(ErrorUrl, ResourceHandler.FromString(errorPage.Replace("{err}", errorTitle)));
|
||||
browser.Load(ErrorUrl);
|
||||
}
|
||||
browser.Load("td://resources/error/error.html#" + Uri.EscapeDataString(errorTitle));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,14 +192,14 @@ namespace TweetDuck.Browser{
|
||||
prevSoundNotificationPath = newNotificationPath;
|
||||
|
||||
if (hasCustomSound) {
|
||||
resourceHandlerFactory.RegisterHandler(soundUrl, SoundNotification.CreateFileHandler(newNotificationPath));
|
||||
resourceHandlers.Register(soundUrl, SoundNotification.CreateFileHandler(newNotificationPath));
|
||||
}
|
||||
else {
|
||||
resourceHandlerFactory.UnregisterHandler(soundUrl);
|
||||
resourceHandlers.Unregister(soundUrl);
|
||||
}
|
||||
}
|
||||
|
||||
browser.ExecuteScriptAsync("TDGF_setSoundNotificationData", hasCustomSound, Config.NotificationSoundVolume);
|
||||
browser.ExecuteJsAsync("TDGF_setSoundNotificationData", hasCustomSound, Config.NotificationSoundVolume);
|
||||
}
|
||||
|
||||
// external handling
|
||||
@@ -219,57 +209,60 @@ namespace TweetDuck.Browser{
|
||||
browser.GetBrowser().GetHost().SendFocusEvent(true);
|
||||
}
|
||||
|
||||
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
|
||||
browser.ExecuteJsAsync("$('#td-video-player-overlay').remove()");
|
||||
}
|
||||
|
||||
// javascript calls
|
||||
|
||||
public void ReloadToTweetDeck() {
|
||||
browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUrls.TweetDeck}'");
|
||||
browser.ExecuteJsAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUrls.TweetDeck}'");
|
||||
}
|
||||
|
||||
public void OnModulesLoaded(string moduleNamespace) {
|
||||
if (moduleNamespace == NamespaceTweetDeck) {
|
||||
ReinjectCustomCSS(Config.CustomBrowserCSS);
|
||||
Config_SoundNotificationInfoChanged(null, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateProperties() {
|
||||
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Browser));
|
||||
}
|
||||
|
||||
public void InjectBrowserCSS(){
|
||||
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", Program.Resources.Load("styles/browser.css")?.TrimEnd() ?? string.Empty);
|
||||
browser.ExecuteJsAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Browser));
|
||||
}
|
||||
|
||||
public void ReinjectCustomCSS(string css) {
|
||||
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
|
||||
browser.ExecuteJsAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
|
||||
}
|
||||
|
||||
public void OnMouseClickExtra(IntPtr param) {
|
||||
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (param.ToInt32() >> 16) & 0xFFFF);
|
||||
browser.ExecuteJsAsync("TDGF_onMouseClickExtra", (param.ToInt32() >> 16) & 0xFFFF);
|
||||
}
|
||||
|
||||
public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl) {
|
||||
browser.ExecuteScriptAsync("TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
|
||||
browser.ExecuteJsAsync("TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
|
||||
}
|
||||
|
||||
public void AddSearchColumn(string query) {
|
||||
browser.ExecuteScriptAsync("TDGF_performSearch", query);
|
||||
browser.ExecuteJsAsync("TDGF_performSearch", query);
|
||||
}
|
||||
|
||||
public void TriggerTweetScreenshot(){
|
||||
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
|
||||
public void TriggerTweetScreenshot(string columnId, string chirpId) {
|
||||
browser.ExecuteJsAsync("TDGF_triggerScreenshot", columnId, chirpId);
|
||||
}
|
||||
|
||||
public void ReloadColumns() {
|
||||
browser.ExecuteScriptAsync("TDGF_reloadColumns()");
|
||||
browser.ExecuteJsAsync("TDGF_reloadColumns()");
|
||||
}
|
||||
|
||||
public void PlaySoundNotification() {
|
||||
browser.ExecuteScriptAsync("TDGF_playSoundNotification()");
|
||||
browser.ExecuteJsAsync("TDGF_playSoundNotification()");
|
||||
}
|
||||
|
||||
public void ApplyROT13() {
|
||||
browser.ExecuteScriptAsync("TDGF_applyROT13()");
|
||||
browser.ExecuteJsAsync("TDGF_applyROT13()");
|
||||
}
|
||||
|
||||
public void ShowUpdateNotification(string versionTag, string releaseNotes) {
|
||||
browser.ExecuteScriptAsync("TDUF_displayNotification", versionTag, Convert.ToBase64String(Encoding.GetEncoding("iso-8859-1").GetBytes(releaseNotes)));
|
||||
browser.ExecuteJsAsync("TDUF_displayNotification", versionTag, Convert.ToBase64String(Encoding.GetEncoding("iso-8859-1").GetBytes(releaseNotes)));
|
||||
}
|
||||
|
||||
public void OpenDevTools() {
|
||||
|
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using TweetDuck.Browser.Data;
|
||||
using TweetLib.Core.Features.Configuration;
|
||||
using TweetLib.Core.Features.Plugins.Config;
|
||||
using TweetLib.Core.Serialization.Converters;
|
||||
using TweetLib.Core.Systems.Configuration;
|
||||
using TweetLib.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Configuration {
|
||||
|
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TweetLib.Core.Features.Configuration;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
using TweetLib.Core.Features.Plugins.Config;
|
||||
using TweetLib.Core.Features.Plugins.Events;
|
||||
using TweetLib.Core.Systems.Configuration;
|
||||
|
||||
namespace TweetDuck.Configuration {
|
||||
sealed class PluginConfig : BaseConfig, IPluginConfig {
|
||||
|
@@ -1,11 +1,10 @@
|
||||
using TweetLib.Core.Features.Configuration;
|
||||
using TweetLib.Core.Systems.Configuration;
|
||||
|
||||
namespace TweetDuck.Configuration {
|
||||
sealed class SystemConfig : BaseConfig {
|
||||
|
||||
// CONFIGURATION DATA
|
||||
|
||||
public bool _hardwareAcceleration = true;
|
||||
private bool _hardwareAcceleration = true;
|
||||
|
||||
public bool ClearCacheAutomatically { get; set; } = true;
|
||||
public int ClearCacheThreshold { get; set; } = 250;
|
||||
|
@@ -1,18 +1,20 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Drawing;
|
||||
using TweetDuck.Browser;
|
||||
using TweetDuck.Browser.Data;
|
||||
using TweetDuck.Controls;
|
||||
using TweetLib.Core.Features.Configuration;
|
||||
using TweetLib.Core.Features.Notifications;
|
||||
using TweetLib.Core.Features.Twitter;
|
||||
using TweetLib.Core.Systems.Configuration;
|
||||
|
||||
namespace TweetDuck.Configuration {
|
||||
sealed class UserConfig : BaseConfig {
|
||||
|
||||
// CONFIGURATION DATA
|
||||
|
||||
public bool FirstRun { get; set; } = true;
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public bool AllowDataCollection { get; set; } = false;
|
||||
|
||||
public WindowState BrowserWindow { get; set; } = new WindowState();
|
||||
@@ -27,6 +29,7 @@ namespace TweetDuck.Configuration{
|
||||
|
||||
private bool _enableSmoothScrolling = true;
|
||||
private bool _enableTouchAdjustment = false;
|
||||
private bool _enableColorProfileDetection = false;
|
||||
private string _customCefArgs = null;
|
||||
|
||||
public string BrowserPath { get; set; } = null;
|
||||
@@ -79,6 +82,9 @@ namespace TweetDuck.Configuration{
|
||||
public string CustomBrowserCSS { get; set; } = null;
|
||||
public string CustomNotificationCSS { get; set; } = null;
|
||||
|
||||
private bool _useSystemProxyForAllConnections;
|
||||
|
||||
public bool DevToolsInContextMenu { get; set; } = false;
|
||||
public bool DevToolsWindowOnTop { get; set; } = true;
|
||||
|
||||
// SPECIAL PROPERTIES
|
||||
@@ -124,6 +130,16 @@ namespace TweetDuck.Configuration{
|
||||
set => UpdatePropertyWithRestartRequest(ref _enableTouchAdjustment, value);
|
||||
}
|
||||
|
||||
public bool EnableColorProfileDetection {
|
||||
get => _enableColorProfileDetection;
|
||||
set => UpdatePropertyWithRestartRequest(ref _enableColorProfileDetection, value);
|
||||
}
|
||||
|
||||
public bool UseSystemProxyForAllConnections {
|
||||
get => _useSystemProxyForAllConnections;
|
||||
set => UpdatePropertyWithRestartRequest(ref _useSystemProxyForAllConnections, value);
|
||||
}
|
||||
|
||||
public string CustomCefArgs {
|
||||
get => _customCefArgs;
|
||||
set => UpdatePropertyWithRestartRequest(ref _customCefArgs, value);
|
||||
|
@@ -1,10 +1,8 @@
|
||||
using System.Drawing;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using TweetDuck.Browser;
|
||||
using TweetDuck.Browser.Adapters;
|
||||
using TweetDuck.Browser.Data;
|
||||
using TweetDuck.Browser.Handling;
|
||||
using TweetDuck.Browser.Handling.General;
|
||||
@@ -14,39 +12,18 @@ using TweetDuck.Utils;
|
||||
|
||||
namespace TweetDuck.Dialogs {
|
||||
sealed partial class FormGuide : Form, FormManager.IAppDialog {
|
||||
private const string GuideUrl = "https://tweetduck.chylex.com/guide/v2/";
|
||||
private const string GuidePathRegex = @"^guide(?:/v\d+)?(?:/(#.*))?";
|
||||
private const string GuideUrl = @"td://guide/index.html";
|
||||
|
||||
private static readonly ResourceLink DummyPage = new ResourceLink("http://td/dummy", ResourceHandler.FromString(""));
|
||||
|
||||
public static bool CheckGuideUrl(string url, out string hash){
|
||||
if (!url.Contains("//tweetduck.chylex.com/guide")){
|
||||
hash = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
string path = url.Substring(url.IndexOf("/guide")+1);
|
||||
Match match = Regex.Match(path, GuidePathRegex);
|
||||
|
||||
if (match.Success){
|
||||
hash = match.Groups[1].Value;
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
hash = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private static readonly ResourceLink DummyPage = new ResourceLink("http://td/dummy", ResourceHandlers.ForString(string.Empty));
|
||||
|
||||
public static void Show(string hash = null) {
|
||||
string url = GuideUrl + (hash ?? string.Empty);
|
||||
string url = GuideUrl + (string.IsNullOrEmpty(hash) ? string.Empty : "#" + hash);
|
||||
FormGuide guide = FormManager.TryFind<FormGuide>();
|
||||
|
||||
if (guide == null) {
|
||||
FormBrowser owner = FormManager.TryFind<FormBrowser>();
|
||||
|
||||
if (owner != null) {
|
||||
owner.AnalyticsFile.OpenGuide.Trigger();
|
||||
new FormGuide(url, owner).Show(owner);
|
||||
}
|
||||
}
|
||||
@@ -69,20 +46,19 @@ namespace TweetDuck.Dialogs{
|
||||
Size = new Size(owner.Size.Width * 3 / 4, owner.Size.Height * 3 / 4);
|
||||
VisibleChanged += (sender, args) => this.MoveToCenter(owner);
|
||||
|
||||
ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory();
|
||||
resourceHandlerFactory.RegisterHandler(DummyPage);
|
||||
var resourceRequestHandler = new ResourceRequestHandlerBase();
|
||||
resourceRequestHandler.ResourceHandlers.Register(DummyPage);
|
||||
|
||||
this.browser = new ChromiumWebBrowser(url) {
|
||||
MenuHandler = new ContextMenuGuide(owner),
|
||||
MenuHandler = new ContextMenuGuide(),
|
||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||
KeyboardHandler = new KeyboardHandlerBase(),
|
||||
LifeSpanHandler = new LifeSpanHandler(),
|
||||
LifeSpanHandler = new CustomLifeSpanHandler(),
|
||||
RequestHandler = new RequestHandlerBase(true),
|
||||
ResourceHandlerFactory = resourceHandlerFactory
|
||||
ResourceRequestHandlerFactory = resourceRequestHandler.SelfFactory
|
||||
};
|
||||
|
||||
browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||
browser.FrameLoadEnd += browser_FrameLoadEnd;
|
||||
|
||||
browser.BrowserSettings.BackgroundColor = (uint) BackColor.ToArgb();
|
||||
browser.Dock = DockStyle.None;
|
||||
@@ -124,9 +100,5 @@ namespace TweetDuck.Dialogs{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||
CefScriptExecutor.RunScript(e.Frame, "Array.prototype.forEach.call(document.getElementsByTagName('A'), ele => ele.addEventListener('click', e => { e.preventDefault(); window.open(ele.getAttribute('href')); }))", "gen:links");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -28,18 +28,14 @@ namespace TweetDuck.Dialogs{
|
||||
Size = new Size(Math.Max(MinimumSize.Width, targetSize.Width), Math.Max(MinimumSize.Height, targetSize.Height));
|
||||
}
|
||||
|
||||
Shown += (sender, args) => {
|
||||
ReloadPluginList();
|
||||
};
|
||||
Shown += (sender, args) => { ReloadPluginList(); };
|
||||
|
||||
FormClosed += (sender, args) => {
|
||||
Config.PluginsWindowSize = Size;
|
||||
Config.Save();
|
||||
};
|
||||
|
||||
ResizeEnd += (sender, args) => {
|
||||
timerLayout.Start();
|
||||
};
|
||||
ResizeEnd += (sender, args) => { timerLayout.Start(); };
|
||||
}
|
||||
|
||||
private int GetPluginOrderIndex(Plugin plugin) {
|
||||
@@ -74,7 +70,7 @@ namespace TweetDuck.Dialogs{
|
||||
Padding = new Padding(Padding.Left, Padding.Top, Padding.Right - 1, Padding.Bottom - 1);
|
||||
}
|
||||
|
||||
public void flowLayoutPlugins_Resize(object sender, EventArgs e){
|
||||
private void flowLayoutPlugins_Resize(object sender, EventArgs e) {
|
||||
Control lastPlugin = flowLayoutPlugins.Controls.OfType<PluginControl>().LastOrDefault();
|
||||
|
||||
if (lastPlugin == null) {
|
||||
|
@@ -9,10 +9,9 @@ using TweetDuck.Configuration;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Dialogs.Settings;
|
||||
using TweetDuck.Management;
|
||||
using TweetDuck.Management.Analytics;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
using TweetLib.Core.Features.Updates;
|
||||
using TweetLib.Core.Systems.Updates;
|
||||
|
||||
namespace TweetDuck.Dialogs {
|
||||
sealed partial class FormSettings : Form, FormManager.IAppDialog {
|
||||
@@ -26,7 +25,7 @@ namespace TweetDuck.Dialogs{
|
||||
private readonly Dictionary<Type, SettingsTab> tabs = new Dictionary<Type, SettingsTab>(8);
|
||||
private SettingsTab currentTab;
|
||||
|
||||
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, AnalyticsManager analytics, Type startTab){
|
||||
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, Type startTab) {
|
||||
InitializeComponent();
|
||||
|
||||
Text = Program.BrandName + " Options";
|
||||
@@ -44,7 +43,7 @@ namespace TweetDuck.Dialogs{
|
||||
AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins)));
|
||||
AddButton("Sounds", () => new TabSettingsSounds(this.browser.PlaySoundNotification));
|
||||
AddButton("Tray", () => new TabSettingsTray());
|
||||
AddButton("Feedback", () => new TabSettingsFeedback(analytics, AnalyticsReportGenerator.ExternalInfo.From(this.browser), this.plugins));
|
||||
AddButton("Feedback", () => new TabSettingsFeedback());
|
||||
AddButton("Advanced", () => new TabSettingsAdvanced(this.browser.ReinjectCustomCSS, this.browser.OpenDevTools));
|
||||
|
||||
SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]);
|
||||
@@ -209,7 +208,7 @@ namespace TweetDuck.Dialogs{
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class BaseTab : UserControl{
|
||||
internal class BaseTab : UserControl {
|
||||
protected static UserConfig Config => Program.Config.User;
|
||||
protected static SystemConfig SysConfig => Program.Config.System;
|
||||
|
||||
|
94
Dialogs/Settings/DialogSettingsAnalytics.Designer.cs
generated
94
Dialogs/Settings/DialogSettingsAnalytics.Designer.cs
generated
@@ -1,94 +0,0 @@
|
||||
namespace TweetDuck.Dialogs.Settings {
|
||||
partial class DialogSettingsAnalytics {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.textBoxReport = new System.Windows.Forms.TextBox();
|
||||
this.btnClose = new System.Windows.Forms.Button();
|
||||
this.labelInfo = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// textBoxReport
|
||||
//
|
||||
this.textBoxReport.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.textBoxReport.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular);
|
||||
this.textBoxReport.Location = new System.Drawing.Point(12, 45);
|
||||
this.textBoxReport.Multiline = true;
|
||||
this.textBoxReport.Name = "textBoxReport";
|
||||
this.textBoxReport.ReadOnly = true;
|
||||
this.textBoxReport.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||
this.textBoxReport.Size = new System.Drawing.Size(435, 474);
|
||||
this.textBoxReport.TabIndex = 1;
|
||||
//
|
||||
// btnClose
|
||||
//
|
||||
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnClose.AutoSize = true;
|
||||
this.btnClose.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular);
|
||||
this.btnClose.Location = new System.Drawing.Point(397, 525);
|
||||
this.btnClose.Name = "btnClose";
|
||||
this.btnClose.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.btnClose.Size = new System.Drawing.Size(50, 25);
|
||||
this.btnClose.TabIndex = 2;
|
||||
this.btnClose.Text = "Close";
|
||||
this.btnClose.UseVisualStyleBackColor = true;
|
||||
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
|
||||
//
|
||||
// labelInfo
|
||||
//
|
||||
this.labelInfo.AutoSize = true;
|
||||
this.labelInfo.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular);
|
||||
this.labelInfo.Location = new System.Drawing.Point(12, 9);
|
||||
this.labelInfo.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3);
|
||||
this.labelInfo.Name = "labelInfo";
|
||||
this.labelInfo.Size = new System.Drawing.Size(434, 30);
|
||||
this.labelInfo.TabIndex = 0;
|
||||
this.labelInfo.Text = "When enabled, this data will be sent over a secure network roughly every 14 days." +
|
||||
"\r\nSome numbers in the report were made imprecise on purpose.";
|
||||
//
|
||||
// DialogSettingsAnalytics
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(459, 562);
|
||||
this.Controls.Add(this.labelInfo);
|
||||
this.Controls.Add(this.btnClose);
|
||||
this.Controls.Add(this.textBoxReport);
|
||||
this.MinimumSize = new System.Drawing.Size(475, 340);
|
||||
this.Name = "DialogSettingsAnalytics";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.TextBox textBoxReport;
|
||||
private System.Windows.Forms.Button btnClose;
|
||||
private System.Windows.Forms.Label labelInfo;
|
||||
}
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Management.Analytics;
|
||||
|
||||
namespace TweetDuck.Dialogs.Settings{
|
||||
sealed partial class DialogSettingsAnalytics : Form{
|
||||
public DialogSettingsAnalytics(AnalyticsReport report){
|
||||
InitializeComponent();
|
||||
|
||||
Text = Program.BrandName + " Options - Analytics Report";
|
||||
|
||||
textBoxReport.EnableMultilineShortcuts();
|
||||
textBoxReport.Text = report.ToString().TrimEnd();
|
||||
textBoxReport.Select(0, 0);
|
||||
}
|
||||
|
||||
private void btnClose_Click(object sender, EventArgs e){
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,7 +3,6 @@ using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Utils;
|
||||
|
||||
namespace TweetDuck.Dialogs.Settings {
|
||||
sealed partial class DialogSettingsCSS : Form {
|
||||
@@ -27,10 +26,6 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
textBoxNotificationCSS.EnableMultilineShortcuts();
|
||||
textBoxNotificationCSS.Text = notificationCSS ?? "";
|
||||
|
||||
if (!BrowserUtils.HasDevTools){
|
||||
btnOpenDevTools.Enabled = false;
|
||||
}
|
||||
|
||||
ActiveControl = textBoxBrowserCSS;
|
||||
textBoxBrowserCSS.Select(textBoxBrowserCSS.TextLength, 0);
|
||||
}
|
||||
|
@@ -10,7 +10,10 @@ using TweetLib.Core.Utils;
|
||||
namespace TweetDuck.Dialogs.Settings {
|
||||
sealed partial class DialogSettingsManage : Form {
|
||||
private enum State {
|
||||
Deciding, Reset, Import, Export
|
||||
Deciding,
|
||||
Reset,
|
||||
Import,
|
||||
Export
|
||||
}
|
||||
|
||||
private ProfileManager.Items SelectedItems {
|
||||
|
54
Dialogs/Settings/TabSettingsAdvanced.Designer.cs
generated
54
Dialogs/Settings/TabSettingsAdvanced.Designer.cs
generated
@@ -41,7 +41,10 @@
|
||||
this.panelConfiguration = new System.Windows.Forms.Panel();
|
||||
this.labelConfiguration = new System.Windows.Forms.Label();
|
||||
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.labelProxy = new System.Windows.Forms.Label();
|
||||
this.checkUseSystemProxyForAllConnections = new System.Windows.Forms.CheckBox();
|
||||
this.labelDevTools = new System.Windows.Forms.Label();
|
||||
this.checkDevToolsInContextMenu = new System.Windows.Forms.CheckBox();
|
||||
this.checkDevToolsWindowOnTop = new System.Windows.Forms.CheckBox();
|
||||
((System.ComponentModel.ISupportInitialize)(this.numClearCacheThreshold)).BeginInit();
|
||||
this.panelAppButtons.SuspendLayout();
|
||||
@@ -242,7 +245,10 @@
|
||||
this.flowPanel.Controls.Add(this.panelClearCacheAuto);
|
||||
this.flowPanel.Controls.Add(this.labelConfiguration);
|
||||
this.flowPanel.Controls.Add(this.panelConfiguration);
|
||||
this.flowPanel.Controls.Add(this.labelProxy);
|
||||
this.flowPanel.Controls.Add(this.checkUseSystemProxyForAllConnections);
|
||||
this.flowPanel.Controls.Add(this.labelDevTools);
|
||||
this.flowPanel.Controls.Add(this.checkDevToolsInContextMenu);
|
||||
this.flowPanel.Controls.Add(this.checkDevToolsWindowOnTop);
|
||||
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
|
||||
this.flowPanel.Location = new System.Drawing.Point(9, 9);
|
||||
@@ -251,26 +257,61 @@
|
||||
this.flowPanel.TabIndex = 0;
|
||||
this.flowPanel.WrapContents = false;
|
||||
//
|
||||
// labelProxy
|
||||
//
|
||||
this.labelProxy.AutoSize = true;
|
||||
this.labelProxy.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
|
||||
this.labelProxy.Location = new System.Drawing.Point(0, 302);
|
||||
this.labelProxy.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1);
|
||||
this.labelProxy.Name = "labelProxy";
|
||||
this.labelProxy.Size = new System.Drawing.Size(54, 19);
|
||||
this.labelProxy.TabIndex = 7;
|
||||
this.labelProxy.Text = "PROXY";
|
||||
//
|
||||
// checkUseSystemProxyForAllConnections
|
||||
//
|
||||
this.checkUseSystemProxyForAllConnections.AutoSize = true;
|
||||
this.checkUseSystemProxyForAllConnections.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.checkUseSystemProxyForAllConnections.Location = new System.Drawing.Point(6, 328);
|
||||
this.checkUseSystemProxyForAllConnections.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
|
||||
this.checkUseSystemProxyForAllConnections.Name = "checkUseSystemProxyForAllConnections";
|
||||
this.checkUseSystemProxyForAllConnections.Size = new System.Drawing.Size(223, 19);
|
||||
this.checkUseSystemProxyForAllConnections.TabIndex = 8;
|
||||
this.checkUseSystemProxyForAllConnections.Text = "Use System Proxy for All Connections";
|
||||
this.checkUseSystemProxyForAllConnections.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// labelDevTools
|
||||
//
|
||||
this.labelDevTools.AutoSize = true;
|
||||
this.labelDevTools.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
|
||||
this.labelDevTools.Location = new System.Drawing.Point(0, 302);
|
||||
this.labelDevTools.Location = new System.Drawing.Point(0, 379);
|
||||
this.labelDevTools.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1);
|
||||
this.labelDevTools.Name = "labelDevTools";
|
||||
this.labelDevTools.Size = new System.Drawing.Size(156, 19);
|
||||
this.labelDevTools.TabIndex = 7;
|
||||
this.labelDevTools.TabIndex = 9;
|
||||
this.labelDevTools.Text = "DEVELOPMENT TOOLS";
|
||||
//
|
||||
// checkDevToolsInContextMenu
|
||||
//
|
||||
this.checkDevToolsInContextMenu.AutoSize = true;
|
||||
this.checkDevToolsInContextMenu.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.checkDevToolsInContextMenu.Location = new System.Drawing.Point(6, 405);
|
||||
this.checkDevToolsInContextMenu.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
|
||||
this.checkDevToolsInContextMenu.Name = "checkDevToolsInContextMenu";
|
||||
this.checkDevToolsInContextMenu.Size = new System.Drawing.Size(201, 19);
|
||||
this.checkDevToolsInContextMenu.TabIndex = 10;
|
||||
this.checkDevToolsInContextMenu.Text = "Show Dev Tools in Context Menu";
|
||||
this.checkDevToolsInContextMenu.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// checkDevToolsWindowOnTop
|
||||
//
|
||||
this.checkDevToolsWindowOnTop.AutoSize = true;
|
||||
this.checkDevToolsWindowOnTop.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.checkDevToolsWindowOnTop.Location = new System.Drawing.Point(6, 328);
|
||||
this.checkDevToolsWindowOnTop.Margin = new System.Windows.Forms.Padding(6, 6, 0, 2);
|
||||
this.checkDevToolsWindowOnTop.Location = new System.Drawing.Point(6, 429);
|
||||
this.checkDevToolsWindowOnTop.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
||||
this.checkDevToolsWindowOnTop.Name = "checkDevToolsWindowOnTop";
|
||||
this.checkDevToolsWindowOnTop.Size = new System.Drawing.Size(168, 19);
|
||||
this.checkDevToolsWindowOnTop.TabIndex = 8;
|
||||
this.checkDevToolsWindowOnTop.TabIndex = 11;
|
||||
this.checkDevToolsWindowOnTop.Text = "Dev Tools Window On Top";
|
||||
this.checkDevToolsWindowOnTop.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -312,6 +353,9 @@
|
||||
private System.Windows.Forms.CheckBox checkClearCacheAuto;
|
||||
private System.Windows.Forms.FlowLayoutPanel flowPanel;
|
||||
private System.Windows.Forms.Label labelDevTools;
|
||||
private System.Windows.Forms.CheckBox checkDevToolsInContextMenu;
|
||||
private System.Windows.Forms.CheckBox checkDevToolsWindowOnTop;
|
||||
private System.Windows.Forms.Label labelProxy;
|
||||
private System.Windows.Forms.CheckBox checkUseSystemProxyForAllConnections;
|
||||
}
|
||||
}
|
||||
|
@@ -44,15 +44,19 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
toolTip.SetToolTip(btnEditCefArgs, "Set custom command line arguments for Chromium Embedded Framework.");
|
||||
toolTip.SetToolTip(btnEditCSS, "Set custom CSS for browser and notification windows.");
|
||||
|
||||
// proxy
|
||||
|
||||
toolTip.SetToolTip(checkUseSystemProxyForAllConnections, "Sets whether all connections should automatically detect and use the system proxy.\r\nBy default, only the browser component uses the system proxy, while other parts (such as update checks) ignore it.\r\nDisabled by default because Windows' proxy detection can be really slow.");
|
||||
|
||||
checkUseSystemProxyForAllConnections.Checked = Config.UseSystemProxyForAllConnections;
|
||||
|
||||
// development tools
|
||||
|
||||
toolTip.SetToolTip(checkDevToolsInContextMenu, "Sets whether all context menus include an option to open dev tools.");
|
||||
toolTip.SetToolTip(checkDevToolsWindowOnTop, "Sets whether dev tool windows appears on top of other windows.");
|
||||
|
||||
checkDevToolsInContextMenu.Checked = Config.DevToolsInContextMenu;
|
||||
checkDevToolsWindowOnTop.Checked = Config.DevToolsWindowOnTop;
|
||||
|
||||
if (!BrowserUtils.HasDevTools){
|
||||
checkDevToolsWindowOnTop.Enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnReady() {
|
||||
@@ -67,7 +71,10 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
btnEditCefArgs.Click += btnEditCefArgs_Click;
|
||||
btnEditCSS.Click += btnEditCSS_Click;
|
||||
|
||||
checkUseSystemProxyForAllConnections.CheckedChanged += checkUseSystemProxyForAllConnections_CheckedChanged;
|
||||
|
||||
checkDevToolsWindowOnTop.CheckedChanged += checkDevToolsWindowOnTop_CheckedChanged;
|
||||
checkDevToolsInContextMenu.CheckedChanged += checkDevToolsInContextMenuOnCheckedChanged;
|
||||
}
|
||||
|
||||
public override void OnClosing() {
|
||||
@@ -98,6 +105,7 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Browser Cache
|
||||
|
||||
private void btnClearCache_Click(object sender, EventArgs e) {
|
||||
@@ -111,14 +119,13 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Configuration
|
||||
|
||||
private void btnEditCefArgs_Click(object sender, EventArgs e) {
|
||||
DialogSettingsCefArgs form = new DialogSettingsCefArgs(Config.CustomCefArgs);
|
||||
|
||||
form.VisibleChanged += (sender2, args2) => {
|
||||
form.MoveToCenter(ParentForm);
|
||||
};
|
||||
form.VisibleChanged += (sender2, args2) => { form.MoveToCenter(ParentForm); };
|
||||
|
||||
form.FormClosed += (sender2, args2) => {
|
||||
RestoreParentForm();
|
||||
@@ -137,9 +144,7 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
private void btnEditCSS_Click(object sender, EventArgs e) {
|
||||
DialogSettingsCSS form = new DialogSettingsCSS(Config.CustomBrowserCSS, Config.CustomNotificationCSS, reinjectBrowserCSS, openDevTools);
|
||||
|
||||
form.VisibleChanged += (sender2, args2) => {
|
||||
form.MoveToCenter(ParentForm);
|
||||
};
|
||||
form.VisibleChanged += (sender2, args2) => { form.MoveToCenter(ParentForm); };
|
||||
|
||||
form.FormClosed += (sender2, args2) => {
|
||||
RestoreParentForm();
|
||||
@@ -164,8 +169,21 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Proxy
|
||||
|
||||
private void checkUseSystemProxyForAllConnections_CheckedChanged(object sender, EventArgs e) {
|
||||
Config.UseSystemProxyForAllConnections = checkUseSystemProxyForAllConnections.Checked;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Development Tools
|
||||
|
||||
private void checkDevToolsInContextMenuOnCheckedChanged(object sender, EventArgs e) {
|
||||
Config.DevToolsInContextMenu = checkDevToolsInContextMenu.Checked;
|
||||
}
|
||||
|
||||
private void checkDevToolsWindowOnTop_CheckedChanged(object sender, EventArgs e) {
|
||||
Config.DevToolsWindowOnTop = checkDevToolsWindowOnTop.Checked;
|
||||
}
|
||||
|
125
Dialogs/Settings/TabSettingsFeedback.Designer.cs
generated
125
Dialogs/Settings/TabSettingsFeedback.Designer.cs
generated
@@ -24,80 +24,13 @@
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.panelDataCollection = new System.Windows.Forms.Panel();
|
||||
this.labelDataCollectionLink = new System.Windows.Forms.LinkLabel();
|
||||
this.checkDataCollection = new System.Windows.Forms.CheckBox();
|
||||
this.labelDataCollectionMessage = new System.Windows.Forms.Label();
|
||||
this.btnViewReport = new System.Windows.Forms.Button();
|
||||
this.btnSendFeedback = new System.Windows.Forms.Button();
|
||||
this.labelDataCollection = new System.Windows.Forms.Label();
|
||||
this.labelFeedback = new System.Windows.Forms.Label();
|
||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.panelDataCollection.SuspendLayout();
|
||||
this.labelFeedback = new System.Windows.Forms.Label();
|
||||
this.flowPanel.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// panelDataCollection
|
||||
//
|
||||
this.panelDataCollection.Controls.Add(this.labelDataCollectionLink);
|
||||
this.panelDataCollection.Controls.Add(this.checkDataCollection);
|
||||
this.panelDataCollection.Location = new System.Drawing.Point(0, 78);
|
||||
this.panelDataCollection.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.panelDataCollection.Name = "panelDataCollection";
|
||||
this.panelDataCollection.Size = new System.Drawing.Size(300, 28);
|
||||
this.panelDataCollection.TabIndex = 3;
|
||||
//
|
||||
// labelDataCollectionLink
|
||||
//
|
||||
this.labelDataCollectionLink.AutoSize = true;
|
||||
this.labelDataCollectionLink.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.labelDataCollectionLink.LinkArea = new System.Windows.Forms.LinkArea(1, 10);
|
||||
this.labelDataCollectionLink.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline;
|
||||
this.labelDataCollectionLink.Location = new System.Drawing.Point(153, 4);
|
||||
this.labelDataCollectionLink.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.labelDataCollectionLink.Name = "labelDataCollectionLink";
|
||||
this.labelDataCollectionLink.Size = new System.Drawing.Size(71, 21);
|
||||
this.labelDataCollectionLink.TabIndex = 1;
|
||||
this.labelDataCollectionLink.TabStop = true;
|
||||
this.labelDataCollectionLink.Text = "(learn more)";
|
||||
this.labelDataCollectionLink.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
this.labelDataCollectionLink.UseCompatibleTextRendering = true;
|
||||
//
|
||||
// checkDataCollection
|
||||
//
|
||||
this.checkDataCollection.AutoSize = true;
|
||||
this.checkDataCollection.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.checkDataCollection.Location = new System.Drawing.Point(6, 6);
|
||||
this.checkDataCollection.Margin = new System.Windows.Forms.Padding(6, 6, 0, 2);
|
||||
this.checkDataCollection.Name = "checkDataCollection";
|
||||
this.checkDataCollection.Size = new System.Drawing.Size(147, 19);
|
||||
this.checkDataCollection.TabIndex = 0;
|
||||
this.checkDataCollection.Text = "Send Anonymous Data";
|
||||
this.checkDataCollection.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// labelDataCollectionMessage
|
||||
//
|
||||
this.labelDataCollectionMessage.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.labelDataCollectionMessage.Location = new System.Drawing.Point(6, 143);
|
||||
this.labelDataCollectionMessage.Margin = new System.Windows.Forms.Padding(6);
|
||||
this.labelDataCollectionMessage.Name = "labelDataCollectionMessage";
|
||||
this.labelDataCollectionMessage.Size = new System.Drawing.Size(288, 67);
|
||||
this.labelDataCollectionMessage.TabIndex = 5;
|
||||
//
|
||||
// btnViewReport
|
||||
//
|
||||
this.btnViewReport.AutoSize = true;
|
||||
this.btnViewReport.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.btnViewReport.Location = new System.Drawing.Point(5, 109);
|
||||
this.btnViewReport.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||
this.btnViewReport.Name = "btnViewReport";
|
||||
this.btnViewReport.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.btnViewReport.Size = new System.Drawing.Size(155, 25);
|
||||
this.btnViewReport.TabIndex = 4;
|
||||
this.btnViewReport.Text = "View My Analytics Report";
|
||||
this.btnViewReport.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnSendFeedback
|
||||
//
|
||||
this.btnSendFeedback.AutoSize = true;
|
||||
@@ -111,16 +44,17 @@
|
||||
this.btnSendFeedback.Text = "Send Feedback / Bug Report";
|
||||
this.btnSendFeedback.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// labelDataCollection
|
||||
// flowPanel
|
||||
//
|
||||
this.labelDataCollection.AutoSize = true;
|
||||
this.labelDataCollection.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
|
||||
this.labelDataCollection.Location = new System.Drawing.Point(3, 63);
|
||||
this.labelDataCollection.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||
this.labelDataCollection.Name = "labelDataCollection";
|
||||
this.labelDataCollection.Size = new System.Drawing.Size(88, 15);
|
||||
this.labelDataCollection.TabIndex = 2;
|
||||
this.labelDataCollection.Text = "Data Collection";
|
||||
this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles) ((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.flowPanel.Controls.Add(this.labelFeedback);
|
||||
this.flowPanel.Controls.Add(this.btnSendFeedback);
|
||||
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
|
||||
this.flowPanel.Location = new System.Drawing.Point(9, 9);
|
||||
this.flowPanel.Name = "flowPanel";
|
||||
this.flowPanel.Size = new System.Drawing.Size(300, 462);
|
||||
this.flowPanel.TabIndex = 0;
|
||||
this.flowPanel.WrapContents = false;
|
||||
//
|
||||
// labelFeedback
|
||||
//
|
||||
@@ -133,24 +67,6 @@
|
||||
this.labelFeedback.TabIndex = 0;
|
||||
this.labelFeedback.Text = "FEEDBACK";
|
||||
//
|
||||
// flowPanel
|
||||
//
|
||||
this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.flowPanel.Controls.Add(this.labelFeedback);
|
||||
this.flowPanel.Controls.Add(this.btnSendFeedback);
|
||||
this.flowPanel.Controls.Add(this.labelDataCollection);
|
||||
this.flowPanel.Controls.Add(this.panelDataCollection);
|
||||
this.flowPanel.Controls.Add(this.btnViewReport);
|
||||
this.flowPanel.Controls.Add(this.labelDataCollectionMessage);
|
||||
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
|
||||
this.flowPanel.Location = new System.Drawing.Point(9, 9);
|
||||
this.flowPanel.Name = "flowPanel";
|
||||
this.flowPanel.Size = new System.Drawing.Size(300, 462);
|
||||
this.flowPanel.TabIndex = 0;
|
||||
this.flowPanel.WrapContents = false;
|
||||
//
|
||||
// TabSettingsFeedback
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
@@ -158,25 +74,16 @@
|
||||
this.Controls.Add(this.flowPanel);
|
||||
this.Name = "TabSettingsFeedback";
|
||||
this.Size = new System.Drawing.Size(631, 480);
|
||||
this.panelDataCollection.ResumeLayout(false);
|
||||
this.panelDataCollection.PerformLayout();
|
||||
this.flowPanel.ResumeLayout(false);
|
||||
this.flowPanel.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
private System.Windows.Forms.ToolTip toolTip;
|
||||
private System.Windows.Forms.Button btnSendFeedback;
|
||||
private System.Windows.Forms.FlowLayoutPanel flowPanel;
|
||||
private System.Windows.Forms.Label labelFeedback;
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Panel panelDataCollection;
|
||||
private System.Windows.Forms.CheckBox checkDataCollection;
|
||||
private System.Windows.Forms.Label labelDataCollection;
|
||||
private System.Windows.Forms.Label labelFeedback;
|
||||
private System.Windows.Forms.ToolTip toolTip;
|
||||
private System.Windows.Forms.LinkLabel labelDataCollectionLink;
|
||||
private System.Windows.Forms.Button btnSendFeedback;
|
||||
private System.Windows.Forms.Button btnViewReport;
|
||||
private System.Windows.Forms.Label labelDataCollectionMessage;
|
||||
private System.Windows.Forms.FlowLayoutPanel flowPanel;
|
||||
}
|
||||
}
|
||||
|
@@ -1,37 +1,14 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using TweetDuck.Management.Analytics;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
|
||||
namespace TweetDuck.Dialogs.Settings {
|
||||
sealed partial class TabSettingsFeedback : FormSettings.BaseTab {
|
||||
private readonly AnalyticsFile analyticsFile;
|
||||
private readonly AnalyticsReportGenerator.ExternalInfo analyticsInfo;
|
||||
private readonly PluginManager plugins;
|
||||
|
||||
public TabSettingsFeedback(AnalyticsManager analytics, AnalyticsReportGenerator.ExternalInfo analyticsInfo, PluginManager plugins){
|
||||
public TabSettingsFeedback() {
|
||||
InitializeComponent();
|
||||
|
||||
this.analyticsFile = analytics?.File ?? AnalyticsFile.Load(Program.AnalyticsFilePath);
|
||||
this.analyticsInfo = analyticsInfo;
|
||||
this.plugins = plugins;
|
||||
|
||||
// feedback
|
||||
|
||||
checkDataCollection.Checked = Config.AllowDataCollection;
|
||||
|
||||
if (analytics != null){
|
||||
string collectionTime = analyticsFile.LastCollectionMessage;
|
||||
labelDataCollectionMessage.Text = string.IsNullOrEmpty(collectionTime) ? "No collection yet" : "Last collection: " + collectionTime;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnReady() {
|
||||
btnSendFeedback.Click += btnSendFeedback_Click;
|
||||
checkDataCollection.CheckedChanged += checkDataCollection_CheckedChanged;
|
||||
labelDataCollectionLink.LinkClicked += labelDataCollectionLink_LinkClicked;
|
||||
btnViewReport.Click += btnViewReport_Click;
|
||||
}
|
||||
|
||||
#region Feedback
|
||||
@@ -40,19 +17,6 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/issues/new");
|
||||
}
|
||||
|
||||
private void checkDataCollection_CheckedChanged(object sender, EventArgs e){
|
||||
Config.AllowDataCollection = checkDataCollection.Checked;
|
||||
}
|
||||
|
||||
private void labelDataCollectionLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
|
||||
BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data");
|
||||
}
|
||||
|
||||
private void btnViewReport_Click(object sender, EventArgs e){
|
||||
using DialogSettingsAnalytics dialog = new DialogSettingsAnalytics(AnalyticsReportGenerator.Create(analyticsFile, analyticsInfo, plugins));
|
||||
dialog.ShowDialog();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
41
Dialogs/Settings/TabSettingsGeneral.Designer.cs
generated
41
Dialogs/Settings/TabSettingsGeneral.Designer.cs
generated
@@ -44,6 +44,7 @@
|
||||
this.labelBrowserSettings = new System.Windows.Forms.Label();
|
||||
this.checkSmoothScrolling = new System.Windows.Forms.CheckBox();
|
||||
this.checkTouchAdjustment = new System.Windows.Forms.CheckBox();
|
||||
this.checkAutomaticallyDetectColorProfile = new System.Windows.Forms.CheckBox();
|
||||
this.checkHardwareAcceleration = new System.Windows.Forms.CheckBox();
|
||||
this.labelBrowserPath = new System.Windows.Forms.Label();
|
||||
this.comboBoxCustomBrowser = new System.Windows.Forms.ComboBox();
|
||||
@@ -90,11 +91,11 @@
|
||||
//
|
||||
this.checkUpdateNotifications.AutoSize = true;
|
||||
this.checkUpdateNotifications.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 403);
|
||||
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 409);
|
||||
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
|
||||
this.checkUpdateNotifications.Name = "checkUpdateNotifications";
|
||||
this.checkUpdateNotifications.Size = new System.Drawing.Size(182, 19);
|
||||
this.checkUpdateNotifications.TabIndex = 14;
|
||||
this.checkUpdateNotifications.TabIndex = 15;
|
||||
this.checkUpdateNotifications.Text = "Check Updates Automatically";
|
||||
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -102,12 +103,12 @@
|
||||
//
|
||||
this.btnCheckUpdates.AutoSize = true;
|
||||
this.btnCheckUpdates.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.btnCheckUpdates.Location = new System.Drawing.Point(5, 427);
|
||||
this.btnCheckUpdates.Location = new System.Drawing.Point(5, 433);
|
||||
this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||
this.btnCheckUpdates.Name = "btnCheckUpdates";
|
||||
this.btnCheckUpdates.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.btnCheckUpdates.Size = new System.Drawing.Size(128, 25);
|
||||
this.btnCheckUpdates.TabIndex = 15;
|
||||
this.btnCheckUpdates.TabIndex = 16;
|
||||
this.btnCheckUpdates.Text = "Check Updates Now";
|
||||
this.btnCheckUpdates.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -166,11 +167,11 @@
|
||||
//
|
||||
this.labelZoom.AutoSize = true;
|
||||
this.labelZoom.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
|
||||
this.labelZoom.Location = new System.Drawing.Point(3, 299);
|
||||
this.labelZoom.Location = new System.Drawing.Point(3, 323);
|
||||
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||
this.labelZoom.Name = "labelZoom";
|
||||
this.labelZoom.Size = new System.Drawing.Size(39, 15);
|
||||
this.labelZoom.TabIndex = 11;
|
||||
this.labelZoom.TabIndex = 12;
|
||||
this.labelZoom.Text = "Zoom";
|
||||
//
|
||||
// zoomUpdateTimer
|
||||
@@ -193,11 +194,11 @@
|
||||
//
|
||||
this.panelZoom.Controls.Add(this.trackBarZoom);
|
||||
this.panelZoom.Controls.Add(this.labelZoomValue);
|
||||
this.panelZoom.Location = new System.Drawing.Point(0, 315);
|
||||
this.panelZoom.Location = new System.Drawing.Point(0, 339);
|
||||
this.panelZoom.Margin = new System.Windows.Forms.Padding(0, 1, 0, 0);
|
||||
this.panelZoom.Name = "panelZoom";
|
||||
this.panelZoom.Size = new System.Drawing.Size(300, 35);
|
||||
this.panelZoom.TabIndex = 12;
|
||||
this.panelZoom.TabIndex = 13;
|
||||
//
|
||||
// checkAnimatedAvatars
|
||||
//
|
||||
@@ -215,11 +216,11 @@
|
||||
//
|
||||
this.labelUpdates.AutoSize = true;
|
||||
this.labelUpdates.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
|
||||
this.labelUpdates.Location = new System.Drawing.Point(0, 377);
|
||||
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 27, 0, 1);
|
||||
this.labelUpdates.Location = new System.Drawing.Point(0, 383);
|
||||
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 7, 0, 1);
|
||||
this.labelUpdates.Name = "labelUpdates";
|
||||
this.labelUpdates.Size = new System.Drawing.Size(69, 19);
|
||||
this.labelUpdates.TabIndex = 13;
|
||||
this.labelUpdates.TabIndex = 14;
|
||||
this.labelUpdates.Text = "UPDATES";
|
||||
//
|
||||
// flowPanelLeft
|
||||
@@ -236,6 +237,7 @@
|
||||
this.flowPanelLeft.Controls.Add(this.labelBrowserSettings);
|
||||
this.flowPanelLeft.Controls.Add(this.checkSmoothScrolling);
|
||||
this.flowPanelLeft.Controls.Add(this.checkTouchAdjustment);
|
||||
this.flowPanelLeft.Controls.Add(this.checkAutomaticallyDetectColorProfile);
|
||||
this.flowPanelLeft.Controls.Add(this.checkHardwareAcceleration);
|
||||
this.flowPanelLeft.Controls.Add(this.labelZoom);
|
||||
this.flowPanelLeft.Controls.Add(this.panelZoom);
|
||||
@@ -308,15 +310,27 @@
|
||||
this.checkTouchAdjustment.Text = "Touch Screen Adjustment";
|
||||
this.checkTouchAdjustment.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// checkAutomaticallyDetectColorProfile
|
||||
//
|
||||
this.checkAutomaticallyDetectColorProfile.AutoSize = true;
|
||||
this.checkAutomaticallyDetectColorProfile.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.checkAutomaticallyDetectColorProfile.Location = new System.Drawing.Point(6, 266);
|
||||
this.checkAutomaticallyDetectColorProfile.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
||||
this.checkAutomaticallyDetectColorProfile.Name = "checkAutomaticallyDetectColorProfile";
|
||||
this.checkAutomaticallyDetectColorProfile.Size = new System.Drawing.Size(206, 19);
|
||||
this.checkAutomaticallyDetectColorProfile.TabIndex = 10;
|
||||
this.checkAutomaticallyDetectColorProfile.Text = "Automatically Detect Color Profile";
|
||||
this.checkAutomaticallyDetectColorProfile.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// checkHardwareAcceleration
|
||||
//
|
||||
this.checkHardwareAcceleration.AutoSize = true;
|
||||
this.checkHardwareAcceleration.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 266);
|
||||
this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 290);
|
||||
this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
||||
this.checkHardwareAcceleration.Name = "checkHardwareAcceleration";
|
||||
this.checkHardwareAcceleration.Size = new System.Drawing.Size(146, 19);
|
||||
this.checkHardwareAcceleration.TabIndex = 10;
|
||||
this.checkHardwareAcceleration.TabIndex = 11;
|
||||
this.checkHardwareAcceleration.Text = "Hardware Acceleration";
|
||||
this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -619,6 +633,7 @@
|
||||
private System.Windows.Forms.Label labelSearchEngine;
|
||||
private System.Windows.Forms.ComboBox comboBoxSearchEngine;
|
||||
private System.Windows.Forms.CheckBox checkTouchAdjustment;
|
||||
private System.Windows.Forms.CheckBox checkAutomaticallyDetectColorProfile;
|
||||
private System.Windows.Forms.FlowLayoutPanel flowPanelRight;
|
||||
private System.Windows.Forms.Panel panelSeparator;
|
||||
private System.Windows.Forms.Label labelLocales;
|
||||
|
@@ -5,7 +5,7 @@ using System.Windows.Forms;
|
||||
using TweetDuck.Browser.Handling.General;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Features.Updates;
|
||||
using TweetLib.Core.Systems.Updates;
|
||||
using TweetLib.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Dialogs.Settings {
|
||||
@@ -66,6 +66,7 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
|
||||
toolTip.SetToolTip(checkSmoothScrolling, "Toggles smooth mouse wheel scrolling.");
|
||||
toolTip.SetToolTip(checkTouchAdjustment, "Toggles Chromium touch screen adjustment.\r\nDisabled by default, because it is very imprecise with TweetDeck.");
|
||||
toolTip.SetToolTip(checkAutomaticallyDetectColorProfile, "Automatically detects the color profile of your system.\r\nUses the sRGB profile if disabled.");
|
||||
toolTip.SetToolTip(checkHardwareAcceleration, "Uses graphics card to improve performance.\r\nDisable if you experience visual glitches, or to save a small amount of RAM.");
|
||||
toolTip.SetToolTip(comboBoxCustomBrowser, "Sets the default browser for opening links.");
|
||||
toolTip.SetToolTip(comboBoxCustomVideoPlayer, "Sets the default application for playing videos.");
|
||||
@@ -73,6 +74,7 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
|
||||
checkSmoothScrolling.Checked = Config.EnableSmoothScrolling;
|
||||
checkTouchAdjustment.Checked = Config.EnableTouchAdjustment;
|
||||
checkAutomaticallyDetectColorProfile.Checked = Config.EnableColorProfileDetection;
|
||||
checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration;
|
||||
|
||||
foreach (WindowsUtils.Browser browserInfo in WindowsUtils.FindInstalledBrowsers()) {
|
||||
@@ -146,6 +148,7 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
|
||||
checkSmoothScrolling.CheckedChanged += checkSmoothScrolling_CheckedChanged;
|
||||
checkTouchAdjustment.CheckedChanged += checkTouchAdjustment_CheckedChanged;
|
||||
checkAutomaticallyDetectColorProfile.CheckedChanged += checkAutomaticallyDetectColorProfile_CheckedChanged;
|
||||
checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged;
|
||||
comboBoxCustomBrowser.SelectedIndexChanged += comboBoxCustomBrowser_SelectedIndexChanged;
|
||||
btnCustomBrowserChange.Click += btnCustomBrowserChange_Click;
|
||||
@@ -204,6 +207,7 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Updates
|
||||
|
||||
private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e) {
|
||||
@@ -232,6 +236,7 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Browser Settings
|
||||
|
||||
private void checkSmoothScrolling_CheckedChanged(object sender, EventArgs e) {
|
||||
@@ -242,6 +247,10 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
Config.EnableTouchAdjustment = checkTouchAdjustment.Checked;
|
||||
}
|
||||
|
||||
private void checkAutomaticallyDetectColorProfile_CheckedChanged(object sender, EventArgs e) {
|
||||
Config.EnableColorProfileDetection = checkAutomaticallyDetectColorProfile.Checked;
|
||||
}
|
||||
|
||||
private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e) {
|
||||
SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
|
||||
}
|
||||
@@ -385,6 +394,7 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Locales
|
||||
|
||||
private void checkSpellCheck_CheckedChanged(object sender, EventArgs e) {
|
||||
|
@@ -23,7 +23,6 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
});
|
||||
};
|
||||
|
||||
this.notification.Activated += notification_Activated;
|
||||
this.notification.Show();
|
||||
|
||||
Disposed += (sender, args) => this.notification.Dispose();
|
||||
@@ -69,11 +68,21 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
toolTip.SetToolTip(radioLocCustom, "Drag the example notification window to the desired location.");
|
||||
|
||||
switch (Config.NotificationPosition) {
|
||||
case DesktopNotification.Position.TopLeft: radioLocTL.Checked = true; break;
|
||||
case DesktopNotification.Position.TopRight: radioLocTR.Checked = true; break;
|
||||
case DesktopNotification.Position.BottomLeft: radioLocBL.Checked = true; break;
|
||||
case DesktopNotification.Position.BottomRight: radioLocBR.Checked = true; break;
|
||||
case DesktopNotification.Position.Custom: radioLocCustom.Checked = true; break;
|
||||
case DesktopNotification.Position.TopLeft:
|
||||
radioLocTL.Checked = true;
|
||||
break;
|
||||
case DesktopNotification.Position.TopRight:
|
||||
radioLocTR.Checked = true;
|
||||
break;
|
||||
case DesktopNotification.Position.BottomLeft:
|
||||
radioLocBL.Checked = true;
|
||||
break;
|
||||
case DesktopNotification.Position.BottomRight:
|
||||
radioLocBR.Checked = true;
|
||||
break;
|
||||
case DesktopNotification.Position.Custom:
|
||||
radioLocCustom.Checked = true;
|
||||
break;
|
||||
}
|
||||
|
||||
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked;
|
||||
@@ -94,8 +103,12 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
toolTip.SetToolTip(radioSizeCustom, "Resize the example notification window to the desired size.");
|
||||
|
||||
switch (Config.NotificationSize) {
|
||||
case DesktopNotification.Size.Auto: radioSizeAuto.Checked = true; break;
|
||||
case DesktopNotification.Size.Custom: radioSizeCustom.Checked = true; break;
|
||||
case DesktopNotification.Size.Auto:
|
||||
radioSizeAuto.Checked = true;
|
||||
break;
|
||||
case DesktopNotification.Size.Custom:
|
||||
radioSizeCustom.Checked = true;
|
||||
break;
|
||||
}
|
||||
|
||||
trackBarScrollSpeed.SetValueSafe(Config.NotificationScrollSpeed);
|
||||
@@ -139,11 +152,6 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
}
|
||||
}
|
||||
|
||||
private void notification_Activated(object sender, EventArgs e){
|
||||
notification.Hide();
|
||||
notification.Activated -= notification_Activated;
|
||||
}
|
||||
|
||||
private void notification_Move(object sender, EventArgs e) {
|
||||
if (radioLocCustom.Checked && notification.Location != ControlExtensions.InvisibleLocation) {
|
||||
Config.CustomNotificationPosition = notification.Location;
|
||||
@@ -188,6 +196,7 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Timer
|
||||
|
||||
private void checkNotificationTimer_CheckedChanged(object sender, EventArgs e) {
|
||||
@@ -227,6 +236,7 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Location
|
||||
|
||||
private void radioLoc_CheckedChanged(object sender, EventArgs e) {
|
||||
@@ -280,6 +290,7 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Size
|
||||
|
||||
private void radioSize_CheckedChanged(object sender, EventArgs e) {
|
||||
|
@@ -72,6 +72,14 @@ namespace TweetDuck.Dialogs.Settings{
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == DialogResult.OK) {
|
||||
try {
|
||||
if (new FileInfo(dialog.FileName).Length > (1024 * 1024) && !FormMessage.Warning("Sound Notification", "The sound file is larger than 1 MB, this will cause increased memory usage. Use this file anyway?", FormMessage.Yes, FormMessage.No)) {
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
tbCustomSound.Text = dialog.FileName;
|
||||
}
|
||||
}
|
||||
|
@@ -1,136 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using TweetLib.Core.Serialization;
|
||||
using TweetLib.Core.Serialization.Converters;
|
||||
|
||||
namespace TweetDuck.Management.Analytics{
|
||||
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local")]
|
||||
sealed class AnalyticsFile{
|
||||
private static readonly FileSerializer<AnalyticsFile> Serializer = new FileSerializer<AnalyticsFile>();
|
||||
|
||||
static AnalyticsFile(){
|
||||
Serializer.RegisterTypeConverter(typeof(DateTime), new SingleTypeConverter<DateTime>{
|
||||
ConvertToString = value => value.ToBinary().ToString(),
|
||||
ConvertToObject = value => DateTime.FromBinary(long.Parse(value))
|
||||
});
|
||||
|
||||
Serializer.RegisterTypeConverter(typeof(Counter), new SingleTypeConverter<Counter>{
|
||||
ConvertToString = value => value.Value.ToString(),
|
||||
ConvertToObject = value => int.Parse(value)
|
||||
});
|
||||
}
|
||||
|
||||
public static readonly AnalyticsFile Dummy = new AnalyticsFile();
|
||||
|
||||
// STATE PROPERTIES
|
||||
|
||||
public DateTime LastDataCollection { get; set; } = DateTime.MinValue;
|
||||
public string LastCollectionVersion { get; set; } = null;
|
||||
public string LastCollectionMessage { get; set; } = null;
|
||||
|
||||
// USAGE DATA
|
||||
|
||||
public Counter OpenOptions { get; private set; } = 0;
|
||||
public Counter OpenPlugins { get; private set; } = 0;
|
||||
public Counter OpenAbout { get; private set; } = 0;
|
||||
public Counter OpenGuide { get; private set; } = 0;
|
||||
|
||||
public Counter DesktopNotifications { get; private set; } = 0;
|
||||
public Counter SoundNotifications { get; private set; } = 0;
|
||||
public Counter NotificationMutes { get; private set; } = 0;
|
||||
|
||||
public Counter BrowserContextMenus { get; private set; } = 0;
|
||||
public Counter BrowserExtraMouseButtons { get; private set; } = 0;
|
||||
public Counter NotificationContextMenus { get; private set; } = 0;
|
||||
public Counter NotificationExtraMouseButtons { get; private set; } = 0;
|
||||
public Counter NotificationKeyboardShortcuts { get; private set; } = 0;
|
||||
|
||||
public Counter BrowserReloads { get; private set; } = 0;
|
||||
public Counter CopiedUsernames { get; private set; } = 0;
|
||||
public Counter ViewedImages { get; private set; } = 0;
|
||||
public Counter DownloadedImages { get; private set; } = 0;
|
||||
public Counter DownloadedVideos { get; private set; } = 0;
|
||||
public Counter UsedROT13 { get; private set; } = 0;
|
||||
|
||||
public Counter TweetScreenshots { get; private set; } = 0;
|
||||
public Counter TweetDetails { get; private set; } = 0;
|
||||
public Counter VideoPlays { get; private set; } = 0;
|
||||
|
||||
// END OF DATA
|
||||
|
||||
public event EventHandler PropertyChanged;
|
||||
|
||||
private readonly string file;
|
||||
|
||||
private AnalyticsFile(string file){
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
private AnalyticsFile(){
|
||||
this.file = null;
|
||||
}
|
||||
|
||||
private void SetupProperties(){
|
||||
foreach(Counter counter in GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.PropertyType == typeof(Counter)).Select(prop => (Counter)prop.GetValue(this))){
|
||||
counter.SetOwner(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPropertyChanged(){
|
||||
PropertyChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void Save(){
|
||||
if (file == null){
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
Serializer.Write(file, this);
|
||||
}catch(Exception e){
|
||||
Program.Reporter.HandleException("Analytics File Error", "Could not save the analytics file.", true, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static AnalyticsFile Load(string file){
|
||||
AnalyticsFile config = new AnalyticsFile(file);
|
||||
|
||||
try{
|
||||
Serializer.ReadIfExists(file, config);
|
||||
}catch(Exception e){
|
||||
Program.Reporter.HandleException("Analytics File Error", "Could not open the analytics file.", true, e);
|
||||
}
|
||||
|
||||
config.SetupProperties();
|
||||
return config;
|
||||
}
|
||||
|
||||
public interface IProvider{
|
||||
AnalyticsFile AnalyticsFile { get; }
|
||||
}
|
||||
|
||||
public sealed class Counter{
|
||||
public int Value { get; private set; }
|
||||
|
||||
private AnalyticsFile owner;
|
||||
|
||||
public Counter(int value){
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
public void SetOwner(AnalyticsFile owner){
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public void Trigger(){
|
||||
++Value;
|
||||
owner?.OnPropertyChanged();
|
||||
}
|
||||
|
||||
public static implicit operator int(Counter counter) => counter.Value;
|
||||
public static implicit operator Counter(int value) => new Counter(value);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,160 +0,0 @@
|
||||
// Uncomment to debug reports locally
|
||||
// #define ANALYTICS_LOCALHOST
|
||||
// #define ANALYTICS_INSTANT
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using TweetDuck.Browser;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
using TweetLib.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Management.Analytics{
|
||||
sealed class AnalyticsManager : IDisposable{
|
||||
private static readonly TimeSpan CollectionInterval = TimeSpan.FromDays(14);
|
||||
|
||||
private static readonly Uri CollectionUrl = new Uri(
|
||||
#if (DEBUG && ANALYTICS_LOCALHOST)
|
||||
"http://localhost/newhome/tweetduck/~breadcrumb/request.php?type=report"
|
||||
#else
|
||||
"https://tweetduck.chylex.com/breadcrumb/report"
|
||||
#endif
|
||||
);
|
||||
|
||||
public AnalyticsFile File { get; }
|
||||
|
||||
private readonly FormBrowser browser;
|
||||
private readonly PluginManager plugins;
|
||||
private readonly Timer currentTimer, saveTimer;
|
||||
|
||||
public AnalyticsManager(FormBrowser browser, PluginManager plugins, string file){
|
||||
this.browser = browser;
|
||||
this.plugins = plugins;
|
||||
|
||||
this.File = AnalyticsFile.Load(file);
|
||||
this.File.PropertyChanged += File_PropertyChanged;
|
||||
|
||||
this.currentTimer = new Timer{ SynchronizingObject = browser };
|
||||
this.currentTimer.Elapsed += currentTimer_Elapsed;
|
||||
|
||||
this.saveTimer = new Timer{ SynchronizingObject = browser, Interval = 60000 };
|
||||
this.saveTimer.Elapsed += saveTimer_Elapsed;
|
||||
|
||||
if (this.File.LastCollectionVersion != Program.VersionTag){
|
||||
ScheduleReportIn(TimeSpan.FromHours(8), string.Empty);
|
||||
}
|
||||
else{
|
||||
RestartTimer();
|
||||
}
|
||||
|
||||
#if (DEBUG && ANALYTICS_INSTANT)
|
||||
SendReport();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Dispose(){
|
||||
File.PropertyChanged -= File_PropertyChanged;
|
||||
|
||||
if (saveTimer.Enabled){
|
||||
File.Save();
|
||||
}
|
||||
|
||||
currentTimer.Dispose();
|
||||
saveTimer.Dispose();
|
||||
}
|
||||
|
||||
private void File_PropertyChanged(object sender, EventArgs e){
|
||||
saveTimer.Enabled = true;
|
||||
}
|
||||
|
||||
private void saveTimer_Elapsed(object sender, ElapsedEventArgs e){
|
||||
saveTimer.Stop();
|
||||
File.Save();
|
||||
}
|
||||
|
||||
private void ScheduleReportIn(TimeSpan delay, string message = null){
|
||||
SetLastDataCollectionTime(DateTime.Now.Subtract(CollectionInterval).Add(delay), message);
|
||||
}
|
||||
|
||||
private void SetLastDataCollectionTime(DateTime dt, string message = null){
|
||||
File.LastDataCollection = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, dt.Kind);
|
||||
File.LastCollectionVersion = Program.VersionTag;
|
||||
File.LastCollectionMessage = message ?? dt.ToString("g", Lib.Culture);
|
||||
|
||||
File.Save();
|
||||
RestartTimer();
|
||||
}
|
||||
|
||||
private void RestartTimer(){
|
||||
TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection);
|
||||
int minutesTillNext = (int)(CollectionInterval.TotalMinutes - Math.Floor(diff.TotalMinutes));
|
||||
|
||||
currentTimer.Interval = Math.Max(minutesTillNext, 2) * 60000;
|
||||
currentTimer.Start();
|
||||
}
|
||||
|
||||
private void currentTimer_Elapsed(object sender, ElapsedEventArgs e){
|
||||
currentTimer.Stop();
|
||||
|
||||
TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection);
|
||||
|
||||
if (Math.Floor(diff.TotalMinutes) >= CollectionInterval.TotalMinutes){
|
||||
SendReport();
|
||||
}
|
||||
else{
|
||||
RestartTimer();
|
||||
}
|
||||
}
|
||||
|
||||
private void SendReport(){
|
||||
AnalyticsReportGenerator.ExternalInfo info = AnalyticsReportGenerator.ExternalInfo.From(browser);
|
||||
|
||||
Task.Factory.StartNew(() => {
|
||||
AnalyticsReport report = AnalyticsReportGenerator.Create(File, info, plugins);
|
||||
|
||||
#if (DEBUG && !ANALYTICS_INSTANT)
|
||||
System.Diagnostics.Debugger.Break();
|
||||
#endif
|
||||
|
||||
WebUtils.NewClient(BrowserUtils.UserAgentVanilla).UploadValues(CollectionUrl, "POST", report.ToNameValueCollection());
|
||||
}).ContinueWith(task => browser.InvokeAsyncSafe(() => {
|
||||
if (task.Status == TaskStatus.RanToCompletion){
|
||||
SetLastDataCollectionTime(DateTime.Now);
|
||||
}
|
||||
else if (task.Exception != null){
|
||||
string message = null;
|
||||
|
||||
if (task.Exception.InnerException is WebException e){
|
||||
switch(e.Status){
|
||||
case WebExceptionStatus.ConnectFailure:
|
||||
message = "Connection Error";
|
||||
break;
|
||||
|
||||
case WebExceptionStatus.NameResolutionFailure:
|
||||
message = "DNS Error";
|
||||
break;
|
||||
|
||||
case WebExceptionStatus.ProtocolError:
|
||||
message = "HTTP Error " + (e.Response is HttpWebResponse response ? $"{(int)response.StatusCode} ({response.StatusDescription})" : "(unknown code)");
|
||||
break;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
System.IO.Stream responseStream = e.Response.GetResponseStream();
|
||||
|
||||
if (responseStream != null){
|
||||
System.Diagnostics.Debug.WriteLine(new System.IO.StreamReader(responseStream).ReadToEnd());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ScheduleReportIn(TimeSpan.FromHours(4), message ?? "Error: " + (task.Exception.InnerException?.Message ?? task.Exception.Message));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,57 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text;
|
||||
|
||||
namespace TweetDuck.Management.Analytics{
|
||||
sealed class AnalyticsReport : IEnumerable{
|
||||
private OrderedDictionary data = new OrderedDictionary(32);
|
||||
private int separators;
|
||||
|
||||
public void Add(int ignored){ // adding separators to pretty print
|
||||
data.Add((++separators).ToString(), null);
|
||||
}
|
||||
|
||||
public void Add(string key, string value){
|
||||
data.Add(key, value);
|
||||
}
|
||||
|
||||
public AnalyticsReport FinalizeReport(){
|
||||
if (!data.IsReadOnly){
|
||||
data = data.AsReadOnly();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator(){
|
||||
return data.GetEnumerator();
|
||||
}
|
||||
|
||||
public NameValueCollection ToNameValueCollection(){
|
||||
NameValueCollection collection = new NameValueCollection();
|
||||
|
||||
foreach(DictionaryEntry entry in data){
|
||||
if (entry.Value != null){
|
||||
collection.Add(((string)entry.Key).ToLower().Replace(' ', '_'), (string)entry.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
public override string ToString(){
|
||||
StringBuilder build = new StringBuilder(625);
|
||||
|
||||
foreach(DictionaryEntry entry in data){
|
||||
if (entry.Value == null){
|
||||
build.AppendLine();
|
||||
}
|
||||
else{
|
||||
build.AppendLine(entry.Key + ": " + entry.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return build.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,322 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Win32;
|
||||
using TweetDuck.Browser;
|
||||
using TweetDuck.Configuration;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core;
|
||||
using TweetLib.Core.Features.Notifications;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
using TweetLib.Core.Features.Plugins.Enums;
|
||||
using TweetLib.Core.Utils;
|
||||
|
||||
namespace TweetDuck.Management.Analytics{
|
||||
static class AnalyticsReportGenerator{
|
||||
public static AnalyticsReport Create(AnalyticsFile file, ExternalInfo info, PluginManager plugins){
|
||||
Dictionary<string, string> editLayoutDesign = EditLayoutDesignPluginData;
|
||||
|
||||
return new AnalyticsReport{
|
||||
{ "App Version" , Program.VersionTag },
|
||||
{ "App Type" , Program.IsPortable ? "portable" : "installed" },
|
||||
{ "App Dev Tools" , Bool(BrowserUtils.HasDevTools) },
|
||||
0,
|
||||
{ "System Name" , SystemName },
|
||||
{ "System Edition" , SystemEdition },
|
||||
{ "System Environment" , Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit" },
|
||||
{ "System Build" , SystemBuild },
|
||||
{ "System Locale" , Lib.Culture.Name.ToLower() },
|
||||
0,
|
||||
{ "RAM" , Exact(RamSize) },
|
||||
{ "GPU" , GpuVendor },
|
||||
0,
|
||||
{ "Screen Count" , Exact(Screen.AllScreens.Length) },
|
||||
{ "Screen Resolution" , info.Resolution ?? "(unknown)" },
|
||||
{ "Screen DPI" , info.DPI != null ? Exact(info.DPI.Value) : "(unknown)" },
|
||||
0,
|
||||
{ "Hardware Acceleration" , Bool(SysConfig.HardwareAcceleration) },
|
||||
{ "Clear Cache Automatically" , Bool(SysConfig.ClearCacheAutomatically) },
|
||||
{ "Clear Cache Threshold" , Exact(SysConfig.ClearCacheThreshold) },
|
||||
0,
|
||||
{ "Expand Links" , Bool(UserConfig.ExpandLinksOnHover) },
|
||||
{ "Search In First Column" , Bool(UserConfig.OpenSearchInFirstColumn) },
|
||||
{ "Keep Like Follow Dialogs Open" , Bool(UserConfig.KeepLikeFollowDialogsOpen) },
|
||||
{ "Best Image Quality" , Bool(UserConfig.BestImageQuality) },
|
||||
{ "Animated Images" , Bool(UserConfig.EnableAnimatedImages) },
|
||||
0,
|
||||
{ "Smooth Scrolling" , Bool(UserConfig.EnableSmoothScrolling) },
|
||||
{ "Custom Browser" , CustomBrowser },
|
||||
{ "Zoom" , Exact(UserConfig.ZoomLevel) },
|
||||
0,
|
||||
{ "Spell Check" , Bool(UserConfig.EnableSpellCheck) },
|
||||
{ "Spell Check Language" , UserConfig.SpellCheckLanguage.ToLower() },
|
||||
{ "Translation Target Language" , UserConfig.TranslationTarget },
|
||||
0,
|
||||
{ "Updates" , Bool(UserConfig.EnableUpdateCheck) },
|
||||
{ "Update Dismissed" , Bool(!string.IsNullOrEmpty(UserConfig.DismissedUpdate)) },
|
||||
0,
|
||||
{ "Tray" , TrayMode },
|
||||
{ "Tray Highlight" , Bool(UserConfig.EnableTrayHighlight) },
|
||||
0,
|
||||
{ "Notification Position" , NotificationPosition },
|
||||
{ "Notification Size" , NotificationSize },
|
||||
{ "Notification Timer" , NotificationTimer },
|
||||
{ "Notification Timer Speed" , RoundUp(UserConfig.NotificationDurationValue, 5) },
|
||||
{ "Notification Scroll Speed" , Exact(UserConfig.NotificationScrollSpeed) },
|
||||
{ "Notification Column Title" , Bool(UserConfig.DisplayNotificationColumn) },
|
||||
{ "Notification Media Previews" , Bool(UserConfig.NotificationMediaPreviews) },
|
||||
{ "Notification Link Skip" , Bool(UserConfig.NotificationSkipOnLinkClick) },
|
||||
{ "Notification Non-Intrusive" , Bool(UserConfig.NotificationNonIntrusiveMode) },
|
||||
{ "Notification Idle Pause" , Exact(UserConfig.NotificationIdlePauseSeconds) },
|
||||
{ "Custom Sound Notification" , string.IsNullOrEmpty(UserConfig.NotificationSoundPath) ? "off" : Path.GetExtension(UserConfig.NotificationSoundPath) },
|
||||
{ "Custom Sound Notification Volume" , RoundUp(UserConfig.NotificationSoundVolume, 5) },
|
||||
0,
|
||||
{ "Program Arguments" , List(ProgramArguments) },
|
||||
{ "Custom CEF Arguments" , RoundUp((UserConfig.CustomCefArgs ?? string.Empty).Length, 10) },
|
||||
{ "Custom Browser CSS" , RoundUp((UserConfig.CustomBrowserCSS ?? string.Empty).Length, 50) },
|
||||
{ "Custom Notification CSS" , RoundUp((UserConfig.CustomNotificationCSS ?? string.Empty).Length, 50) },
|
||||
0,
|
||||
{ "Plugins All" , List(plugins.Plugins.Select(Plugin)) },
|
||||
{ "Plugins Enabled" , List(plugins.Plugins.Where(plugins.Config.IsEnabled).Select(Plugin)) },
|
||||
0,
|
||||
{ "Theme" , Dict(editLayoutDesign, "_theme", "light/def") },
|
||||
{ "Column Width" , Dict(editLayoutDesign, "columnWidth", "310px/def") },
|
||||
{ "Font Size" , Dict(editLayoutDesign, "fontSize", "12px/def") },
|
||||
{ "Large Quote Font" , Dict(editLayoutDesign, "increaseQuoteTextSize", "false/def") },
|
||||
{ "Small Compose Font" , Dict(editLayoutDesign, "smallComposeTextSize", "false/def") },
|
||||
{ "Avatar Radius" , Dict(editLayoutDesign, "avatarRadius", "2/def") },
|
||||
{ "Hide Tweet Actions" , Dict(editLayoutDesign, "hideTweetActions", "true/def") },
|
||||
{ "Move Tweet Actions" , Dict(editLayoutDesign, "moveTweetActionsToRight", "true/def") },
|
||||
{ "Theme Color Tweaks" , Dict(editLayoutDesign, "themeColorTweaks", "true/def") },
|
||||
{ "Revert Icons" , Dict(editLayoutDesign, "revertIcons", "true/def") },
|
||||
{ "Optimize Animations" , Dict(editLayoutDesign, "optimizeAnimations", "true/def") },
|
||||
{ "Reply Account Mode" , ReplyAccountConfigFromPlugin },
|
||||
{ "Template Count" , Exact(TemplateCountFromPlugin) },
|
||||
0,
|
||||
{ "Opened Options" , LogRound(file.OpenOptions, 4) },
|
||||
{ "Opened Plugins" , LogRound(file.OpenPlugins, 4) },
|
||||
{ "Opened About" , LogRound(file.OpenAbout, 4) },
|
||||
{ "Opened Guide" , LogRound(file.OpenGuide, 4) },
|
||||
{ "Desktop Notifications" , LogRound(file.DesktopNotifications, 5) },
|
||||
{ "Sound Notifications" , LogRound(file.SoundNotifications, 5) },
|
||||
{ "Notification Mutes" , LogRound(file.NotificationMutes, 2) },
|
||||
{ "Browser Context Menus" , LogRound(file.BrowserContextMenus, 2) },
|
||||
{ "Browser Extra Mouse Buttons" , LogRound(file.BrowserExtraMouseButtons, 2) },
|
||||
{ "Notification Context Menus" , LogRound(file.NotificationContextMenus, 2) },
|
||||
{ "Notification Extra Mouse Buttons" , LogRound(file.NotificationExtraMouseButtons, 2) },
|
||||
{ "Notification Keyboard Shortcuts" , LogRound(file.NotificationKeyboardShortcuts, 2) },
|
||||
{ "Browser Reloads" , LogRound(file.BrowserReloads, 2) },
|
||||
{ "Copied Usernames" , LogRound(file.CopiedUsernames, 2) },
|
||||
{ "Viewed Images" , LogRound(file.ViewedImages, 2) },
|
||||
{ "Downloaded Images" , LogRound(file.DownloadedImages, 2) },
|
||||
{ "Downloaded Videos" , LogRound(file.DownloadedVideos, 2) },
|
||||
{ "Used ROT13" , LogRound(file.UsedROT13, 2) },
|
||||
{ "Tweet Screenshots" , LogRound(file.TweetScreenshots, 2) },
|
||||
{ "Tweet Details" , LogRound(file.TweetDetails, 2) },
|
||||
{ "Video Plays" , LogRound(file.VideoPlays, 4) }
|
||||
}.FinalizeReport();
|
||||
}
|
||||
|
||||
private static UserConfig UserConfig => Program.Config.User;
|
||||
private static SystemConfig SysConfig => Program.Config.System;
|
||||
|
||||
private static string Bool(bool value) => value ? "on" : "off";
|
||||
private static string Exact(int value) => value.ToString();
|
||||
private static string RoundUp(int value, int multiple) => (multiple * (int)Math.Ceiling((double)value / multiple)).ToString();
|
||||
private static string LogRound(int value, int logBase) => (value <= 0 ? 0 : (int)Math.Pow(logBase, Math.Floor(Math.Log(value, logBase)))).ToString();
|
||||
private static string Plugin(Plugin plugin) => plugin.Group.GetIdentifierPrefixShort() + plugin.Identifier.Substring(plugin.Group.GetIdentifierPrefix().Length);
|
||||
private static string Dict(Dictionary<string, string> dict, string key, string def = "(unknown)") => dict.TryGetValue(key, out string value) ? value : def;
|
||||
private static string List(IEnumerable<string> list) => string.Join("|", list.DefaultIfEmpty("(none)"));
|
||||
|
||||
private static string SystemName { get; }
|
||||
private static string SystemEdition { get; }
|
||||
private static string SystemBuild { get; }
|
||||
private static int RamSize { get; }
|
||||
private static string GpuVendor { get; }
|
||||
private static string[] ProgramArguments { get; }
|
||||
|
||||
static AnalyticsReportGenerator(){
|
||||
string osName, osEdition, osBuild;
|
||||
|
||||
try{
|
||||
using RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", false);
|
||||
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
osName = key.GetValue("ProductName") as string;
|
||||
osBuild = key.GetValue("CurrentBuild") as string;
|
||||
osEdition = null;
|
||||
|
||||
if (osName != null){
|
||||
Match match = Regex.Match(osName, @"^(.*?\d+(?:\.\d+)?) (.*)$");
|
||||
|
||||
if (match.Success){
|
||||
osName = match.Groups[1].Value;
|
||||
osEdition = match.Groups[2].Value;
|
||||
}
|
||||
}
|
||||
}catch{
|
||||
osName = osEdition = osBuild = null;
|
||||
}
|
||||
|
||||
SystemName = osName ?? "Windows (unknown)";
|
||||
SystemEdition = osEdition ?? "(unknown)";
|
||||
SystemBuild = osBuild ?? "(unknown)";
|
||||
|
||||
try{
|
||||
using ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Capacity FROM Win32_PhysicalMemory");
|
||||
|
||||
foreach(ManagementBaseObject obj in searcher.Get()){
|
||||
RamSize += (int)((ulong)obj["Capacity"] / (1024L * 1024L));
|
||||
}
|
||||
}catch{
|
||||
RamSize = 0;
|
||||
}
|
||||
|
||||
string gpu = null;
|
||||
|
||||
try{
|
||||
using ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_VideoController");
|
||||
|
||||
foreach(ManagementBaseObject obj in searcher.Get()){
|
||||
string vendor = obj["Caption"] as string;
|
||||
|
||||
if (!string.IsNullOrEmpty(vendor)){
|
||||
gpu = vendor;
|
||||
}
|
||||
}
|
||||
}catch{
|
||||
// rip
|
||||
}
|
||||
|
||||
GpuVendor = gpu ?? "(unknown)";
|
||||
|
||||
Dictionary<string, string> args = new Dictionary<string, string>();
|
||||
Arguments.GetCurrentClean().ToDictionary(args);
|
||||
ProgramArguments = args.Keys.Select(key => key.TrimStart('-')).ToArray();
|
||||
}
|
||||
|
||||
private static string CustomBrowser{
|
||||
get{
|
||||
return Path.GetFileName(UserConfig.BrowserPath) ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static string TrayMode{
|
||||
get => UserConfig.TrayBehavior switch{
|
||||
TrayIcon.Behavior.DisplayOnly => "icon",
|
||||
TrayIcon.Behavior.MinimizeToTray => "minimize",
|
||||
TrayIcon.Behavior.CloseToTray => "close",
|
||||
TrayIcon.Behavior.Combined => "combined",
|
||||
_ => "off"
|
||||
};
|
||||
}
|
||||
|
||||
private static string NotificationPosition{
|
||||
get => UserConfig.NotificationPosition switch{
|
||||
DesktopNotification.Position.TopLeft => "top left",
|
||||
DesktopNotification.Position.TopRight => "top right",
|
||||
DesktopNotification.Position.BottomLeft => "bottom left",
|
||||
DesktopNotification.Position.BottomRight => "bottom right",
|
||||
_ => "custom"
|
||||
};
|
||||
}
|
||||
|
||||
private static string NotificationSize{
|
||||
get => UserConfig.NotificationSize switch{
|
||||
DesktopNotification.Size.Auto => "auto",
|
||||
_ => RoundUp(UserConfig.CustomNotificationSize.Width, 20) + "x" + RoundUp(UserConfig.CustomNotificationSize.Height, 20)
|
||||
};
|
||||
}
|
||||
|
||||
private static string NotificationTimer{
|
||||
get{
|
||||
if (!UserConfig.DisplayNotificationTimer){
|
||||
return "off";
|
||||
}
|
||||
else{
|
||||
return UserConfig.NotificationTimerCountDown ? "count down" : "count up";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> EditLayoutDesignPluginData{
|
||||
get{
|
||||
Dictionary<string, string> dict = new Dictionary<string, string>();
|
||||
|
||||
try{
|
||||
string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "edit-design", "config.json"));
|
||||
|
||||
foreach(Match match in Regex.Matches(data, "\"(\\w+?)\":(.*?)[,}]")){
|
||||
dict[match.Groups[1].Value] = match.Groups[2].Value.Trim('"');
|
||||
}
|
||||
}catch{
|
||||
// rip
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
|
||||
private static int TemplateCountFromPlugin{
|
||||
get{
|
||||
try{
|
||||
string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "templates", "config.json"));
|
||||
return Math.Min(StringUtils.CountOccurrences(data, "{\"name\":"), StringUtils.CountOccurrences(data, ",\"contents\":"));
|
||||
}catch{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReplyAccountConfigFromPlugin{
|
||||
get{
|
||||
try{
|
||||
string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "reply-account", "configuration.js")).Replace(" ", "");
|
||||
|
||||
Match matchType = Regex.Match(data, "defaultAccount:\"([#@])(.*?)\"(?:,|$)");
|
||||
Match matchAdvanced = Regex.Match(data, "useAdvancedSelector:(.*?)(?:,|$)", RegexOptions.Multiline);
|
||||
|
||||
if (!matchType.Success){
|
||||
return data.Contains("defaultAccount:\"\"") ? "(legacy)" : "(unknown)";
|
||||
}
|
||||
|
||||
string accType = matchType.Groups[1].Value == "#" ? matchType.Groups[2].Value : "account";
|
||||
return matchAdvanced.Success && !matchAdvanced.Value.Contains("false") ? "advanced/" + accType : accType;
|
||||
}catch{
|
||||
return "(unknown)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ExternalInfo{
|
||||
public static ExternalInfo From(Form form){
|
||||
if (form == null){
|
||||
return new ExternalInfo();
|
||||
}
|
||||
else{
|
||||
Screen screen = Screen.FromControl(form); // works on multi-monitor setups even in tray
|
||||
int dpi;
|
||||
|
||||
using(Graphics graphics = form.CreateGraphics()){
|
||||
dpi = (int)graphics.DpiY;
|
||||
}
|
||||
|
||||
return new ExternalInfo{
|
||||
Resolution = screen.Bounds.Width + "x" + screen.Bounds.Height,
|
||||
DPI = dpi
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public string Resolution { get; private set; }
|
||||
public int? DPI { get; private set; }
|
||||
|
||||
private ExternalInfo(){}
|
||||
}
|
||||
}
|
||||
}
|
@@ -8,8 +8,8 @@ namespace TweetDuck.Management{
|
||||
static class BrowserCache {
|
||||
public static string CacheFolder => Path.Combine(Program.StoragePath, "Cache");
|
||||
|
||||
private static bool ClearOnExit;
|
||||
private static Timer AutoClearTimer;
|
||||
private static bool clearOnExit;
|
||||
private static Timer autoClearTimer;
|
||||
|
||||
private static long CalculateCacheSize() {
|
||||
return new DirectoryInfo(CacheFolder).EnumerateFiles().Select(file => {
|
||||
@@ -28,15 +28,15 @@ namespace TweetDuck.Management{
|
||||
}
|
||||
|
||||
public static void RefreshTimer() {
|
||||
bool shouldRun = Program.Config.System.ClearCacheAutomatically && !ClearOnExit;
|
||||
bool shouldRun = Program.Config.System.ClearCacheAutomatically && !clearOnExit;
|
||||
|
||||
if (!shouldRun && AutoClearTimer != null){
|
||||
AutoClearTimer.Dispose();
|
||||
AutoClearTimer = null;
|
||||
if (!shouldRun && autoClearTimer != null) {
|
||||
autoClearTimer.Dispose();
|
||||
autoClearTimer = null;
|
||||
}
|
||||
else if (shouldRun && AutoClearTimer == null){
|
||||
AutoClearTimer = new Timer(state => {
|
||||
if (AutoClearTimer != null){
|
||||
else if (shouldRun && autoClearTimer == null) {
|
||||
autoClearTimer = new Timer(state => {
|
||||
if (autoClearTimer != null) {
|
||||
try {
|
||||
if (CalculateCacheSize() >= Program.Config.System.ClearCacheThreshold * 1024L * 1024L) {
|
||||
SetClearOnExit();
|
||||
@@ -50,7 +50,7 @@ namespace TweetDuck.Management{
|
||||
}
|
||||
|
||||
public static void SetClearOnExit() {
|
||||
ClearOnExit = true;
|
||||
clearOnExit = true;
|
||||
RefreshTimer();
|
||||
}
|
||||
|
||||
@@ -63,12 +63,12 @@ namespace TweetDuck.Management{
|
||||
}
|
||||
|
||||
public static void Exit() {
|
||||
if (AutoClearTimer != null){
|
||||
AutoClearTimer.Dispose();
|
||||
AutoClearTimer = null;
|
||||
if (autoClearTimer != null) {
|
||||
autoClearTimer.Dispose();
|
||||
autoClearTimer = null;
|
||||
}
|
||||
|
||||
if (ClearOnExit){
|
||||
if (clearOnExit) {
|
||||
TryClearNow();
|
||||
}
|
||||
}
|
||||
|
129
Management/LockManager.cs
Normal file
129
Management/LockManager.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using TweetDuck.Dialogs;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Systems.Startup;
|
||||
|
||||
namespace TweetDuck.Management {
|
||||
sealed class LockManager {
|
||||
private const int WaitRetryDelay = 250;
|
||||
private const int RestoreFailTimeout = 2000;
|
||||
private const int CloseNaturallyTimeout = 10000;
|
||||
private const int CloseKillTimeout = 5000;
|
||||
|
||||
private readonly LockFile lockFile;
|
||||
|
||||
public LockManager(string path) {
|
||||
this.lockFile = new LockFile(path);
|
||||
}
|
||||
|
||||
public bool Lock(bool wasRestarted) {
|
||||
return wasRestarted ? LaunchAfterRestart() : LaunchNormally();
|
||||
}
|
||||
|
||||
public bool Unlock() {
|
||||
return lockFile.Unlock();
|
||||
}
|
||||
|
||||
// Locking
|
||||
|
||||
private bool LaunchNormally() {
|
||||
LockResult lockResult = lockFile.Lock();
|
||||
|
||||
if (lockResult is LockResult.HasProcess info) {
|
||||
if (!RestoreProcess(info.Process) && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)) {
|
||||
if (!CloseProcess(info.Process)) {
|
||||
FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
|
||||
return false;
|
||||
}
|
||||
|
||||
info.Dispose();
|
||||
lockResult = lockFile.Lock();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (lockResult is LockResult.Fail fail) {
|
||||
ShowGenericException(fail);
|
||||
return false;
|
||||
}
|
||||
else if (lockResult != LockResult.Success) {
|
||||
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool LaunchAfterRestart() {
|
||||
LockResult lockResult = lockFile.LockWait(10000, WaitRetryDelay);
|
||||
|
||||
while (lockResult != LockResult.Success) {
|
||||
if (lockResult is LockResult.Fail fail) {
|
||||
ShowGenericException(fail);
|
||||
return false;
|
||||
}
|
||||
else if (!FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lockResult = lockFile.LockWait(5000, WaitRetryDelay);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
private static void ShowGenericException(LockResult.Fail fail) {
|
||||
Program.Reporter.HandleException("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", false, fail.Exception);
|
||||
}
|
||||
|
||||
private static bool RestoreProcess(Process process) {
|
||||
if (process.MainWindowHandle == IntPtr.Zero) { // restore if the original process is in tray
|
||||
NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint) process.Id, 0);
|
||||
|
||||
if (WindowsUtils.TrySleepUntil(() => CheckProcessExited(process) || (process.MainWindowHandle != IntPtr.Zero && process.Responding), RestoreFailTimeout, WaitRetryDelay)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool CloseProcess(Process process) {
|
||||
try {
|
||||
if (process.CloseMainWindow()) {
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseNaturallyTimeout, WaitRetryDelay);
|
||||
}
|
||||
|
||||
if (!process.HasExited) {
|
||||
process.Kill();
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseKillTimeout, WaitRetryDelay);
|
||||
}
|
||||
|
||||
if (process.HasExited) {
|
||||
process.Dispose();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception ex) when (ex is InvalidOperationException || ex is Win32Exception) {
|
||||
bool hasExited = CheckProcessExited(process);
|
||||
process.Dispose();
|
||||
return hasExited;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CheckProcessExited(Process process) {
|
||||
process.Refresh();
|
||||
return process.HasExited;
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,7 +10,12 @@ using TweetLib.Core.Features.Plugins.Enums;
|
||||
namespace TweetDuck.Management {
|
||||
sealed class ProfileManager {
|
||||
private static readonly string CookiesPath = Path.Combine(Program.StoragePath, "Cookies");
|
||||
private static readonly string LocalPrefsPath = Path.Combine(Program.StoragePath, "LocalPrefs.json");
|
||||
|
||||
private static readonly string TempCookiesPath = Path.Combine(Program.StoragePath, "CookiesTmp");
|
||||
private static readonly string TempLocalPrefsPath = Path.Combine(Program.StoragePath, "LocalPrefsTmp.json");
|
||||
|
||||
private static readonly int SessionFileCount = 2;
|
||||
|
||||
[Flags]
|
||||
public enum Items {
|
||||
@@ -32,7 +37,8 @@ namespace TweetDuck.Management{
|
||||
|
||||
public bool Export(Items items) {
|
||||
try {
|
||||
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
||||
using CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None));
|
||||
|
||||
if (items.HasFlag(Items.UserConfig)) {
|
||||
stream.WriteFile("config", Program.UserConfigFilePath);
|
||||
}
|
||||
@@ -57,11 +63,10 @@ namespace TweetDuck.Management{
|
||||
|
||||
if (items.HasFlag(Items.Session)) {
|
||||
stream.WriteFile("cookies", CookiesPath);
|
||||
stream.WriteFile("localprefs", LocalPrefsPath);
|
||||
}
|
||||
|
||||
stream.Flush();
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting TweetDuck profile.", true, e);
|
||||
@@ -92,6 +97,7 @@ namespace TweetDuck.Management{
|
||||
break;
|
||||
|
||||
case "cookies":
|
||||
case "localprefs":
|
||||
items |= Items.Session;
|
||||
break;
|
||||
}
|
||||
@@ -105,7 +111,8 @@ namespace TweetDuck.Management{
|
||||
|
||||
public bool Import(Items items) {
|
||||
try {
|
||||
HashSet<string> missingPlugins = new HashSet<string>();
|
||||
var missingPlugins = new HashSet<string>();
|
||||
var sessionFiles = new HashSet<string>();
|
||||
|
||||
using (CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))) {
|
||||
CombinedFileStream.Entry entry;
|
||||
@@ -148,7 +155,16 @@ namespace TweetDuck.Management{
|
||||
|
||||
case "cookies":
|
||||
if (items.HasFlag(Items.Session)) {
|
||||
entry.WriteToFile(Path.Combine(Program.StoragePath, TempCookiesPath));
|
||||
entry.WriteToFile(TempCookiesPath);
|
||||
sessionFiles.Add(entry.KeyName);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "localprefs":
|
||||
if (items.HasFlag(Items.Session)) {
|
||||
entry.WriteToFile(TempLocalPrefsPath);
|
||||
sessionFiles.Add(entry.KeyName);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -156,6 +172,13 @@ namespace TweetDuck.Management{
|
||||
}
|
||||
}
|
||||
|
||||
if (items.HasFlag(Items.Session) && sessionFiles.Count != SessionFileCount) {
|
||||
FormMessage.Error("Profile Import Error", "Cannot import login session from an older version of TweetDuck.", FormMessage.OK);
|
||||
File.Delete(TempCookiesPath);
|
||||
File.Delete(TempLocalPrefsPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (missingPlugins.Count > 0) {
|
||||
FormMessage.Information("Profile Import", "Detected missing plugins when importing plugin data:\n" + string.Join("\n", missingPlugins), FormMessage.OK);
|
||||
}
|
||||
@@ -168,13 +191,18 @@ namespace TweetDuck.Management{
|
||||
}
|
||||
|
||||
public static void ImportCookies() {
|
||||
if (File.Exists(TempCookiesPath)){
|
||||
if (File.Exists(TempCookiesPath) && File.Exists(TempLocalPrefsPath)) {
|
||||
try {
|
||||
if (File.Exists(CookiesPath)) {
|
||||
File.Delete(CookiesPath);
|
||||
}
|
||||
|
||||
if (File.Exists(LocalPrefsPath)) {
|
||||
File.Delete(LocalPrefsPath);
|
||||
}
|
||||
|
||||
File.Move(TempCookiesPath, CookiesPath);
|
||||
File.Move(TempLocalPrefsPath, LocalPrefsPath);
|
||||
} catch (Exception e) {
|
||||
Program.Reporter.HandleException("Profile Import Error", "Could not import the cookie file to restore login session.", true, e);
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ using TweetDuck.Configuration;
|
||||
using TweetDuck.Controls;
|
||||
using TweetDuck.Dialogs;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Communication;
|
||||
using TweetLib.Communication.Pipe;
|
||||
|
||||
namespace TweetDuck.Management {
|
||||
sealed class VideoPlayer : IDisposable {
|
||||
@@ -37,14 +37,15 @@ namespace TweetDuck.Management{
|
||||
DuplexPipe.Server pipe = DuplexPipe.CreateServer();
|
||||
pipe.DataIn += pipe_DataIn;
|
||||
|
||||
Process process;
|
||||
|
||||
if ((process = Process.Start(new ProcessStartInfo{
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo {
|
||||
FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"),
|
||||
Arguments = $"{owner.Handle} {(int) Math.Floor(100F * owner.GetDPIScale())} {Config.VideoPlayerVolume} \"{videoUrl}\" \"{pipe.GenerateToken()}\"",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true
|
||||
})) != null){
|
||||
};
|
||||
|
||||
Process process;
|
||||
if ((process = Process.Start(startInfo)) != null) {
|
||||
currentInstance = new Instance(process, pipe, videoUrl, tweetUrl, username);
|
||||
|
||||
process.EnableRaisingEvents = true;
|
||||
@@ -81,7 +82,6 @@ namespace TweetDuck.Management{
|
||||
|
||||
case "download":
|
||||
if (currentInstance != null) {
|
||||
owner.AnalyticsFile.DownloadedVideos.Trigger();
|
||||
TwitterUtils.DownloadVideo(currentInstance.VideoUrl, currentInstance.Username);
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using CefSharp;
|
||||
using TweetDuck.Browser.Adapters;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Browser;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
using TweetLib.Core.Features.Plugins.Events;
|
||||
using TweetLib.Core.Features.Twitter;
|
||||
|
||||
namespace TweetDuck.Plugins {
|
||||
sealed class PluginDispatcher : IPluginDispatcher {
|
||||
@@ -12,21 +12,23 @@ namespace TweetDuck.Plugins{
|
||||
|
||||
private readonly IWebBrowser browser;
|
||||
private readonly IScriptExecutor executor;
|
||||
private readonly Func<string, bool> executeOnUrl;
|
||||
|
||||
public PluginDispatcher(IWebBrowser browser){
|
||||
public PluginDispatcher(IWebBrowser browser, Func<string, bool> executeOnUrl) {
|
||||
this.executeOnUrl = executeOnUrl;
|
||||
this.browser = browser;
|
||||
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
|
||||
this.executor = new CefScriptExecutor(browser);
|
||||
}
|
||||
|
||||
void IPluginDispatcher.AttachBridge(string name, object bridge) {
|
||||
browser.RegisterAsyncJsObject(name, bridge);
|
||||
browser.RegisterJsBridge(name, bridge);
|
||||
}
|
||||
|
||||
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e) {
|
||||
IFrame frame = e.Frame;
|
||||
|
||||
if (frame.IsMain && TwitterUrls.IsTweetDeck(frame.Url)){
|
||||
if (frame.IsMain && executeOnUrl(frame.Url)) {
|
||||
Ready?.Invoke(this, new PluginDispatchEventArgs(executor));
|
||||
}
|
||||
}
|
||||
|
23
Plugins/PluginSchemeFactory.cs
Normal file
23
Plugins/PluginSchemeFactory.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using CefSharp;
|
||||
using TweetDuck.Browser.Handling;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
|
||||
namespace TweetDuck.Plugins {
|
||||
sealed class PluginSchemeFactory : ISchemeHandlerFactory {
|
||||
public const string Name = PluginSchemeHandler<IResourceHandler>.Name;
|
||||
|
||||
private readonly PluginSchemeHandler<IResourceHandler> handler;
|
||||
|
||||
public PluginSchemeFactory(ResourceProvider resourceProvider) {
|
||||
handler = new PluginSchemeHandler<IResourceHandler>(resourceProvider);
|
||||
}
|
||||
|
||||
internal void Setup(PluginManager plugins) {
|
||||
handler.Setup(plugins);
|
||||
}
|
||||
|
||||
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request) {
|
||||
return handler.Process(request.Url);
|
||||
}
|
||||
}
|
||||
}
|
89
Program.cs
89
Program.cs
@@ -1,9 +1,9 @@
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using TweetDuck.Application;
|
||||
using TweetDuck.Browser;
|
||||
using TweetDuck.Browser.Handling;
|
||||
@@ -11,10 +11,10 @@ using TweetDuck.Browser.Handling.General;
|
||||
using TweetDuck.Configuration;
|
||||
using TweetDuck.Dialogs;
|
||||
using TweetDuck.Management;
|
||||
using TweetDuck.Plugins;
|
||||
using TweetDuck.Resources;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core;
|
||||
using TweetLib.Core.Application.Helpers;
|
||||
using TweetLib.Core.Collections;
|
||||
using TweetLib.Core.Utils;
|
||||
using Win = System.Windows.Forms;
|
||||
@@ -31,8 +31,9 @@ namespace TweetDuck{
|
||||
|
||||
public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable"));
|
||||
|
||||
public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts");
|
||||
public static readonly string ResourcesPath = Path.Combine(ProgramPath, "resources");
|
||||
public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins");
|
||||
public static readonly string GuidePath = Path.Combine(ProgramPath, "guide");
|
||||
|
||||
public static readonly string StoragePath = IsPortable ? Path.Combine(ProgramPath, "portable", "storage") : GetDataStoragePath();
|
||||
|
||||
@@ -43,7 +44,6 @@ namespace TweetDuck{
|
||||
public static string UserConfigFilePath => Path.Combine(StoragePath, "TD_UserConfig.cfg");
|
||||
public static string SystemConfigFilePath => Path.Combine(StoragePath, "TD_SystemConfig.cfg");
|
||||
public static string PluginConfigFilePath => Path.Combine(StoragePath, "TD_PluginConfig.cfg");
|
||||
public static string AnalyticsFilePath => Path.Combine(StoragePath, "TD_Analytics.cfg");
|
||||
|
||||
private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt");
|
||||
private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt");
|
||||
@@ -51,11 +51,10 @@ namespace TweetDuck{
|
||||
public static uint WindowRestoreMessage;
|
||||
|
||||
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
|
||||
private static bool HasCleanedUp;
|
||||
private static bool hasCleanedUp;
|
||||
|
||||
public static Reporter Reporter { get; }
|
||||
public static ConfigManager Config { get; }
|
||||
public static ScriptLoader Resources { get; }
|
||||
|
||||
static Program() {
|
||||
Reporter = new Reporter(ErrorLogFilePath);
|
||||
@@ -63,17 +62,9 @@ namespace TweetDuck{
|
||||
|
||||
Config = new ConfigManager();
|
||||
|
||||
#if DEBUG
|
||||
Resources = new ScriptLoaderDebug();
|
||||
#else
|
||||
Resources = new ScriptLoader();
|
||||
#endif
|
||||
|
||||
Lib.Initialize(new App.Builder {
|
||||
ErrorHandler = Reporter,
|
||||
LockHandler = new LockHandler(),
|
||||
SystemHandler = new SystemHandler(),
|
||||
ResourceHandler = Resources
|
||||
});
|
||||
}
|
||||
|
||||
@@ -94,43 +85,9 @@ namespace TweetDuck{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Arguments.HasFlag(Arguments.ArgRestart)){
|
||||
LockManager.Result lockResult = LockManager.LockWait(10000);
|
||||
|
||||
while(lockResult != LockManager.Result.Success){
|
||||
if (lockResult == LockManager.Result.Fail){
|
||||
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
|
||||
if (!LockManager.Lock(Arguments.HasFlag(Arguments.ArgRestart))) {
|
||||
return;
|
||||
}
|
||||
else if (!FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)){
|
||||
return;
|
||||
}
|
||||
|
||||
lockResult = LockManager.LockWait(5000);
|
||||
}
|
||||
}
|
||||
else{
|
||||
LockManager.Result lockResult = LockManager.Lock();
|
||||
|
||||
if (lockResult == LockManager.Result.HasProcess){
|
||||
if (!LockManager.RestoreLockingProcess() && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
|
||||
if (!LockManager.CloseLockingProcess()){
|
||||
FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
|
||||
return;
|
||||
}
|
||||
|
||||
lockResult = LockManager.Lock();
|
||||
}
|
||||
else{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (lockResult != LockManager.Result.Success){
|
||||
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Config.LoadAll();
|
||||
|
||||
@@ -143,24 +100,28 @@ namespace TweetDuck{
|
||||
|
||||
if (Arguments.HasFlag(Arguments.ArgUpdated)) {
|
||||
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
|
||||
WindowsUtils.TryDeleteFolderWhenAble(Path.Combine(StoragePath, "Service Worker"), 4000);
|
||||
BrowserCache.TryClearNow();
|
||||
}
|
||||
|
||||
try {
|
||||
RequestHandlerBase.LoadResourceRewriteRules(Arguments.GetValue(Arguments.ArgFreeze));
|
||||
ResourceRequestHandlerBase.LoadResourceRewriteRules(Arguments.GetValue(Arguments.ArgFreeze));
|
||||
} catch (Exception e) {
|
||||
FormMessage.Error("Resource Freeze", "Error parsing resource rewrite rules: " + e.Message, FormMessage.OK);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Config.User.UseSystemProxyForAllConnections) {
|
||||
WebUtils.EnableSystemProxy();
|
||||
}
|
||||
|
||||
BrowserCache.RefreshTimer();
|
||||
|
||||
CefSharpSettings.WcfEnabled = false;
|
||||
CefSharpSettings.LegacyJavascriptBindingEnabled = true;
|
||||
|
||||
CefSettings settings = new CefSettings {
|
||||
UserAgent = BrowserUtils.UserAgentChrome,
|
||||
BrowserSubprocessPath = BrandName + ".Browser.exe",
|
||||
BrowserSubprocessPath = Path.Combine(ProgramPath, BrandName + ".Browser.exe"),
|
||||
CachePath = StoragePath,
|
||||
UserDataPath = CefDataPath,
|
||||
LogFile = ConsoleLogFilePath,
|
||||
@@ -169,6 +130,13 @@ namespace TweetDuck{
|
||||
#endif
|
||||
};
|
||||
|
||||
var resourceProvider = new ResourceProvider();
|
||||
var resourceScheme = new ResourceSchemeFactory(resourceProvider);
|
||||
var pluginScheme = new PluginSchemeFactory(resourceProvider);
|
||||
|
||||
settings.SetupCustomScheme(ResourceSchemeFactory.Name, resourceScheme);
|
||||
settings.SetupCustomScheme(PluginSchemeFactory.Name, pluginScheme);
|
||||
|
||||
CommandLineArgs.ReadCefArguments(Config.User.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
||||
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
|
||||
|
||||
@@ -176,18 +144,13 @@ namespace TweetDuck{
|
||||
|
||||
Win.Application.ApplicationExit += (sender, args) => ExitCleanup();
|
||||
|
||||
FormBrowser mainForm = new FormBrowser();
|
||||
Resources.Initialize(mainForm);
|
||||
FormBrowser mainForm = new FormBrowser(resourceProvider, pluginScheme);
|
||||
Win.Application.Run(mainForm);
|
||||
|
||||
if (mainForm.UpdateInstallerPath != null){
|
||||
if (mainForm.UpdateInstaller != null) {
|
||||
ExitCleanup();
|
||||
|
||||
// ProgramPath has a trailing backslash
|
||||
string updaterArgs = "/SP- /SILENT /FORCECLOSEAPPLICATIONS /UPDATEPATH=\"" + ProgramPath + "\" /RUNARGS=\"" + Arguments.GetCurrentForInstallerCmd() + "\"" + (IsPortable ? " /PORTABLE=1" : "");
|
||||
bool runElevated = !IsPortable || !FileUtils.CheckFolderWritePermission(ProgramPath);
|
||||
|
||||
if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){
|
||||
if (mainForm.UpdateInstaller.Launch()) {
|
||||
Win.Application.Exit();
|
||||
}
|
||||
else {
|
||||
@@ -238,7 +201,7 @@ namespace TweetDuck{
|
||||
}
|
||||
|
||||
private static void ExitCleanup() {
|
||||
if (HasCleanedUp){
|
||||
if (hasCleanedUp) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -248,7 +211,7 @@ namespace TweetDuck{
|
||||
BrowserCache.Exit();
|
||||
|
||||
LockManager.Unlock();
|
||||
HasCleanedUp = true;
|
||||
hasCleanedUp = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
99
Resources/Content/.all.js
Normal file
99
Resources/Content/.all.js
Normal file
@@ -0,0 +1,99 @@
|
||||
// noinspection ES6UnusedImports,JSUnusedLocalSymbols
|
||||
|
||||
/*
|
||||
* Imports all root modules and global functions to mark them as used.
|
||||
*/
|
||||
|
||||
import introduction from "./introduction/introduction.js";
|
||||
import hide_cookie_bar from "./login/hide_cookie_bar.js";
|
||||
import setup_document_attributes from "./login/setup_document_attributes.js";
|
||||
import add_skip_button from "./notification/add_skip_button.js";
|
||||
import disable_clipboard_formatting_notification from "./notification/disable_clipboard_formatting.js";
|
||||
import expand_links_or_show_tooltip_notification from "./notification/expand_links_or_show_tooltip.js";
|
||||
import handle_links from "./notification/handle_links.js";
|
||||
import handle_show_this_thread_link from "./notification/handle_show_this_thread_link.js";
|
||||
import recalculate_tweet_sent_time from "./notification/recalculate_tweet_sent_time.js";
|
||||
import reset_scroll_position_on_load from "./notification/reset_scroll_position_on_load.js";
|
||||
import scroll_smoothly from "./notification/scroll_smoothly.js";
|
||||
import setup_body_hover_class from "./notification/setup_body_hover_class.js";
|
||||
import add_tweetduck_to_settings_menu from "./tweetdeck/add_tweetduck_to_settings_menu.js";
|
||||
import bypass_tco_links from "./tweetdeck/bypass_t.co_links.js";
|
||||
import clear_search_input from "./tweetdeck/clear_search_input.js";
|
||||
import configure_first_day_of_week from "./tweetdeck/configure_first_day_of_week.js";
|
||||
import configure_language_for_translations from "./tweetdeck/configure_language_for_translations.js";
|
||||
import disable_clipboard_formatting from "./tweetdeck/disable_clipboard_formatting.js";
|
||||
import disable_td_metrics from "./tweetdeck/disable_td_metrics.js";
|
||||
import drag_links_onto_columns from "./tweetdeck/drag_links_onto_columns.js";
|
||||
import expand_links_or_show_tooltip from "./tweetdeck/expand_links_or_show_tooltip.js";
|
||||
import fix_dm_input_box_focus from "./tweetdeck/fix_dm_input_box_focus.js";
|
||||
import fix_horizontal_scrolling_of_column_container from "./tweetdeck/fix_horizontal_scrolling_of_column_container.js";
|
||||
import fix_marking_dm_as_read_when_replying from "./tweetdeck/fix_marking_dm_as_read_when_replying.js";
|
||||
import fix_media_preview_urls from "./tweetdeck/fix_media_preview_urls.js";
|
||||
import fix_missing_bing_translator_languages from "./tweetdeck/fix_missing_bing_translator_languages.js";
|
||||
import fix_os_name from "./tweetdeck/fix_os_name.js";
|
||||
import fix_scheduled_tweets_not_appearing from "./tweetdeck/fix_scheduled_tweets_not_appearing.js";
|
||||
import fix_youtube_previews from "./tweetdeck/fix_youtube_previews.js";
|
||||
import focus_composer_after_alt_tab from "./tweetdeck/focus_composer_after_alt_tab.js";
|
||||
import focus_composer_after_image_upload from "./tweetdeck/focus_composer_after_image_upload.js";
|
||||
import focus_composer_after_switching_account from "./tweetdeck/focus_composer_after_switching_account.js";
|
||||
import handle_extra_mouse_buttons from "./tweetdeck/handle_extra_mouse_buttons.js";
|
||||
import hook_theme_settings from "./tweetdeck/hook_theme_settings.js";
|
||||
import inject_css from "./tweetdeck/inject_css.js";
|
||||
import keep_like_follow_dialogs_open from "./tweetdeck/keep_like_follow_dialogs_open.js";
|
||||
import limit_loaded_dm_count from "./tweetdeck/limit_loaded_dm_count.js";
|
||||
import make_retweets_lowercase from "./tweetdeck/make_retweets_lowercase.js";
|
||||
import middle_click_tweet_icon_actions from "./tweetdeck/middle_click_tweet_icon_actions.js";
|
||||
import move_accounts_above_hashtags_in_search from "./tweetdeck/move_accounts_above_hashtags_in_search.js";
|
||||
import offline_notification from "./tweetdeck/offline_notification.js";
|
||||
import open_search_externally from "./tweetdeck/open_search_externally.js";
|
||||
import open_search_in_first_column from "./tweetdeck/open_search_in_first_column.js";
|
||||
import paste_images_from_clipboard from "./tweetdeck/paste_images_from_clipboard.js";
|
||||
import perform_search from "./tweetdeck/perform_search.js";
|
||||
import pin_composer_icon from "./tweetdeck/pin_composer_icon.js";
|
||||
import ready_plugins from "./tweetdeck/ready_plugins.js";
|
||||
import register_composer_active_event from "./tweetdeck/register_composer_active_event.js";
|
||||
import register_global_functions from "./tweetdeck/register_global_functions.js";
|
||||
import register_global_functions_jquery from "./tweetdeck/register_global_functions_jquery.js";
|
||||
import restore_cleared_column from "./tweetdeck/restore_cleared_column.js";
|
||||
import screenshot_tweet from "./tweetdeck/screenshot_tweet.js";
|
||||
import setup_column_type_attributes from "./tweetdeck/setup_column_type_attributes.js";
|
||||
import setup_desktop_notifications from "./tweetdeck/setup_desktop_notifications.js";
|
||||
import setup_link_context_menu from "./tweetdeck/setup_link_context_menu.js";
|
||||
import setup_sound_notifications from "./tweetdeck/setup_sound_notifications.js";
|
||||
import setup_tweet_context_menu from "./tweetdeck/setup_tweet_context_menu.js";
|
||||
import setup_tweetduck_account_bamboozle from "./tweetdeck/setup_tweetduck_account_bamboozle.js";
|
||||
import setup_video_player from "./tweetdeck/setup_video_player.js";
|
||||
import skip_pre_login_page from "./tweetdeck/skip_pre_login_page.js";
|
||||
import update from "./update/update.js";
|
||||
|
||||
const globalFunctions = [
|
||||
window.PluginBase,
|
||||
window.TDGF_applyROT13,
|
||||
window.TDGF_getColumnName,
|
||||
window.TDGF_injectMustache,
|
||||
window.TDGF_onGlobalDragStart,
|
||||
window.TDGF_onMouseClickExtra,
|
||||
window.TDGF_onPropertiesUpdated,
|
||||
window.TDGF_performSearch,
|
||||
window.TDGF_playSoundNotification,
|
||||
window.TDGF_playVideo,
|
||||
window.TDGF_registerPropertyUpdateCallback,
|
||||
window.TDGF_reinjectCustomCSS,
|
||||
window.TDGF_reload,
|
||||
window.TDGF_reloadColumns,
|
||||
window.TDGF_scrollSmoothly,
|
||||
window.TDGF_setSoundNotificationData,
|
||||
window.TDGF_showTweetDetail,
|
||||
window.TDGF_triggerScreenshot,
|
||||
window.TDPF_configurePlugin,
|
||||
window.TDPF_createCustomStyle,
|
||||
window.TDPF_createStorage,
|
||||
window.TDPF_loadConfigurationFile,
|
||||
window.TDPF_playVideo,
|
||||
window.TDPF_registerPropertyUpdateCallback,
|
||||
window.TDPF_requestReload,
|
||||
window.TDPF_setPluginState,
|
||||
window.TDUF_displayNotification,
|
||||
window.TD_PLUGINS_INSTALL,
|
||||
window.jQuery,
|
||||
];
|
76
Resources/Content/api/bridge.js
Normal file
76
Resources/Content/api/bridge.js
Normal file
@@ -0,0 +1,76 @@
|
||||
if (!("$TD" in window)) {
|
||||
throw "Missing $TD in global scope";
|
||||
}
|
||||
|
||||
if (!("$TDX" in window)) {
|
||||
throw "Missing $TDX in global scope";
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef TD_Bridge
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function(type: "error"|"warning"|"info"|"", contents: string)} alert
|
||||
* @property {function(message: string)} crashDebug
|
||||
* @property {function(tooltip: string|null)} displayTooltip
|
||||
* @property {function} fixClipboard
|
||||
* @property {function} loadNextNotification
|
||||
* @property {function(fontSize: string, headLayout: string)} loadNotificationLayout
|
||||
* @property {function(namespace: string)} onModulesLoaded
|
||||
* @property {function(columnId: string, chirpId: string, columnName: string, tweetHtml: string, tweetCharacters: int, tweetUrl: string, quoteUrl: string)} onTweetPopup
|
||||
* @property {function} onTweetSound
|
||||
* @property {function(url: string)} openBrowser
|
||||
* @property {function} openContextMenu
|
||||
* @property {function(showGuide: boolean)} onIntroductionClosed
|
||||
* @property {function(videoUrl: string, tweetUrl: string, username: string, callback: function)} playVideo
|
||||
* @property {function(columnId: string, chirpId: string, tweetUrl: string, quoteUrl: string, chirpAuthors: string, chirpImages: string)} setRightClickedChirp
|
||||
* @property {function(type: string, url: string)} setRightClickedLink
|
||||
* @property {function} showTweetDetail
|
||||
* @property {function(html: string, width: number)} screenshotTweet
|
||||
* @property {function} stopVideo
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Properties
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {boolean} [expandLinksOnHover]
|
||||
* @property {number} [firstDayOfWeek]
|
||||
* @property {boolean} [focusDmInput]
|
||||
* @property {boolean} [keepLikeFollowDialogsOpen]
|
||||
* @property {boolean} [muteNotifications]
|
||||
* @property {boolean} [notificationMediaPreviews]
|
||||
* @property {boolean} [openSearchInFirstColumn]
|
||||
* @property {boolean} [skipOnLinkClick]
|
||||
* @property {string} [translationTarget]
|
||||
*/
|
||||
|
||||
/** @type {TD_Bridge} */
|
||||
export const $TD = window.$TD;
|
||||
|
||||
/** @type {TD_Properties} */
|
||||
export const $TDX = window.$TDX;
|
||||
|
||||
/**
|
||||
* @type {function(TD_Properties)[]}
|
||||
*/
|
||||
const propertyUpdateCallbacks = [];
|
||||
|
||||
/**
|
||||
* Registers a callback that responds to `$TDX` property value changes.
|
||||
* @param {function(TD_Properties)} callback
|
||||
* @param {boolean} [callAfterRegistering] whether to call the callback immediately after registering
|
||||
*/
|
||||
export function registerPropertyUpdateCallback(callback, callAfterRegistering) {
|
||||
propertyUpdateCallbacks.push(callback);
|
||||
if (callAfterRegistering) {
|
||||
callback($TDX);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers all registered callbacks.
|
||||
*/
|
||||
export function triggerPropertiesUpdated() {
|
||||
propertyUpdateCallbacks.forEach(func => func($TDX));
|
||||
}
|
67
Resources/Content/api/jquery.js
vendored
Normal file
67
Resources/Content/api/jquery.js
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
if (!("$" in window)) {
|
||||
throw "Missing jQuery in global scope";
|
||||
}
|
||||
|
||||
/** @type {JQuery} */
|
||||
export const $ = window.$;
|
||||
|
||||
/**
|
||||
* Returns a jQuery object, or throws if no elements are found.
|
||||
* @param {JQuery.Selector} selector
|
||||
* @param {Element|string|null} [context]
|
||||
* @returns {JQuery}
|
||||
*/
|
||||
export const $$ = function(selector, context) {
|
||||
// noinspection JSValidateTypes
|
||||
const result = $(selector, context);
|
||||
|
||||
if (!result.length) {
|
||||
throw "No elements were found for selector: " + selector;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef InternalJQueryData
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {InternalJQueryEvents} events
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef InternalJQueryEvents
|
||||
* @type {Object<string, function[]>}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {EventTarget} target
|
||||
* @returns {InternalJQueryEvents}
|
||||
*/
|
||||
export function getEvents(target) {
|
||||
// noinspection JSUnresolvedFunction
|
||||
/** @type {InternalJQueryData} */
|
||||
const jqData = $._data(target);
|
||||
return jqData.events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws if an element does not have a registered jQuery event.
|
||||
* @param {EventTarget|HTMLElement} element
|
||||
* @param {string} eventName
|
||||
*/
|
||||
export function ensureEventExists(element, eventName) {
|
||||
if (!(eventName in getEvents(element))) {
|
||||
const tagName = element?.tagName.toLowerCase() ?? element?.nodeName;
|
||||
const classList = "classList" in element ? Array.from(element.classList).map(cls => `.${cls}`).join("") : "";
|
||||
throw `Missing jQuery event '${eventName}' in: ${tagName}${classList}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jq {JQuery}
|
||||
* @returns {HTMLElement|null}
|
||||
*/
|
||||
export function single(jq) {
|
||||
return jq.length === 1 ? jq[0] : null;
|
||||
}
|
41
Resources/Content/api/ready.js
Normal file
41
Resources/Content/api/ready.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { $ } from "./jquery.js";
|
||||
import { TD } from "./td.js";
|
||||
import { crashDebug } from "./utils.js";
|
||||
|
||||
const callbacks = [];
|
||||
|
||||
/**
|
||||
* @param {function} callback
|
||||
*/
|
||||
function executeCallback(callback) {
|
||||
try {
|
||||
callback();
|
||||
} catch (e) {
|
||||
crashDebug("Caught error in function " + callback.name);
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isAppReady() {
|
||||
return TD.ready;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {function} callback
|
||||
*/
|
||||
export function onAppReady(callback) {
|
||||
if (isAppReady()) {
|
||||
executeCallback(callback);
|
||||
}
|
||||
else {
|
||||
callbacks.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).one("TD.ready", function() {
|
||||
callbacks.forEach(executeCallback);
|
||||
callbacks.length = 0;
|
||||
});
|
377
Resources/Content/api/td.js
Normal file
377
Resources/Content/api/td.js
Normal file
@@ -0,0 +1,377 @@
|
||||
if (!("TD" in window)) {
|
||||
throw "Missing TD in global scope";
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef TD
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {TD_Clients} clients
|
||||
* @property {TD_Components} components
|
||||
* @property {TD_Controller} controller
|
||||
* @property {TD_Languages} languages
|
||||
* @property {TD_Metrics} metrics
|
||||
* @property {Map<string, string>|null} mustaches
|
||||
* @property {TD_Services} services
|
||||
* @property {TD_Settings} settings
|
||||
* @property {TD_UI} ui
|
||||
* @property {TD_Util} util
|
||||
* @property {TD_VO} vo
|
||||
*
|
||||
* @property {boolean} ready
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Clients
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function(key: string): TwitterClient|null} getClient
|
||||
* @property {function(): TwitterClient} getPreferredClient
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Column
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {string} [_tduck_icon]
|
||||
* @property {function(id: string): ChirpBase|null} findChirp
|
||||
* @property {function(id: string): ChirpBase|null} findMostInterestingChirp
|
||||
* @property {function: string} getMediaPreviewSize
|
||||
* @property {TD_Column_Model} model
|
||||
* @property {boolean} notificationsDisabled
|
||||
* @property {function} reloadTweets
|
||||
* @property {{ columnWidth: number }} visibility
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Column_Model
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function: boolean} getHasNotification
|
||||
* @property {function: boolean} getHasSound
|
||||
* @property {function: string} getKey
|
||||
* @property {{ key: string, apiid: string }} privateState
|
||||
* @property {function(timestamp: number)} setClearedTimestamp
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Components
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {Class<BaseModal>} BaseModal
|
||||
* @property {Class} ConversationDetailView
|
||||
* @property {Class<MediaGallery>} MediaGallery
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Controller
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {TD_Controller_ColumnManager} columnManager
|
||||
* @property {TD_Controller_Init} init
|
||||
* @property {TD_Controller_Notifications} notifications
|
||||
* @property {TD_Controller_Stats} stats
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Controller_ColumnManager
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {string[]} _columnOrder
|
||||
* @property {function(id: string): TD_Column|null} get
|
||||
* @property {function: Map<string, TD_Column>} getAll
|
||||
* @property {function: TD_Column[]} getAllOrdered
|
||||
* @property {function(id: string): TD_Column|null} getByApiid
|
||||
* @property {function(id: string, direction: "left")} move
|
||||
* @property {function(modelKey: string)} showColumn
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Controller_Init
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function} showLogin
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Controller_Notifications
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function: boolean} hasNotifications
|
||||
* @property {function: boolean} isPermissionGranted
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Controller_Stats
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function} quoteTweet
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Languages
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function: string[]} getSupportedTranslationSourceLanguages
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Metrics
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function} inflate
|
||||
* @property {function} inflateMetricTriple
|
||||
* @property {function} log
|
||||
* @property {function} makeKey
|
||||
* @property {function} send
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Services
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {ChirpBase_Class} ChirpBase
|
||||
* @property {TwitterActionFollow_Class} TwitterActionFollow
|
||||
* @property {Class} TwitterActionRetweet
|
||||
* @property {Class} TwitterActionRetweetedInteraction
|
||||
* @property {Class<TwitterClient>} TwitterClient
|
||||
* @property {Class<TwitterConversation>} TwitterConversation
|
||||
* @property {Class} TwitterConversationMessageEvent
|
||||
* @property {TwitterMedia_Class} TwitterMedia
|
||||
* @property {Class<TwitterStatus>} TwitterStatus
|
||||
* @property {Class<TwitterUser>} TwitterUser
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Settings
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function: boolean} getComposeStayOpen
|
||||
* @property {function: boolean} getDisplaySensitiveMedia
|
||||
* @property {function: string} getFontSize
|
||||
* @property {function: string} getTheme
|
||||
* @property {function(boolean)} setComposeStayOpen
|
||||
* @property {function(string)} setFontSize
|
||||
* @property {function(string)} setTheme
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_UI
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {Object} columns
|
||||
* @property {TD_UI_Updates} updates
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_UI_Updates
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function(column: TD_Column, chirp: ChirpBase, parentChirp: ChirpBase)} showDetailView
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_Util
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function(a: any, b: any): number} chirpReverseColumnSort
|
||||
* @property {function} getOSName
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TD_VO
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {Class} Column
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef BaseModal
|
||||
* @typedef {Object}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef ChirpBase
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {TwitterUser_Account} account
|
||||
* @property {string} chirpType
|
||||
* @property {function: string} getChirpType
|
||||
* @property {function: string} getChirpURL
|
||||
* @property {function: ChirpBase|null} getMainTweet
|
||||
* @property {function: TwitterUser} getMainUser
|
||||
* @property {function: TwitterMedia[]} getMedia
|
||||
* @property {function: ChirpBase|null} getRelatedTweet
|
||||
* @property {function: TwitterUser[]} getReplyUsers
|
||||
* @property {function: boolean} hasImage
|
||||
* @property {function: boolean} hasMedia
|
||||
* @property {string} id
|
||||
* @property {function: boolean} isAboutYou
|
||||
* @property {TwitterConversationMessageEvent[]} [messages]
|
||||
* @property {boolean} possiblySensitive
|
||||
* @property {ChirpBase|null} quotedTweet
|
||||
* @property {function(ChirpRenderSettings)} render
|
||||
* @property {TwitterUser} user
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef ChirpBase_Class
|
||||
* @type {Class<ChirpBase>}
|
||||
*
|
||||
* @property {string} TWEET
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef ChirpRenderSettings
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {boolean} withFooter
|
||||
* @property {boolean} withTweetActions
|
||||
* @property {boolean} isInConvo
|
||||
* @property {boolean} isFavorite
|
||||
* @property {boolean} isRetweeted
|
||||
* @property {boolean} isPossiblySensitive
|
||||
* @property {string} mediaPreviewSize
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef MediaGallery
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {ChirpBase} chirp
|
||||
* @property {string} clickedMediaEntityId
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterActionFollow
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {TwitterUser} following
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterActionFollow_Class
|
||||
* @type {Class<TwitterActionFollow>}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterCardJSON
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {{ [card_url]: { [string_value]: string } }} binding_values
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterClient
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function(chirp: ChirpBase)} callback
|
||||
* @property {string} chirpId
|
||||
* @property {TwitterConversations} conversations
|
||||
* @property {function(ids: string[], onSuccess: function(users: TwitterUser[]), onError: function)} getUsersByIds
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterConversation
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function} markAsRead
|
||||
* @property {Array} messages
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterConversationMessageEvent
|
||||
* @extends ChirpBase
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterConversations
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function(id: string): TwitterConversation|null} getConversation
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterEntity_URL
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {string} url
|
||||
* @property {string} expanded_url
|
||||
* @property {string} display_url
|
||||
* @property {string} indices
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterMedia
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function: { url: string }} chooseVideoVariant
|
||||
* @property {{ media_url_https: string }} entity
|
||||
* @property {boolean} isAnimatedGif
|
||||
* @property {boolean} isVideo
|
||||
* @property {function: string} large
|
||||
* @property {string} mediaId
|
||||
* @property {string} service
|
||||
* @property {function: string} small
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterMedia_Class
|
||||
* @type {Class<TwitterMedia>}
|
||||
*
|
||||
* @property {RegExp} YOUTUBE_TINY_RE
|
||||
* @property {RegExp} YOUTUBE_LONG_RE
|
||||
* @property {RegExp} YOUTUBE_RE
|
||||
* @property {{ youtube: RegExp }} SERVICES
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterStatus
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {TwitterCardJSON|null} card
|
||||
* @property {{ urls: TwitterEntity_URL[] }} entities
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterUser
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {string} emojifiedName
|
||||
* @property {TwitterUserEntities} entities
|
||||
* @property {function: string} getProfileURL
|
||||
* @property {string} id
|
||||
* @property {string} name
|
||||
* @property {string} profileImageURL
|
||||
* @property {string} screenName
|
||||
* @property {string} url
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterUserJSON
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {string} id
|
||||
* @property {string} id_str
|
||||
* @property {string} name
|
||||
* @property {string} profile_image_url
|
||||
* @property {string} profile_image_url_https
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterUserEntities
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {{ urls: TwitterEntity_URL[] }} url
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef TwitterUser_Account
|
||||
* @type {Object}
|
||||
*
|
||||
* @property {function: string} getKey
|
||||
*/
|
||||
|
||||
/** @type {TD} */
|
||||
export const TD = window.TD;
|
49
Resources/Content/api/utils.js
Normal file
49
Resources/Content/api/utils.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Throws if an object is missing any property in the chain.
|
||||
* @param {Object} obj
|
||||
* @param {...string} chain
|
||||
*/
|
||||
export function ensurePropertyExists(obj, ...chain) {
|
||||
for (const prop of chain) {
|
||||
if (obj.hasOwnProperty(prop)) {
|
||||
obj = obj[prop];
|
||||
}
|
||||
else {
|
||||
throw "Missing property '" + prop + "' in chain [obj]." + chain.join(".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an object has every property in the chain.
|
||||
* Otherwise, returns false and triggers a debug-only error message.
|
||||
* @param {Object} obj
|
||||
* @param {...string} chain
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function checkPropertyExists(obj, ...chain) {
|
||||
try {
|
||||
ensurePropertyExists(obj, ...chain);
|
||||
return true;
|
||||
} catch (err) {
|
||||
crashDebug(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an error to the console, and also shows an error message if in debug mode.
|
||||
*/
|
||||
export function crashDebug(message) {
|
||||
console.error(message);
|
||||
debugger;
|
||||
|
||||
if ("$TD" in window) {
|
||||
window.$TD.crashDebug(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No-op function.
|
||||
*/
|
||||
export function noop() {}
|
25
Resources/Content/bootstrap.js
vendored
Normal file
25
Resources/Content/bootstrap.js
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
(function() {
|
||||
document.documentElement.id = "tduck";
|
||||
|
||||
const namespace = "{{namespace}}";
|
||||
const modules = "{{modules}}";
|
||||
const stylesheets = "{{stylesheets}}".split("|").filter(name => name.length);
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.async = false;
|
||||
script.type = "text/javascript";
|
||||
script.id = `tweetduck-bootstrap-${namespace}`;
|
||||
script.src = "td://resources/load.js";
|
||||
script.setAttribute("data-namespace", namespace);
|
||||
script.setAttribute("data-modules", modules);
|
||||
document.head.appendChild(script);
|
||||
|
||||
for (const stylesheet of stylesheets) {
|
||||
const style = document.createElement("link");
|
||||
style.id = `tweetduck-styles-${namespace}-${stylesheet}`;
|
||||
style.rel = "stylesheet";
|
||||
style.href = `td://resources/${namespace}/${stylesheet}.css`;
|
||||
style.setAttribute("data-td-exclude-notification", "");
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
})();
|
@@ -1,7 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<style type="text/css">
|
||||
<title>TweetDuck Error</title>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
body {
|
||||
color: #e1e8ed;
|
||||
background-color: #1c6399;
|
||||
@@ -16,9 +18,10 @@
|
||||
display: table;
|
||||
}
|
||||
|
||||
center {
|
||||
main {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@@ -39,11 +42,14 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<center>
|
||||
<main>
|
||||
<h1>Connection Error</h1>
|
||||
<p>{err}</p>
|
||||
<button onclick="location.href = 'https://tweetdeck.twitter.com'; [].forEach.call(document.getElementsByTagName('button'), e => e.style.visibility = 'hidden')">Retry</button>
|
||||
<button onclick="window.close()">Exit</button>
|
||||
</center>
|
||||
<p id="error"></p>
|
||||
<button onclick="location.href = 'https://tweetdeck.twitter.com'; Array.from(document.getElementsByTagName('button')).forEach(e => e.style.visibility = 'hidden');">Retry</button>
|
||||
<button onclick="window.close();">Exit</button>
|
||||
</main>
|
||||
<script type="text/javascript">
|
||||
document.getElementById("error").innerText = decodeURIComponent(location.hash.slice(1));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -63,18 +63,3 @@
|
||||
#td-introduction-modal button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
#td-introduction-modal .anondata {
|
||||
float: left;
|
||||
margin: 5px 7px;
|
||||
}
|
||||
|
||||
#td-introduction-modal .anondata input {
|
||||
vertical-align: -10%;
|
||||
}
|
||||
|
||||
#td-introduction-modal .anondata label {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
}
|
69
Resources/Content/introduction/introduction.js
Normal file
69
Resources/Content/introduction/introduction.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import { $TD } from "../api/bridge.js";
|
||||
import { $ } from "../api/jquery.js";
|
||||
import { onAppReady } from "../api/ready.js";
|
||||
import { TD } from "../api/td.js";
|
||||
|
||||
const introductionHTML = `
|
||||
<div id="td-introduction-modal" class="ovl scroll-v scroll-styled-v">
|
||||
<div class="mdl is-inverted-dark">
|
||||
<header class="mdl-header">
|
||||
<h3 class="mdl-header-title">Welcome to TweetDuck</h3>
|
||||
<a href="#" class="mdl-dismiss link-normal-dark"><i class="icon icon-close"></i></a>
|
||||
</header>
|
||||
<div class="mdl-inner">
|
||||
<div class="mdl-content">
|
||||
<p>Thank you for downloading TweetDuck!</p>
|
||||
<p><a id="td-introduction-follow" href="#">Follow @TryMyAwesomeApp</a> for latest news and updates about the app.</p>
|
||||
<div class="main-menu"></div>
|
||||
<p><strong>Right-click anywhere</strong> or click <strong>Settings – TweetDuck</strong> in the left panel to open the main menu. You can also right-click links, tweets, images and videos, and desktop notifications to access their respective context menus.</p>
|
||||
<p>Click <strong>Show Guide</strong> to see awesome features TweetDuck offers, or view the guide later by going to <strong>About TweetDuck</strong> and clicking the help button on top.</p>
|
||||
</div>
|
||||
<footer class="txt-right">
|
||||
<button class="Button--primary" data-guide><span class="label">Show Guide</span></button>
|
||||
<button class="Button--secondary"><span class="label">Close</span></button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
/**
|
||||
* Shows an introduction dialog.
|
||||
*/
|
||||
export default function() {
|
||||
const dialog = $(introductionHTML).appendTo(".js-app");
|
||||
|
||||
let tdUser = null;
|
||||
|
||||
const loadTweetDuckUser = (onSuccess, onError) => {
|
||||
if (tdUser !== null) {
|
||||
onSuccess(tdUser);
|
||||
}
|
||||
else {
|
||||
TD.controller.clients.getPreferredClient().getUsersByIds([ "957608948189880320" ], users => onSuccess(users[0]), onError);
|
||||
}
|
||||
};
|
||||
|
||||
onAppReady(function loadIntroductionTweetDuckUser() {
|
||||
loadTweetDuckUser(user => tdUser = user);
|
||||
});
|
||||
|
||||
dialog.find("#td-introduction-follow").click(function() {
|
||||
loadTweetDuckUser(user => {
|
||||
$(document).trigger("uiShowFollowFromOptions", { userToFollow: user });
|
||||
|
||||
$(".js-modals-container").find("header a[rel='user']").each(function() {
|
||||
this.outerHTML = "TweetDuck";
|
||||
});
|
||||
}, () => alert("An error occurred when retrieving the account information."));
|
||||
});
|
||||
|
||||
dialog.find("button, a.mdl-dismiss").click(function() {
|
||||
const showGuide = $(this)[0].hasAttribute("data-guide");
|
||||
|
||||
dialog.fadeOut(200, function() {
|
||||
$TD.onIntroductionClosed(showGuide);
|
||||
document.getElementById("tweetduck-styles-introduction-introduction").remove();
|
||||
dialog.remove();
|
||||
});
|
||||
});
|
||||
};
|
58
Resources/Content/load.js
Normal file
58
Resources/Content/load.js
Normal file
@@ -0,0 +1,58 @@
|
||||
(function() {
|
||||
const script = document.currentScript;
|
||||
const namespace = script.getAttribute("data-namespace");
|
||||
const tag = `[TweetDuck:${namespace}]`;
|
||||
|
||||
async function loadModule(path) {
|
||||
let module;
|
||||
try {
|
||||
module = await import(path);
|
||||
} catch (e) {
|
||||
console.error(`${tag} Error loading '${path}': ${e}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
module.default();
|
||||
console.info(`${tag} Successfully loaded '${path}'`);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error(`${tag} Error executing '${path}': ${e}`);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async function loadModules() {
|
||||
const modules = script.getAttribute("data-modules").split("|");
|
||||
|
||||
let successes = 0;
|
||||
for (const module of modules) {
|
||||
if (await loadModule(`./${namespace}/${module}.js`)) {
|
||||
++successes;
|
||||
}
|
||||
}
|
||||
|
||||
return [ successes, modules.length ];
|
||||
}
|
||||
|
||||
function notifyModulesLoaded(obj) {
|
||||
try {
|
||||
obj.onModulesLoaded(namespace);
|
||||
} catch (e) {
|
||||
console.error(`${tag} Error in post-load notification: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
loadModules().then(([ successes, total ]) => {
|
||||
if ("$TD" in window && "notifyModulesLoaded" in window.$TD) {
|
||||
notifyModulesLoaded(window.$TD);
|
||||
}
|
||||
|
||||
if ("TD_PLUGINS" in window) {
|
||||
notifyModulesLoaded(window.TD_PLUGINS);
|
||||
}
|
||||
|
||||
console.info(`${tag} Successfully loaded ${successes} / ${total} module(s).`);
|
||||
});
|
||||
})();
|
33
Resources/Content/login/hide_cookie_bar.js
Normal file
33
Resources/Content/login/hide_cookie_bar.js
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Calls a callback once an element exists.
|
||||
* @param {string} selector
|
||||
* @param {function(HTMLElement)} callback
|
||||
*/
|
||||
const triggerWhenExists = function(selector, callback) {
|
||||
const id = window.setInterval(function() {
|
||||
const ele = document.querySelector(selector);
|
||||
|
||||
if (ele && callback(ele)) {
|
||||
window.clearInterval(id);
|
||||
}
|
||||
}, 5);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides cookie bar.
|
||||
*/
|
||||
export default function() {
|
||||
triggerWhenExists("a[href^='https://help.twitter.com/rules-and-policies/twitter-cookies']", function(cookie) {
|
||||
while (!!cookie) {
|
||||
if (cookie.offsetHeight > 30) {
|
||||
cookie.remove();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
cookie = cookie.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
3
Resources/Content/login/login.css
Normal file
3
Resources/Content/login/login.css
Normal file
@@ -0,0 +1,3 @@
|
||||
html[login] *, html[logout] * {
|
||||
border-radius: 0 !important;
|
||||
}
|
8
Resources/Content/login/setup_document_attributes.js
Normal file
8
Resources/Content/login/setup_document_attributes.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export default function() {
|
||||
if (location.pathname === "/login") {
|
||||
document.documentElement.setAttribute("login", "");
|
||||
}
|
||||
else if (location.pathname === "/logout") {
|
||||
document.documentElement.setAttribute("logout", "");
|
||||
}
|
||||
};
|
17
Resources/Content/notification/add_skip_button.js
Normal file
17
Resources/Content/notification/add_skip_button.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { $TD } from "../api/bridge.js";
|
||||
|
||||
const iconHTML = `
|
||||
<svg id="td-skip" width="10" height="17" viewBox="0 0 350 600">
|
||||
<path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">
|
||||
</svg>`;
|
||||
|
||||
/**
|
||||
* Adds an icon button that skips to the next notification.
|
||||
*/
|
||||
export default function() {
|
||||
document.body.children[0].insertAdjacentHTML("beforeend", iconHTML);
|
||||
|
||||
document.getElementById("td-skip").addEventListener("click", function() {
|
||||
$TD.loadNextNotification();
|
||||
});
|
||||
};
|
@@ -0,0 +1,5 @@
|
||||
import disable_clipboard_formatting from "../tweetdeck/disable_clipboard_formatting.js";
|
||||
|
||||
export default function() {
|
||||
disable_clipboard_formatting();
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user