30 Commits

Author SHA1 Message Date
3746622a11 ssb: Give channel subscribe/unsubscribe similar grouping treatment to follows/blocks.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 33m16s
2025-09-27 16:16:34 -04:00
ccd50cf59f welcome: No longer just open testing.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 31m51s
2025-09-25 10:51:22 -04:00
93680eb43d build: Update nix, and start work on 0.2025.10.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 31m18s
2025-09-24 19:18:52 -04:00
3ae4b7086a build: Let's build 0.2025.9.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 30m53s
2025-09-24 15:02:22 -04:00
446b1f8600 ssb: Simplify some things to make funky layout issues with the sync now / stay connected buttons go away.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 31m3s
2025-09-24 13:43:33 -04:00
00fd208a2c ssb: Enough plumbing that if a blob is received, we will try to load the image again.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m24s
2025-09-24 11:34:34 -04:00
e574d03716 core: Prefer EXIT_FAILURE. 2025-09-24 10:09:40 -04:00
c1f3116c9d ssb: Clean up stale blob wants.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 31m43s
2025-09-23 20:06:50 -04:00
3aec7e6c14 ssb: Found that clicking on some message ids wasn't working.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m4s
2025-09-23 15:17:02 -04:00
9f0020dec8 update: CodeMirror.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-09-23 15:01:11 -04:00
6e78ad9729 ssb: Fix multiple issues with blob wants determination. 2025-09-23 14:40:53 -04:00
44d84a9b2a test: Add a blob replication test that succeeds for all the wrong reasons. 2025-09-22 08:18:15 -04:00
ac7809415c ssb: Fix bad escape on the query tab. 2025-09-22 07:48:11 -04:00
675cecaa20 update: CodeMirror.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m27s
2025-09-17 03:56:14 -04:00
5d179cc088 update: OpenSSL 3.5.3. 2025-09-17 03:55:36 -04:00
d905618590 update: QuickJS 2025-09-13. 2025-09-17 03:51:07 -04:00
3fd9bc0b18 format
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 31m11s
2025-09-12 15:17:04 -04:00
39abee7f73 ssb: This fixes the compose menu being clipped sometimes. Probably breaks something somewhere else.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-09-09 19:27:32 -04:00
b770619111 ssb: This fixes the attach menu being clipped. I will discover later what it breaks, I'm sure. 2025-09-09 19:20:46 -04:00
1c44857da4 core: Move register and unregister to C. 2025-09-09 19:09:37 -04:00
bca4440867 update: CodeMirror.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 30m58s
2025-09-09 09:43:42 -04:00
4855543961 docs: Not meant to be versioned.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 31m26s
2025-09-03 20:33:06 -04:00
cb3d6a98b9 docs: Appease all/both of the doxygen versions.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m45s
2025-09-03 18:47:34 -04:00
ada67a13d3 update: CodeMirror.
Some checks failed
Build Tilde Friends / Build-All (push) Failing after 5m6s
2025-09-03 12:14:58 -04:00
f4c928f26e android: Of course you can't put null in a LinkedBlockingQueue. Shrug. Fixes a shutdown crash.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 43m31s
2025-09-02 20:57:05 -04:00
91fd515d39 android: Cleaner shutdown still.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 31m38s
2025-08-27 20:23:02 -04:00
be6e841d3d android: This order seems more sensible.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-08-27 20:12:20 -04:00
af6afa6903 android: Don't log from the main thread. It might block?
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-08-27 19:20:02 -04:00
6ab5d2a28d build: Start work on 0.2025.9.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-08-27 18:55:24 -04:00
4be033f288 build: Do the nix dance. 2025-08-27 18:54:41 -04:00
31 changed files with 592 additions and 445 deletions

357
Doxyfile
View File

@@ -1,4 +1,4 @@
# Doxyfile 1.9.8 # Doxyfile 1.9.4
# This file describes the settings to be used by the documentation system # This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project. # doxygen (www.doxygen.org) for a project.
@@ -19,8 +19,7 @@
# configuration file: # configuration file:
# doxygen -x [configFile] # doxygen -x [configFile]
# Use doxygen to compare the used configuration file with the template # Use doxygen to compare the used configuration file with the template
# configuration file without replacing the environment variables or CMake type # configuration file without replacing the environment variables:
# replacement variables:
# doxygen -x_noenv [configFile] # doxygen -x_noenv [configFile]
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@@ -86,7 +85,7 @@ CREATE_SUBDIRS = NO
# level increment doubles the number of directories, resulting in 4096 # level increment doubles the number of directories, resulting in 4096
# directories at level 8 which is the default and also the maximum value. The # directories at level 8 which is the default and also the maximum value. The
# sub-directories are organized in 2 levels, the first level always has a fixed # sub-directories are organized in 2 levels, the first level always has a fixed
# number of 16 directories. # numer of 16 directories.
# Minimum value: 0, maximum value: 8, default value: 8. # Minimum value: 0, maximum value: 8, default value: 8.
# This tag requires that the tag CREATE_SUBDIRS is set to YES. # This tag requires that the tag CREATE_SUBDIRS is set to YES.
@@ -363,17 +362,6 @@ MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 5 TOC_INCLUDE_HEADINGS = 5
# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
# generate identifiers for the Markdown headings. Note: Every identifier is
# unique.
# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a
# sequence number starting at 0 and GITHUB use the lower case version of title
# with any whitespace replaced by '-' and punctuation characters removed.
# The default value is: DOXYGEN.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
MARKDOWN_ID_STYLE = DOXYGEN
# When enabled doxygen tries to link words that correspond to documented # When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can # classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or # be prevented in individual cases by putting a % sign in front of the word or
@@ -498,14 +486,6 @@ LOOKUP_CACHE_SIZE = 0
NUM_PROC_THREADS = 1 NUM_PROC_THREADS = 1
# If the TIMESTAMP tag is set different from NO then each generated page will
# contain the date or date and time when the page was generated. Setting this to
# NO can help when comparing the output of multiple runs.
# Possible values are: YES, NO, DATETIME and DATE.
# The default value is: NO.
TIMESTAMP = NO
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Build related configuration options # Build related configuration options
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@@ -587,8 +567,7 @@ HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set # undocumented classes that are normally visible in the class hierarchy. If set
# to NO, these classes will be included in the various overviews. This option # to NO, these classes will be included in the various overviews. This option
# will also hide undocumented C++ concepts if enabled. This option has no effect # has no effect if EXTRACT_ALL is enabled.
# if EXTRACT_ALL is enabled.
# The default value is: NO. # The default value is: NO.
HIDE_UNDOC_CLASSES = NO HIDE_UNDOC_CLASSES = NO
@@ -626,8 +605,7 @@ INTERNAL_DOCS = NO
# Windows (including Cygwin) and MacOS, users should typically set this option # Windows (including Cygwin) and MacOS, users should typically set this option
# to NO, whereas on Linux or other Unix flavors it should typically be set to # to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES. # YES.
# Possible values are: SYSTEM, NO and YES. # The default value is: system dependent.
# The default value is: SYSTEM.
CASE_SENSE_NAMES = YES CASE_SENSE_NAMES = YES
@@ -879,26 +857,11 @@ WARN_IF_INCOMPLETE_DOC = YES
WARN_NO_PARAMDOC = NO WARN_NO_PARAMDOC = NO
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
# undocumented enumeration values. If set to NO, doxygen will accept
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: NO.
WARN_IF_UNDOC_ENUM_VAL = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
# at the end of the doxygen process doxygen will return with a non-zero status. # at the end of the doxygen process doxygen will return with a non-zero status.
# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves # Possible values are: NO, YES and FAIL_ON_WARNINGS.
# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
# write the warning messages in between other messages but write them at the end
# of a run, in case a WARN_LOGFILE is defined the warning messages will be
# besides being in the defined file also be shown at the end of a run, unless
# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
# the behavior will remain as with the setting FAIL_ON_WARNINGS.
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
# The default value is: NO. # The default value is: NO.
WARN_AS_ERROR = NO WARN_AS_ERROR = NO
@@ -957,21 +920,10 @@ INPUT = README.md \
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv # libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see: # documentation (see:
# https://www.gnu.org/software/libiconv/) for the list of possible encodings. # https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# See also: INPUT_FILE_ENCODING
# The default value is: UTF-8. # The default value is: UTF-8.
INPUT_ENCODING = UTF-8 INPUT_ENCODING = UTF-8
# This tag can be used to specify the character encoding of the source files
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
# character encoding on a per file pattern basis. Doxygen will compare the file
# name with each pattern and apply the encoding instead of the default
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
# "INPUT_ENCODING" for further information on supported encodings.
INPUT_FILE_ENCODING =
# If the value of the INPUT tag contains directories, you can use the # If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories. # *.h) to filter out the source-files in the directories.
@@ -983,12 +935,12 @@ INPUT_FILE_ENCODING =
# Note the list of default checked file patterns might differ from the list of # Note the list of default checked file patterns might differ from the list of
# default file extension mappings. # default file extension mappings.
# #
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php, # *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be # *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
# provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, # comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. # *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.h \ FILE_PATTERNS = *.h \
*.js \ *.js \
@@ -1030,6 +982,9 @@ EXCLUDE_PATTERNS =
# output. The symbol name can be a fully qualified name, a word, or if the # output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass, # wildcard * is used, a substring. Examples: ANamespace, AClass,
# ANamespace::AClass, ANamespace::*Test # ANamespace::AClass, ANamespace::*Test
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS = EXCLUDE_SYMBOLS =
@@ -1074,11 +1029,6 @@ IMAGE_PATH = docs/images/
# code is scanned, but not when the output code is generated. If lines are added # code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly. # or removed, the anchors will not be placed correctly.
# #
# Note that doxygen will use the data processed and written to standard output
# for further processing, therefore nothing else, like debug statements or used
# commands (so in case of a Windows batch file always use @echo OFF), should be
# written to standard output.
#
# Note that for custom extensions or not directly supported extensions you also # Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not # need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen. # properly processed by doxygen.
@@ -1120,15 +1070,6 @@ FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE = README.md USE_MDFILE_AS_MAINPAGE = README.md
# The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common
# extension is to allow longer lines before the automatic comment starts. The
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
# be processed before the automatic comment starts.
# Minimum value: 7, maximum value: 10000, default value: 72.
FORTRAN_COMMENT_AFTER = 72
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to source browsing # Configuration options related to source browsing
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@@ -1266,11 +1207,10 @@ CLANG_DATABASE_PATH =
ALPHABETICAL_INDEX = YES ALPHABETICAL_INDEX = YES
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) # In case all classes in a project start with a common prefix, all classes will
# that should be ignored while generating the index headers. The IGNORE_PREFIX # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# tag works for classes, function and member names. The entity will be placed in # can be used to specify a prefix (or a list of prefixes) that should be ignored
# the alphabetical list under the first letter of the entity name that remains # while generating the index headers.
# after removing the prefix.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX = IGNORE_PREFIX =
@@ -1349,12 +1289,7 @@ HTML_STYLESHEET =
# Doxygen will copy the style sheet files to the output directory. # Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last # Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the # style sheet in the list overrules the setting of the previous ones in the
# list). # list). For an example see the documentation.
# Note: Since the styling of scrollbars can currently not be overruled in
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
# one or more extra stylesheets have been specified. So if scrollbar
# customization is desired it has to be added explicitly. For an example see the
# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET = HTML_EXTRA_STYLESHEET =
@@ -1369,19 +1304,6 @@ HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES = HTML_EXTRA_FILES =
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
# should be rendered with a dark or light theme.
# Possible values are: LIGHT always generate light mode output, DARK always
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
# the user preference, use light mode if no preference is set (the default),
# AUTO_DARK automatically set the mode according to the user preference, use
# dark mode if no preference is set and TOGGLE allow to user to switch between
# light and dark mode via a button.
# The default value is: AUTO_LIGHT.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE = AUTO_LIGHT
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to # will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a color-wheel, see # this color. Hue is specified as an angle on a color-wheel, see
@@ -1412,6 +1334,15 @@ HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80 HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
# to YES can help to show when doxygen was last run and thus if the
# documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
#HTML_TIMESTAMP = NO
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that # documentation will contain a main index with vertical navigation menus that
# are dynamically created via JavaScript. If disabled, the navigation index will # are dynamically created via JavaScript. If disabled, the navigation index will
@@ -1431,13 +1362,6 @@ HTML_DYNAMIC_MENUS = YES
HTML_DYNAMIC_SECTIONS = NO HTML_DYNAMIC_SECTIONS = NO
# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
# dynamically folded and expanded in the generated HTML source code.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_CODE_FOLDING = YES
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand # shown in the various tree structured indices initially; the user can expand
# and collapse entries dynamically later on. Doxygen will expand the tree to # and collapse entries dynamically later on. Doxygen will expand the tree to
@@ -1568,16 +1492,6 @@ BINARY_TOC = NO
TOC_EXPAND = NO TOC_EXPAND = NO
# The SITEMAP_URL tag is used to specify the full URL of the place where the
# generated documentation will be placed on the server by the user during the
# deployment of the documentation. The generated sitemap is called sitemap.xml
# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
# is specified no sitemap is generated. For information about the sitemap
# protocol see https://www.sitemaps.org
# This tag requires that the tag GENERATE_HTML is set to YES.
SITEMAP_URL =
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
@@ -1753,6 +1667,17 @@ HTML_FORMULA_FORMAT = png
FORMULA_FONTSIZE = 10 FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
# Note that when changing this option you need to delete any form_*.png files in
# the HTML output directory before the changes have effect.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
#FORMULA_TRANSPARENT = YES
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See # to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details. # the section "Including formulas" for details.
@@ -1814,8 +1739,8 @@ MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example # extension names that should be enabled during MathJax rendering. For example
# for MathJax version 2 (see # for MathJax version 2 (see https://docs.mathjax.org/en/v2.7-latest/tex.html
# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # #tex-and-latex-extensions):
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# For example for MathJax version 3 (see # For example for MathJax version 3 (see
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): # http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
@@ -2066,16 +1991,9 @@ PDF_HYPERLINKS = YES
USE_PDFLATEX = YES USE_PDFLATEX = YES
# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error. # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch # command to the generated LaTeX files. This will instruct LaTeX to keep running
# mode nothing is printed on the terminal, errors are scrolled as if <return> is # if errors occur, instead of asking the user for help.
# hit at every error; missing files that TeX tries to input or request from
# keyboard input (\read on a not open input stream) cause the job to abort,
# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
# but there is no possibility of user interaction just like in batch mode,
# SCROLL In scroll mode, TeX will stop only for missing files to input or if
# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
# each error, asking for user intervention.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES. # This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -2096,6 +2014,14 @@ LATEX_HIDE_INDICES = NO
LATEX_BIB_STYLE = plain LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
#LATEX_TIMESTAMP = NO
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered, # path from which the emoji images will be read. If a relative path is entered,
# it will be relative to the LATEX_OUTPUT directory. If left blank the # it will be relative to the LATEX_OUTPUT directory. If left blank the
@@ -2261,39 +2187,13 @@ DOCBOOK_OUTPUT = docbook
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures # AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
# the structure of the code including all documentation. Note that this feature # the structure of the code including all documentation. Note that this feature
# is still experimental and incomplete at the moment. # is still experimental and incomplete at the moment.
# The default value is: NO. # The default value is: NO.
GENERATE_AUTOGEN_DEF = NO GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to Sqlite3 output
#---------------------------------------------------------------------------
# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
# database with symbols found by doxygen stored in tables.
# The default value is: NO.
#GENERATE_SQLITE3 = NO
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
# in front of it.
# The default directory is: sqlite3.
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
#SQLITE3_OUTPUT = sqlite3
# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db
# database file will be recreated with each doxygen run. If set to NO, doxygen
# will warn if an a database file is already found and not modify it.
# The default value is: YES.
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
#SQLITE3_RECREATE_DB = YES
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the Perl module output # Configuration options related to the Perl module output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@@ -2436,15 +2336,15 @@ TAGFILES =
GENERATE_TAGFILE = GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces # If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# will be listed in the class and namespace index. If set to NO, only the # the class index. If set to NO, only the inherited external classes will be
# inherited external classes will be listed. # listed.
# The default value is: NO. # The default value is: NO.
ALLEXTERNALS = NO ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
# in the topic index. If set to NO, only the current project's groups will be # in the modules index. If set to NO, only the current project's groups will be
# listed. # listed.
# The default value is: YES. # The default value is: YES.
@@ -2458,9 +2358,16 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES EXTERNAL_PAGES = YES
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to diagram generator tools # Configuration options related to the dot tool
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
DIA_PATH =
# If set to YES the inheritance and collaboration graphs will hide inheritance # If set to YES the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class. # and usage relations if the target is undocumented or is not a class.
# The default value is: YES. # The default value is: YES.
@@ -2469,10 +2376,10 @@ HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz (see: # available from the path. This tool is part of Graphviz (see:
# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is # Bell Labs. The other options in this section have no effect if this option is
# set to NO # set to NO
# The default value is: YES. # The default value is: NO.
HAVE_DOT = YES HAVE_DOT = YES
@@ -2486,51 +2393,37 @@ HAVE_DOT = YES
DOT_NUM_THREADS = 0 DOT_NUM_THREADS = 0
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of # When you want a differently looking font in the dot files that doxygen
# subgraphs. When you want a differently looking font in the dot files that # generates you can specify the font name using DOT_FONTNAME. You need to make
# doxygen generates you can specify fontname, fontcolor and fontsize attributes. # sure dot is able to find the font, which can be done by putting it in a
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node, # standard location or by setting the DOTFONTPATH environment variable or by
# Edge and Graph Attributes specification</a> You need to make sure dot is able # setting DOT_FONTPATH to the directory containing the font.
# to find the font, which can be done by putting it in a standard location or by # The default value is: Helvetica.
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
# directory containing the font. Default graphviz fontsize is 14.
# The default value is: fontname=Helvetica,fontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" #DOT_FONTNAME = Helvetica
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can # The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a # dot graphs.
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about # Minimum value: 4, maximum value: 24, default value: 10.
# arrows shapes.</a>
# The default value is: labelfontname=Helvetica,labelfontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" #DOT_FONTSIZE = 10
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes # By default doxygen will tell dot to use the default font as specified with
# around nodes set 'shape=plain' or 'shape=plaintext' <a # DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a> # the path where dot can find it using this tag.
# The default value is: shape=box,height=0.2,width=0.4.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" #DOT_FONTPATH =
# You can set the path where dot can find font specified with fontname in # If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
# DOT_COMMON_ATTR and others dot attributes. # graph for each documented class showing the direct and indirect inheritance
# This tag requires that the tag HAVE_DOT is set to YES. # relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
DOT_FONTPATH = # to TEXT the direct and indirect inheritance relations will be shown as texts /
# links.
# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will # Possible values are: NO, YES, TEXT and GRAPH.
# generate a graph for each documented class showing the direct and indirect
# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
# relations will be shown as texts / links.
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
# The default value is: YES. # The default value is: YES.
CLASS_GRAPH = YES CLASS_GRAPH = YES
@@ -2538,21 +2431,15 @@ CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a # If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
# graph for each documented class showing the direct and indirect implementation # graph for each documented class showing the direct and indirect implementation
# dependencies (inheritance, containment, and class references variables) of the # dependencies (inheritance, containment, and class references variables) of the
# class with other documented classes. Explicit enabling a collaboration graph, # class with other documented classes.
# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
# command \collaborationgraph. Disabling a collaboration graph can be
# accomplished by means of the command \hidecollaborationgraph.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
# groups, showing the direct groups dependencies. Explicit enabling a group # groups, showing the direct groups dependencies. See also the chapter Grouping
# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means # in the manual.
# of the command \groupgraph. Disabling a directory graph can be accomplished by
# means of the command \hidegroupgraph. See also the chapter Grouping in the
# manual.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@@ -2612,9 +2499,7 @@ TEMPLATE_RELATIONS = NO
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
# YES then doxygen will generate a graph for each documented file showing the # YES then doxygen will generate a graph for each documented file showing the
# direct and indirect include dependencies of the file with other documented # direct and indirect include dependencies of the file with other documented
# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, # files.
# can be accomplished by means of the command \includegraph. Disabling an
# include graph can be accomplished by means of the command \hideincludegraph.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@@ -2623,10 +2508,7 @@ INCLUDE_GRAPH = YES
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
# set to YES then doxygen will generate a graph for each documented file showing # set to YES then doxygen will generate a graph for each documented file showing
# the direct and indirect include dependencies of the file with other documented # the direct and indirect include dependencies of the file with other documented
# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set # files.
# to NO, can be accomplished by means of the command \includedbygraph. Disabling
# an included by graph can be accomplished by means of the command
# \hideincludedbygraph.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@@ -2666,10 +2548,7 @@ GRAPHICAL_HIERARCHY = YES
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the # If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
# dependencies a directory has on other directories in a graphical way. The # dependencies a directory has on other directories in a graphical way. The
# dependency relations are determined by the #include relations between the # dependency relations are determined by the #include relations between the
# files in the directories. Explicit enabling a directory graph, when # files in the directories.
# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
# \directorygraph. Disabling a directory graph can be accomplished by means of
# the command \hidedirectorygraph.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@@ -2685,13 +2564,12 @@ DIR_GRAPH_MAX_DEPTH = 1
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. For an explanation of the image formats see the section # generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see: # output formats in the documentation of the dot tool (Graphviz (see:
# https://www.graphviz.org/)). # http://www.graphviz.org/)).
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this # to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement). # requirement).
# Possible values are: png, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, # Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
# gif, gif:cairo, gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, # png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:cairo, png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus. # png:gdiplus:gdiplus.
# The default value is: png. # The default value is: png.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@@ -2723,12 +2601,11 @@ DOT_PATH =
DOTFILE_DIRS = DOTFILE_DIRS =
# You can include diagrams made with dia in doxygen documentation. Doxygen will # The MSCFILE_DIRS tag can be used to specify one or more directories that
# then run dia to produce the diagram and insert it in the documentation. The # contain msc files that are included in the documentation (see the \mscfile
# DIA_PATH tag allows you to specify the directory where the dia binary resides. # command).
# If left empty dia is assumed to be found in the default search path.
DIA_PATH = MSCFILE_DIRS =
# The DIAFILE_DIRS tag can be used to specify one or more directories that # The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile # contain dia files that are included in the documentation (see the \diafile
@@ -2778,6 +2655,18 @@ DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0 MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
# background. This is disabled by default, because dot on Windows does not seem
# to support this out of the box.
#
# Warning: Depending on the platform used, enabling this option may lead to
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
# read).
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
#DOT_TRANSPARENT = NO
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This # files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support # makes dot run faster, but since only newer versions of dot (>1.8.10) support
@@ -2805,19 +2694,3 @@ GENERATE_LEGEND = YES
# The default value is: YES. # The default value is: YES.
DOT_CLEANUP = YES DOT_CLEANUP = YES
# You can define message sequence charts within doxygen comments using the \msc
# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
# use a built-in version of mscgen tool to produce the charts. Alternatively,
# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
# specifying prog as the value, doxygen will call the tool as prog -T
# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
# output file formats "png", "eps", "svg", and "ismap".
MSCGEN_TOOL =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
MSCFILE_DIRS =

View File

@@ -16,9 +16,9 @@ MAKEFLAGS += --no-builtin-rules
## LD := Linker. ## LD := Linker.
## ANDROID_SDK := Path to the Android SDK. ## ANDROID_SDK := Path to the Android SDK.
VERSION_CODE := 42 VERSION_CODE := 44
VERSION_CODE_IOS := 16 VERSION_CODE_IOS := 18
VERSION_NUMBER := 0.2025.8 VERSION_NUMBER := 0.2025.10-wip
VERSION_NAME := This program kills fascists. VERSION_NAME := This program kills fascists.
IPHONEOS_VERSION_MIN=14.0 IPHONEOS_VERSION_MIN=14.0

View File

@@ -1,5 +1,5 @@
{ {
"type": "tildefriends-app", "type": "tildefriends-app",
"emoji": "🦀", "emoji": "🦀",
"previous": "&Hd6CuhhnZIf13PdFJYZBUYLYZO84WdaKvWXLC29M7Ac=.sha256" "previous": "&01jXxJgs24zTcJk+csXeUWfm/MQ/+94Zy7K0r2OYmWw=.sha256"
} }

View File

@@ -76,6 +76,9 @@ tfrpc.register(function setHash(hash) {
core.register('onMessage', async function (id) { core.register('onMessage', async function (id) {
await tfrpc.rpc.notifyNewMessage(id); await tfrpc.rpc.notifyNewMessage(id);
}); });
core.register('onBlob', async function (id) {
await tfrpc.rpc.notifyNewBlob(id);
});
tfrpc.register(async function store_blob(blob) { tfrpc.register(async function store_blob(blob) {
if (Array.isArray(blob)) { if (Array.isArray(blob)) {
blob = Uint8Array.from(blob); blob = Uint8Array.from(blob);

View File

@@ -65,6 +65,17 @@ class TfElement extends LitElement {
tfrpc.register(async function notifyNewMessage(id) { tfrpc.register(async function notifyNewMessage(id) {
await self.fetch_new_message(id); await self.fetch_new_message(id);
}); });
tfrpc.register(async function notifyNewBlob(id) {
window.dispatchEvent(
new CustomEvent('blob-stored', {
bubbles: true,
composed: true,
detail: {
id: id,
},
})
);
});
tfrpc.register(function set(name, value) { tfrpc.register(function set(name, value) {
if (name === 'broadcasts') { if (name === 'broadcasts') {
self.broadcasts = value; self.broadcasts = value;
@@ -732,7 +743,7 @@ class TfElement extends LitElement {
whoami=${this.whoami} whoami=${this.whoami}
.users=${this.users} .users=${this.users}
query=${this.hash?.startsWith('#sql=') query=${this.hash?.startsWith('#sql=')
? decodeURIComponent(this.hash.substring(5)) ? this.hash.substring(5)
: null} : null}
></tf-tab-query> ></tf-tab-query>
`; `;
@@ -789,14 +800,13 @@ class TfElement extends LitElement {
class="w3-bar w3-theme-l1" class="w3-bar w3-theme-l1"
style="position: static; top: 0; z-index: 10" style="position: static; top: 0; z-index: 10"
> >
${this.is_administrator && self.tab != 'news' ${this.is_administrator
? html` ? html`
<button <button
class=${'w3-bar-item w3-button w3-circle w3-ripple' + class=${'w3-bar-item w3-button w3-circle w3-ripple' +
(this.connections?.some((x) => x.flags.one_shot) (this.connections?.some((x) => x.flags.one_shot)
? ' w3-spin' ? ' w3-spin'
: '')} : '')}
style="width: 1.5em; height: 1.5em; padding: 8px"
@click=${this.refresh} @click=${this.refresh}
> >

View File

@@ -625,7 +625,7 @@ class TfComposeElement extends LitElement {
<div class="w3-half"> <div class="w3-half">
<span <span
class="w3-input w3-theme-d1 w3-border" class="w3-input w3-theme-d1 w3-border"
style="resize: vertical; width: 100%; overflow: hidden; white-space: pre-wrap" style="resize: vertical; width: 100%; white-space: pre-wrap"
placeholder="Write a post here." placeholder="Write a post here."
id="edit" id="edit"
@input=${this.input} @input=${this.input}

View File

@@ -45,11 +45,14 @@ class TfMessageElement extends LitElement {
connectedCallback() { connectedCallback() {
super.connectedCallback(); super.connectedCallback();
this._click_callback = this.document_click.bind(this); this._click_callback = this.document_click.bind(this);
this._blob_stored = this.blob_stored.bind(this);
document.body.addEventListener('mouseup', this._click_callback); document.body.addEventListener('mouseup', this._click_callback);
window.addEventListener('blob-stored', this._blob_stored);
} }
disconnectedCallback() { disconnectedCallback() {
super.disconnectedCallback(); super.disconnectedCallback();
window.removeEventListener('blob-stored', this._blob_stored);
document.body.removeEventListener('mouseup', this._click_callback); document.body.removeEventListener('mouseup', this._click_callback);
} }
@@ -61,6 +64,16 @@ class TfMessageElement extends LitElement {
} }
} }
blob_stored(event) {
let search = `/${event.detail.id}/view`;
for (let img of this.shadowRoot.querySelectorAll('img')) {
if (img.src.indexOf(search) != -1) {
let src = img.src.split('?')[0];
img.src = `${src}?${new Date().valueOf()}`;
}
}
}
show_reply() { show_reply() {
let event = new CustomEvent('tf-draft', { let event = new CustomEvent('tf-draft', {
bubbles: true, bubbles: true,
@@ -537,7 +550,7 @@ class TfMessageElement extends LitElement {
</style> </style>
<div <div
class="w3-card-4 ${this.class_background()} w3-border-theme w3-margin-top" class="w3-card-4 ${this.class_background()} w3-border-theme w3-margin-top"
style="overflow: auto; overflow-wrap: anywhere; display: block; max-width: 100%" style="overflow-wrap: anywhere; display: block; max-width: 100%"
> >
${inner} ${inner}
</div> </div>
@@ -631,6 +644,35 @@ class TfMessageElement extends LitElement {
return result; return result;
} }
channel_group_by_author() {
let sorted = this.message.messages
.map((x) => [
x.author,
x.content.subscribed ? 'subscribed to' : 'unsubscribed from',
x.content.channel,
x,
])
.sort();
let result = [];
let last;
let group;
for (let row of sorted) {
if (last && last[0] == row[0] && last[1] == row[1]) {
group.push(row[2]);
} else {
if (group) {
result.push({author: last[0], action: last[1], channels: group});
}
last = row;
group = [row[2]];
}
}
if (group) {
result.push({author: last[0], action: last[1], channels: group});
}
return result;
}
allow_unread() { allow_unread() {
return ( return (
this.channel == '@' || this.channel == '@' ||
@@ -706,6 +748,55 @@ class TfMessageElement extends LitElement {
</button> </button>
`); `);
} }
} else if (this.message?.type === 'channel_group') {
if (this.expanded[this.expanded_key()]) {
return this.render_frame(html`
<div class="w3-padding">
${this.message.messages.map(
(x) =>
html`<tf-message
.message=${x}
whoami=${this.whoami}
.users=${this.users}
.drafts=${this.drafts}
.expanded=${this.expanded}
channel=${this.channel}
channel_unread=${this.channel_unread}
></tf-message>`
)}
</div>
<button
class="w3-button w3-theme-d1 w3-block w3-bar"
style="box-sizing: border-box"
@click=${() => self.set_expanded(false)}
>
Collapse
</button>
`);
} else {
return this.render_frame(html`
<div class="w3-padding">
${this.channel_group_by_author().map(
(x) => html`
<div>
<tf-user id=${x.author} .users=${this.users}></tf-user>
${x.action}
${x.channels.map(
(y) => html` <tf-tag tag=${'#' + y}></tf-tag> `
)}
</div>
`
)}
</div>
<button
class="w3-button w3-theme-d1 w3-block w3-bar"
style="box-sizing: border-box"
@click=${() => self.set_expanded(true)}
>
Expand
</button>
`);
}
} else if (this.message.placeholder) { } else if (this.message.placeholder) {
return this.render_frame( return this.render_frame(
html`<div> html`<div>

View File

@@ -160,11 +160,29 @@ class TfNewsElement extends LitElement {
return recursive_sort(roots, true); return recursive_sort(roots, true);
} }
group_following(messages) { group_messages(messages) {
let result = []; let result = [];
let group = []; let group = [];
let type = undefined;
for (let message of messages) { for (let message of messages) {
if (message?.content?.type === 'contact') { if (
message?.content?.type === 'contact' ||
message?.content?.type === 'channel'
) {
if (type && message.content.type !== type) {
if (group.length == 1) {
result.push(group[0]);
group = [];
} else if (group.length > 1) {
result.push({
rowid: Math.max(...group.map((x) => x.rowid)),
type: `${type}_group`,
messages: group,
});
group = [];
}
}
type = message.content.type;
group.push(message); group.push(message);
} else { } else {
if (group.length == 1) { if (group.length == 1) {
@@ -173,12 +191,13 @@ class TfNewsElement extends LitElement {
} else if (group.length > 1) { } else if (group.length > 1) {
result.push({ result.push({
rowid: Math.max(...group.map((x) => x.rowid)), rowid: Math.max(...group.map((x) => x.rowid)),
type: 'contact_group', type: `${type}_group`,
messages: group, messages: group,
}); });
group = []; group = [];
} }
result.push(message); result.push(message);
type = undefined;
} }
} }
if (group.length == 1) { if (group.length == 1) {
@@ -187,7 +206,7 @@ class TfNewsElement extends LitElement {
} else if (group.length > 1) { } else if (group.length > 1) {
result.push({ result.push({
rowid: Math.max(...group.map((x) => x.rowid)), rowid: Math.max(...group.map((x) => x.rowid)),
type: 'contact_group', type: `${type}_group`,
messages: group, messages: group,
}); });
} }
@@ -200,7 +219,7 @@ class TfNewsElement extends LitElement {
load_and_render(messages) { load_and_render(messages) {
let messages_by_id = this.process_messages(messages); let messages_by_id = this.process_messages(messages);
let final_messages = this.group_following( let final_messages = this.group_messages(
this.finalize_messages(messages_by_id) this.finalize_messages(messages_by_id)
); );
let unread_rowid = -1; let unread_rowid = -1;

View File

@@ -411,7 +411,7 @@ class TfTabNewsElement extends LitElement {
return cache(html` return cache(html`
${this.render_sidebar()} ${this.render_sidebar()}
<div <div
style="margin-left: 2in; padding: 0px; top: 0; max-height: 100%; overflow: auto; contain: layout" style="margin-left: 2in; padding: 0px; top: 0; height: 100vh; max-height: 100%; overflow: auto; contain: layout"
id="main" id="main"
class="w3-main" class="w3-main"
> >

View File

@@ -104,12 +104,12 @@ export function markdown(md) {
node.destination.startsWith('@') && node.destination.startsWith('@') &&
node.destination.endsWith('.ed25519') node.destination.endsWith('.ed25519')
) { ) {
node.destination = '#' + node.destination; node.destination = '#' + encodeURIComponent(node.destination);
} else if ( } else if (
node.destination.startsWith('%') && node.destination.startsWith('%') &&
node.destination.endsWith('.sha256') node.destination.endsWith('.sha256')
) { ) {
node.destination = '#' + node.destination; node.destination = '#' + encodeURIComponent(node.destination);
} else if ( } else if (
node.destination.startsWith('&') && node.destination.startsWith('&') &&
node.destination.endsWith('.sha256') node.destination.endsWith('.sha256')

View File

@@ -1,5 +1,5 @@
{ {
"type": "tildefriends-app", "type": "tildefriends-app",
"emoji": "👋", "emoji": "👋",
"previous": "&5NkMRSgcMqCYF3xcLOBmaytkoxfV9zx4br7JladKPTs=.sha256" "previous": "&ijyL/pyTwguBd9njagU7Vpc/1EyRermZuzrlq1mnzbY=.sha256"
} }

View File

@@ -104,7 +104,7 @@
src="googleplay.svg" src="googleplay.svg"
style="height: 2em; margin: 0" style="height: 2em; margin: 0"
/> />
Get it on Google Play (Open Testing) Get it on Google Play
</a> </a>
<a <a
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top" class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
@@ -298,7 +298,7 @@
<!-- Technlology Section --> <!-- Technlology Section -->
<div class="w3-container w3-padding-64 w3-light-grey w3-center"> <div class="w3-container w3-padding-64 w3-light-grey w3-center">
<h1 class="w3-jumbo"><b>Built the Old Fashioned Way</b></h1> <h1 class="w3-jumbo"><b>Built to Last</b></h1>
<p> <p>
Tilde Friends strives to use only simple and widely adopted dependencies Tilde Friends strives to use only simple and widely adopted dependencies
in order to keep it easy to build for all sorts of platforms and in order to keep it easy to build for all sorts of platforms and

View File

@@ -438,6 +438,9 @@ class TfNavigationElement extends LitElement {
} }
} }
/**
* Create a tf-navigation element.
*/
customElements.define('tf-navigation', TfNavigationElement); customElements.define('tf-navigation', TfNavigationElement);
/** /**

View File

@@ -199,23 +199,6 @@ async function getProcessBlob(blobId, key, options) {
let imports = { let imports = {
core: { core: {
broadcast: broadcast.bind(process), broadcast: broadcast.bind(process),
register: function (eventName, handler) {
if (!process.eventHandlers[eventName]) {
process.eventHandlers[eventName] = [];
}
process.eventHandlers[eventName].push(handler);
},
unregister: function (eventName, handler) {
if (process.eventHandlers[eventName]) {
let index = process.eventHandlers[eventName].indexOf(handler);
if (index != -1) {
process.eventHandlers[eventName].splice(index, 1);
}
if (process.eventHandlers[eventName].length == 0) {
delete process.eventHandlers[eventName];
}
}
},
user: getUser(process, process), user: getUser(process, process),
users: async function () { users: async function () {
try { try {
@@ -703,10 +686,17 @@ async function getProcessBlob(blobId, key, options) {
return process; return process;
} }
/**
* SSB message added callback.
*/
ssb.addEventListener('message', function () { ssb.addEventListener('message', function () {
broadcastEvent('onMessage', [...arguments]); broadcastEvent('onMessage', [...arguments]);
}); });
ssb.addEventListener('blob', function () {
broadcastEvent('onBlob', [...arguments]);
});
ssb.addEventListener('broadcasts', function () { ssb.addEventListener('broadcasts', function () {
broadcastEvent('onBroadcastsChanged', []); broadcastEvent('onBroadcastsChanged', []);
}); });

View File

@@ -25,14 +25,14 @@
}: }:
pkgs.stdenv.mkDerivation rec { pkgs.stdenv.mkDerivation rec {
pname = "tildefriends"; pname = "tildefriends";
version = "0.0.33"; version = "0.2025.9";
src = pkgs.fetchFromGitea { src = pkgs.fetchFromGitea {
domain = "dev.tildefriends.net"; domain = "dev.tildefriends.net";
owner = "cory"; owner = "cory";
repo = "tildefriends"; repo = "tildefriends";
rev = "v${version}"; rev = "v${version}";
hash = "sha256-9D28gmaBTRVyXhY3zZd/W9PsXA1YZt/K69hz41aVP04="; hash = "sha256-1nhsfhdOO5HIiiTMb+uROB8nDPL/UpOYm52hZ/OpPyk=";
fetchSubmodules = true; fetchSubmodules = true;
}; };

File diff suppressed because one or more lines are too long

240
deps/codemirror_src/package-lock.json generated vendored
View File

@@ -19,9 +19,9 @@
} }
}, },
"node_modules/@codemirror/autocomplete": { "node_modules/@codemirror/autocomplete": {
"version": "6.18.6", "version": "6.18.7",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.7.tgz",
"integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", "integrity": "sha512-8EzdeIoWPJDsMBwz3zdzwXnUpCzMiCyz5/A3FIPpriaclFCGDkAzK13sMcnsu5rowqiyeQN2Vs2TsOcoDPZirQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@codemirror/language": "^6.0.0", "@codemirror/language": "^6.0.0",
@@ -56,9 +56,9 @@
} }
}, },
"node_modules/@codemirror/lang-html": { "node_modules/@codemirror/lang-html": {
"version": "6.4.9", "version": "6.4.10",
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz", "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.10.tgz",
"integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==", "integrity": "sha512-h/SceTVsN5r+WE+TVP2g3KDvNoSzbSrtZXCKo4vkKdbfT5t4otuVgngGdFukOO/rwRD2++pCxoh6xD4TEVMkQA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.0.0", "@codemirror/autocomplete": "^6.0.0",
@@ -155,9 +155,9 @@
} }
}, },
"node_modules/@codemirror/view": { "node_modules/@codemirror/view": {
"version": "6.38.1", "version": "6.38.3",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.1.tgz", "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.3.tgz",
"integrity": "sha512-RmTOkE7hRU3OVREqFVITWHz6ocgBjv08GoePscAakgVQfciA3SGCEk7mb9IzwW61cKKmlTpHXG6DUE5Ubx+MGQ==", "integrity": "sha512-x2t87+oqwB1mduiQZ6huIghjMt4uZKFEdj66IcXw7+a5iBEvv9lh7EWDRHI7crnD4BMGpnyq/RzmCGbiEZLcvQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@codemirror/state": "^6.5.0", "@codemirror/state": "^6.5.0",
@@ -206,9 +206,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@jridgewell/trace-mapping": { "node_modules/@jridgewell/trace-mapping": {
"version": "0.3.30", "version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -254,9 +254,9 @@
} }
}, },
"node_modules/@lezer/javascript": { "node_modules/@lezer/javascript": {
"version": "1.5.1", "version": "1.5.4",
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.1.tgz", "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz",
"integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==", "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@lezer/common": "^1.2.0", "@lezer/common": "^1.2.0",
@@ -338,9 +338,9 @@
} }
}, },
"node_modules/@rollup/pluginutils": { "node_modules/@rollup/pluginutils": {
"version": "5.2.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
"integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/estree": "^1.0.0", "@types/estree": "^1.0.0",
@@ -360,9 +360,9 @@
} }
}, },
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.2.tgz",
"integrity": "sha512-B2wfzCJ+ps/OBzRjeds7DlJumCU3rXMxJJS1vzURyj7+KBHGONm7c9q1TfdBl4vCuNMkDvARn3PBl2wZzuR5mw==", "integrity": "sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -373,9 +373,9 @@
] ]
}, },
"node_modules/@rollup/rollup-android-arm64": { "node_modules/@rollup/rollup-android-arm64": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.2.tgz",
"integrity": "sha512-FGJYXvYdn8Bs6lAlBZYT5n+4x0ciEp4cmttsvKAZc/c8/JiPaQK8u0c/86vKX8lA7OY/+37lIQSe0YoAImvBAA==", "integrity": "sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -386,9 +386,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-arm64": { "node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.2.tgz",
"integrity": "sha512-/9qwE/BM7ATw/W/OFEMTm3dmywbJyLQb4f4v5nmOjgYxPIGpw7HaxRi6LnD4Pjn/q7k55FGeHe1/OD02w63apA==", "integrity": "sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -399,9 +399,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-x64": { "node_modules/@rollup/rollup-darwin-x64": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.2.tgz",
"integrity": "sha512-QkWfNbeRuzFnv2d0aPlrzcA3Ebq2mE8kX/5Pl7VdRShbPBjSnom7dbT8E3Jmhxo2RL784hyqGvR5KHavCJQciw==", "integrity": "sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -412,9 +412,9 @@
] ]
}, },
"node_modules/@rollup/rollup-freebsd-arm64": { "node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.2.tgz",
"integrity": "sha512-+ToyOMYnSfV8D+ckxO6NthPln/PDNp1P6INcNypfZ7muLmEvPKXqduUiD8DlJpMMT8LxHcE5W0dK9kXfJke9Zw==", "integrity": "sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -425,9 +425,9 @@
] ]
}, },
"node_modules/@rollup/rollup-freebsd-x64": { "node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.2.tgz",
"integrity": "sha512-cGT6ey/W+sje6zywbLiqmkfkO210FgRz7tepWAzzEVgQU8Hn91JJmQWNqs55IuglG8sJdzk7XfNgmGRtcYlo1w==", "integrity": "sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -438,9 +438,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-gnueabihf": { "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.2.tgz",
"integrity": "sha512-9fhTJyOb275w5RofPSl8lpr4jFowd+H4oQKJ9XTYzD1JWgxdZKE8bA6d4npuiMemkecQOcigX01FNZNCYnQBdA==", "integrity": "sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -451,9 +451,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-musleabihf": { "node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.2.tgz",
"integrity": "sha512-+6kCIM5Zjvz2HwPl/udgVs07tPMIp1VU2Y0c72ezjOvSvEfAIWsUgpcSDvnC7g9NrjYR6X9bZT92mZZ90TfvXw==", "integrity": "sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -464,9 +464,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-gnu": { "node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.2.tgz",
"integrity": "sha512-SWuXdnsayCZL4lXoo6jn0yyAj7TTjWE4NwDVt9s7cmu6poMhtiras5c8h6Ih6Y0Zk6Z+8t/mLumvpdSPTWub2Q==", "integrity": "sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -477,9 +477,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-musl": { "node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.2.tgz",
"integrity": "sha512-vDknMDqtMhrrroa5kyX6tuC0aRZZlQ+ipDfbXd2YGz5HeV2t8HOl/FDAd2ynhs7Ki5VooWiiZcCtxiZ4IjqZwQ==", "integrity": "sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -489,10 +489,10 @@
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-linux-loongarch64-gnu": { "node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.2.tgz",
"integrity": "sha512-mCBkjRZWhvjtl/x+Bd4fQkWZT8canStKDxGrHlBiTnZmJnWygGcvBylzLVCZXka4dco5ymkWhZlLwKCGFF4ivw==", "integrity": "sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
@@ -503,9 +503,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-ppc64-gnu": { "node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.2.tgz",
"integrity": "sha512-YMdz2phOTFF+Z66dQfGf0gmeDSi5DJzY5bpZyeg9CPBkV9QDzJ1yFRlmi/j7WWRf3hYIWrOaJj5jsfwgc8GTHQ==", "integrity": "sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -516,9 +516,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-gnu": { "node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.2.tgz",
"integrity": "sha512-r0WKLSfFAK8ucG024v2yiLSJMedoWvk8yWqfNICX28NHDGeu3F/wBf8KG6mclghx4FsLePxJr/9N8rIj1PtCnw==", "integrity": "sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -529,9 +529,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-musl": { "node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.2.tgz",
"integrity": "sha512-IaizpPP2UQU3MNyPH1u0Xxbm73D+4OupL0bjo4Hm0496e2wg3zuvoAIhubkD1NGy9fXILEExPQy87mweujEatA==", "integrity": "sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -542,9 +542,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-s390x-gnu": { "node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.2.tgz",
"integrity": "sha512-aCM29orANR0a8wk896p6UEgIfupReupnmISz6SUwMIwTGaTI8MuKdE0OD2LvEg8ondDyZdMvnaN3bW4nFbATPA==", "integrity": "sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@@ -555,9 +555,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-gnu": { "node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.2.tgz",
"integrity": "sha512-0Xj1vZE3cbr/wda8d/m+UeuSL+TDpuozzdD4QaSzu/xSOMK0Su5RhIkF7KVHFQsobemUNHPLEcYllL7ZTCP/Cg==", "integrity": "sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -568,9 +568,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-musl": { "node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.2.tgz",
"integrity": "sha512-kM/orjpolfA5yxsx84kI6bnK47AAZuWxglGKcNmokw2yy9i5eHY5UAjcX45jemTJnfHAWo3/hOoRqEeeTdL5hw==", "integrity": "sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -580,10 +580,23 @@
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.2.tgz",
"integrity": "sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"openharmony"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": { "node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.2.tgz",
"integrity": "sha512-cNLH4psMEsWKILW0isbpQA2OvjXLbKvnkcJFmqAptPQbtLrobiapBJVj6RoIvg6UXVp5w0wnIfd/Q56cNpF+Ew==", "integrity": "sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -594,9 +607,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-ia32-msvc": { "node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.2.tgz",
"integrity": "sha512-OiEa5lRhiANpv4SfwYVgQ3opYWi/QmPDC5ve21m8G9pf6ZO+aX1g2EEF1/IFaM1xPSP7mK0msTRXlPs6mIagkg==", "integrity": "sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -606,10 +619,23 @@
"win32" "win32"
] ]
}, },
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.2.tgz",
"integrity": "sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": { "node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.2.tgz",
"integrity": "sha512-IKL9mewGZ5UuuX4NQlwOmxPyqielvkAPUS2s1cl6yWjjQvyN3h5JTdVFGD5Jr5xMjRC8setOfGQDVgX8V+dkjg==", "integrity": "sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -799,9 +825,9 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.46.4", "version": "4.52.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.4.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.2.tgz",
"integrity": "sha512-YbxoxvoqNg9zAmw4+vzh1FkGAiZRK+LhnSrbSrSXMdZYsRPDWoshcSd/pldKRO6lWzv/e9TiJAVQyirYIeSIPQ==", "integrity": "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/estree": "1.0.8" "@types/estree": "1.0.8"
@@ -814,26 +840,28 @@
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.46.4", "@rollup/rollup-android-arm-eabi": "4.52.2",
"@rollup/rollup-android-arm64": "4.46.4", "@rollup/rollup-android-arm64": "4.52.2",
"@rollup/rollup-darwin-arm64": "4.46.4", "@rollup/rollup-darwin-arm64": "4.52.2",
"@rollup/rollup-darwin-x64": "4.46.4", "@rollup/rollup-darwin-x64": "4.52.2",
"@rollup/rollup-freebsd-arm64": "4.46.4", "@rollup/rollup-freebsd-arm64": "4.52.2",
"@rollup/rollup-freebsd-x64": "4.46.4", "@rollup/rollup-freebsd-x64": "4.52.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.46.4", "@rollup/rollup-linux-arm-gnueabihf": "4.52.2",
"@rollup/rollup-linux-arm-musleabihf": "4.46.4", "@rollup/rollup-linux-arm-musleabihf": "4.52.2",
"@rollup/rollup-linux-arm64-gnu": "4.46.4", "@rollup/rollup-linux-arm64-gnu": "4.52.2",
"@rollup/rollup-linux-arm64-musl": "4.46.4", "@rollup/rollup-linux-arm64-musl": "4.52.2",
"@rollup/rollup-linux-loongarch64-gnu": "4.46.4", "@rollup/rollup-linux-loong64-gnu": "4.52.2",
"@rollup/rollup-linux-ppc64-gnu": "4.46.4", "@rollup/rollup-linux-ppc64-gnu": "4.52.2",
"@rollup/rollup-linux-riscv64-gnu": "4.46.4", "@rollup/rollup-linux-riscv64-gnu": "4.52.2",
"@rollup/rollup-linux-riscv64-musl": "4.46.4", "@rollup/rollup-linux-riscv64-musl": "4.52.2",
"@rollup/rollup-linux-s390x-gnu": "4.46.4", "@rollup/rollup-linux-s390x-gnu": "4.52.2",
"@rollup/rollup-linux-x64-gnu": "4.46.4", "@rollup/rollup-linux-x64-gnu": "4.52.2",
"@rollup/rollup-linux-x64-musl": "4.46.4", "@rollup/rollup-linux-x64-musl": "4.52.2",
"@rollup/rollup-win32-arm64-msvc": "4.46.4", "@rollup/rollup-openharmony-arm64": "4.52.2",
"@rollup/rollup-win32-ia32-msvc": "4.46.4", "@rollup/rollup-win32-arm64-msvc": "4.52.2",
"@rollup/rollup-win32-x64-msvc": "4.46.4", "@rollup/rollup-win32-ia32-msvc": "4.52.2",
"@rollup/rollup-win32-x64-gnu": "4.52.2",
"@rollup/rollup-win32-x64-msvc": "4.52.2",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
@@ -915,14 +943,14 @@
} }
}, },
"node_modules/terser": { "node_modules/terser": {
"version": "5.43.1", "version": "5.44.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz",
"integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"@jridgewell/source-map": "^0.3.3", "@jridgewell/source-map": "^0.3.3",
"acorn": "^8.14.0", "acorn": "^8.15.0",
"commander": "^2.20.0", "commander": "^2.20.0",
"source-map-support": "~0.5.20" "source-map-support": "~0.5.20"
}, },

2
deps/quickjs vendored

6
flake.lock generated
View File

@@ -20,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1753749649, "lastModified": 1758589230,
"narHash": "sha256-+jkEZxs7bfOKfBIk430K+tK9IvXlwzqQQnppC2ZKFj4=", "narHash": "sha256-zMTCFGe8aVGTEr2RqUi/QzC1nOIQ0N1HRsbqB4f646k=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "1f08a4df998e21f4e8be8fb6fbf61d11a1a5076a", "rev": "d1d883129b193f0b495d75c148c2c3a7d95789a0",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -0,0 +1,8 @@
* Fixed multiple issues with blob replication.
* Fixed some link encoding issues.
* Fixed some context menus being cut off.
* Minor Android fixes.
* Updates:
* CodeMirror
* OpenSSL 3.5.3
* QuickJS 2025-09-13

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unprompted.tildefriends" package="com.unprompted.tildefriends"
android:versionCode="42" android:versionCode="44"
android:versionName="0.2025.8"> android:versionName="0.2025.10-wip">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<application <application

View File

@@ -19,7 +19,6 @@ import android.os.RemoteException;
import android.os.StrictMode; import android.os.StrictMode;
import android.text.InputType; import android.text.InputType;
import android.util.Base64; import android.util.Base64;
import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
@@ -44,6 +43,7 @@ import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FileReader; import java.io.FileReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class TildeFriendsActivity extends Activity { public class TildeFriendsActivity extends Activity {
@@ -53,29 +53,42 @@ public class TildeFriendsActivity extends Activity {
String port_file_path; String port_file_path;
Thread create_thread; Thread create_thread;
Thread server_thread; Thread server_thread;
Thread log_thread;
ServiceConnection service_connection; ServiceConnection service_connection;
FileObserver observer; FileObserver observer;
LinkedBlockingQueue<String> log_queue = new LinkedBlockingQueue<String>();
private ValueCallback<Uri[]> upload_message; private ValueCallback<Uri[]> upload_message;
private final static int FILECHOOSER_RESULT = 1; private final static int FILECHOOSER_RESULT = 1;
private float touch_down_y; private float touch_down_y;
private boolean ready = false; private boolean ready = false;
private boolean loaded = false; private boolean loaded = false;
private boolean shutting_down = false;
static { static {
Log.w("tildefriends", "Calling system.loadLibrary()."); log("Calling system.loadLibrary().");
System.loadLibrary("tildefriends"); System.loadLibrary("tildefriends");
Log.w("tildefriends", "system.loadLibrary() completed."); log("system.loadLibrary() completed.");
} }
public static native int tf_server_main(String files_dir, String apk_path, String out_port_file_path, ConnectivityManager connectivity_manager); public static native int tf_server_main(String files_dir, String apk_path, String out_port_file_path, ConnectivityManager connectivity_manager);
public static native int tf_sandbox_main(int pipe_fd); public static native int tf_sandbox_main(int pipe_fd);
public static void log(String message) {
if (s_activity != null && s_activity.log_queue != null && message != null) {
try {
s_activity.log_queue.put(message);
} catch (InterruptedException e) {
android.util.Log.w("tildefriends", message);
}
}
}
private void createThread() { private void createThread() {
web_view = (TildeFriendsWebView)findViewById(R.id.web); web_view = (TildeFriendsWebView)findViewById(R.id.web);
Log.w("tildefriends", String.format("getFilesDir() is %s", getFilesDir().toString())); log(String.format("getFilesDir() is %s", getFilesDir().toString()));
Log.w("tildefriends", String.format("getPackageResourcePath() is %s", getPackageResourcePath().toString())); log(String.format("getPackageResourcePath() is %s", getPackageResourcePath().toString()));
Log.w("tildefriends", String.format("nativeLibraryDir is %s", getApplicationInfo().nativeLibraryDir)); log(String.format("nativeLibraryDir is %s", getApplicationInfo().nativeLibraryDir));
port_file_path = getFilesDir().toString() + "/port.txt"; port_file_path = getFilesDir().toString() + "/port.txt";
new File(port_file_path).delete(); new File(port_file_path).delete();
@@ -86,17 +99,17 @@ public class TildeFriendsActivity extends Activity {
server_thread = new Thread(new Runnable() { server_thread = new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
Log.w("tildefriends", "Watching for changes in: " + getFilesDir().toString()); log("Watching for changes in: " + getFilesDir().toString());
observer = make_file_observer(getFilesDir().toString(), port_file_path); observer = make_file_observer(getFilesDir().toString(), port_file_path);
observer.startWatching(); observer.startWatching();
Log.w("tildefriends", "Calling tf_server_main."); log("Calling tf_server_main.");
int result = tf_server_main( int result = tf_server_main(
getFilesDir().toString(), getFilesDir().toString(),
getPackageResourcePath().toString(), getPackageResourcePath().toString(),
port_file_path, port_file_path,
(ConnectivityManager)getApplicationContext().getSystemService(CONNECTIVITY_SERVICE)); (ConnectivityManager)getApplicationContext().getSystemService(CONNECTIVITY_SERVICE));
Log.w("tildefriends", "tf_server_main returned " + result + "."); log("tf_server_main returned " + result + ".");
} }
}); });
server_thread.start(); server_thread.start();
@@ -110,17 +123,17 @@ public class TildeFriendsActivity extends Activity {
web_view.setDownloadListener(new DownloadListener() { web_view.setDownloadListener(new DownloadListener() {
public void onDownloadStart(String url, String userAgent, String content_disposition, String mime_type, long content_length) { public void onDownloadStart(String url, String userAgent, String content_disposition, String mime_type, long content_length) {
Log.w("tildefriends", "Let's download: " + url + " (" + content_disposition + ")"); log("Let's download: " + url + " (" + content_disposition + ")");
String file_name = URLUtil.guessFileName(url, content_disposition, mime_type); String file_name = URLUtil.guessFileName(url, content_disposition, mime_type);
if (url.startsWith("data:") && url.indexOf(',') != -1) { if (url.startsWith("data:") && url.indexOf(',') != -1) {
String b64 = url.substring(url.indexOf(',') + 1); String b64 = url.substring(url.indexOf(',') + 1);
byte[] data = Base64.decode(b64, Base64.DEFAULT); byte[] data = Base64.decode(b64, Base64.DEFAULT);
Log.w("tildefriends", "Downloaded " + data.length + " bytes."); log("Downloaded " + data.length + " bytes.");
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
try (OutputStream stream = new FileOutputStream(new File(path, file_name))) { try (OutputStream stream = new FileOutputStream(new File(path, file_name))) {
stream.write(data); stream.write(data);
} catch (java.io.IOException e) { } catch (java.io.IOException e) {
Log.w("tildefriends", "IOException: " + e.toString()); log("IOException: " + e.toString());
} }
Toast.makeText(getApplicationContext(), "Downloaded File", Toast.LENGTH_LONG).show(); Toast.makeText(getApplicationContext(), "Downloaded File", Toast.LENGTH_LONG).show();
} else { } else {
@@ -228,7 +241,7 @@ public class TildeFriendsActivity extends Activity {
@Override @Override
public boolean onConsoleMessage(android.webkit.ConsoleMessage consoleMessage) { public boolean onConsoleMessage(android.webkit.ConsoleMessage consoleMessage) {
Log.d("tildefriends", consoleMessage.message() + " -- From line " + consoleMessage.lineNumber() + " of " + consoleMessage.sourceId()); log(consoleMessage.message() + " -- From line " + consoleMessage.lineNumber() + " of " + consoleMessage.sourceId());
return true; return true;
} }
}); });
@@ -259,6 +272,23 @@ public class TildeFriendsActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
s_activity = this; s_activity = this;
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
log_thread = new Thread(new Runnable() {
@Override
public void run() {
while (!s_activity.shutting_down) {
try {
String message = log_queue.take();
if (message != null) {
android.util.Log.w("tildefriends", message);
} else {
break;
}
} catch (InterruptedException e) {
}
}
}
});
log_thread.start();
StrictMode.setThreadPolicy( StrictMode.setThreadPolicy(
new StrictMode.ThreadPolicy.Builder() new StrictMode.ThreadPolicy.Builder()
.detectAll() .detectAll()
@@ -305,8 +335,17 @@ public class TildeFriendsActivity extends Activity {
@Override @Override
protected void onDestroy() protected void onDestroy()
{ {
super.onDestroy(); try {
shutting_down = true;
if (log_queue != null) {
log_queue.put("Goodbye.");
}
log_thread.join();
} catch (InterruptedException e) {
}
log_thread = null;
s_activity = null; s_activity = null;
super.onDestroy();
} }
@Override @Override
@@ -399,32 +438,32 @@ public class TildeFriendsActivity extends Activity {
} }
public static void start_sandbox(int pipe_fd) { public static void start_sandbox(int pipe_fd) {
Log.w("tildefriends", "starting service with fd: " + pipe_fd); log("starting service with fd: " + pipe_fd);
Intent intent = new Intent(s_activity, TildeFriendsSandboxService.class); Intent intent = new Intent(s_activity, TildeFriendsSandboxService.class);
s_activity.service_connection = new ServiceConnection() { s_activity.service_connection = new ServiceConnection() {
@Override @Override
public void onBindingDied(ComponentName name) { public void onBindingDied(ComponentName name) {
Log.w("tildefriends", "onBindingDied"); log("onBindingDied");
} }
@Override @Override
public void onNullBinding(ComponentName name) { public void onNullBinding(ComponentName name) {
Log.w("tildefriends", "onNullBinding"); log("onNullBinding");
} }
@Override @Override
public void onServiceConnected(ComponentName name, IBinder binder) { public void onServiceConnected(ComponentName name, IBinder binder) {
Log.w("tildefriends", "onServiceConnected"); log("onServiceConnected");
Parcel data = Parcel.obtain(); Parcel data = Parcel.obtain();
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(pipe_fd)) { try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(pipe_fd)) {
data.writeParcelable(pfd, 0); data.writeParcelable(pfd, 0);
} catch (java.io.IOException e) { } catch (java.io.IOException e) {
Log.w("tildefriends", "IOException: " + e); log("IOException: " + e);
} }
try { try {
binder.transact(TildeFriendsSandboxService.START_CALL, data, null, IBinder.FLAG_ONEWAY); binder.transact(TildeFriendsSandboxService.START_CALL, data, null, IBinder.FLAG_ONEWAY);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.w("tildefriends", "RemoteException"); log("RemoteException");
} finally { } finally {
data.recycle(); data.recycle();
} }
@@ -432,14 +471,14 @@ public class TildeFriendsActivity extends Activity {
@Override @Override
public void onServiceDisconnected(ComponentName name) { public void onServiceDisconnected(ComponentName name) {
Log.w("tildefriends", "onServiceDisconnected"); log("onServiceDisconnected");
} }
}; };
s_activity.bindService(intent, s_activity.service_connection, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND); s_activity.bindService(intent, s_activity.service_connection, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND);
} }
public static void stop_sandbox() { public static void stop_sandbox() {
Log.w("tildefriends", "stop_sandbox"); log("stop_sandbox");
if (s_activity.service_connection != null) { if (s_activity.service_connection != null) {
s_activity.unbindService(s_activity.service_connection); s_activity.unbindService(s_activity.service_connection);
s_activity.service_connection = null; s_activity.service_connection = null;

View File

@@ -6,7 +6,6 @@ import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.os.Parcel; import android.os.Parcel;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.util.Log;
public class TildeFriendsSandboxService extends Service { public class TildeFriendsSandboxService extends Service {
public static final int START_CALL = IBinder.FIRST_CALL_TRANSACTION; public static final int START_CALL = IBinder.FIRST_CALL_TRANSACTION;
@@ -14,12 +13,12 @@ public class TildeFriendsSandboxService extends Service {
Thread thread; Thread thread;
public int onStartCommand(Intent intent, int flags, int start_id) { public int onStartCommand(Intent intent, int flags, int start_id) {
Log.w("tildefriends", "TildeFriendsSandboxService: onStartCommand"); TildeFriendsActivity.log("TildeFriendsSandboxService: onStartCommand");
return super.onStartCommand(intent, flags, start_id); return super.onStartCommand(intent, flags, start_id);
} }
public void onDestroy() { public void onDestroy() {
Log.w("tildefriends", "TildeFriendsSandboxService: onDestroy"); TildeFriendsActivity.log("TildeFriendsSandboxService: onDestroy");
super.onDestroy(); super.onDestroy();
} }
@@ -27,9 +26,9 @@ public class TildeFriendsSandboxService extends Service {
thread = new Thread(new Runnable() { thread = new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
Log.w("tildefriends", "Calling tf_sandbox_main."); TildeFriendsActivity.log("Calling tf_sandbox_main.");
int result = TildeFriendsActivity.tf_sandbox_main(pipe_fd); int result = TildeFriendsActivity.tf_sandbox_main(pipe_fd);
Log.w("tildefriends", "tf_sandbox_main returned " + result + "."); TildeFriendsActivity.log("tf_sandbox_main returned " + result + ".");
} }
}); });
thread.start(); thread.start();
@@ -43,7 +42,7 @@ public class TildeFriendsSandboxService extends Service {
if (code == START_CALL) { if (code == START_CALL) {
ParcelFileDescriptor pfd = read_pfd(data); ParcelFileDescriptor pfd = read_pfd(data);
if (pfd != null) { if (pfd != null) {
Log.w("tildefriends", "fd is " + pfd.getFd()); TildeFriendsActivity.log("fd is " + pfd.getFd());
start_thread(pfd.detachFd()); start_thread(pfd.detachFd());
try { try {
pfd.close(); pfd.close();

View File

@@ -147,12 +147,77 @@ static JSValue _tf_api_core_apps(JSContext* context, JSValueConst this_val, int
return result; return result;
} }
static JSValue _tf_api_core_register(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
{
JSValue event_name = argv[0];
JSValue handler = argv[1];
JSValue process = data[0];
JSValue event_handlers = JS_GetPropertyStr(context, process, "eventHandlers");
JSAtom atom = JS_ValueToAtom(context, event_name);
JSValue array = JS_GetProperty(context, event_handlers, atom);
if (!JS_IsArray(context, array))
{
JS_FreeValue(context, array);
array = JS_NewArray(context);
JS_SetProperty(context, event_handlers, atom, JS_DupValue(context, array));
}
JS_SetPropertyUint32(context, array, tf_util_get_length(context, array), JS_DupValue(context, handler));
JS_FreeValue(context, array);
JS_FreeAtom(context, atom);
JS_FreeValue(context, event_handlers);
return JS_UNDEFINED;
}
static JSValue _tf_api_core_unregister(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
{
JSValue event_name = argv[0];
JSValue handler = argv[1];
JSValue process = data[0];
JSValue event_handlers = JS_GetPropertyStr(context, process, "eventHandlers");
JSAtom atom = JS_ValueToAtom(context, event_name);
JSValue array = JS_GetProperty(context, event_handlers, atom);
if (JS_IsArray(context, array))
{
JSValue index_of = JS_GetPropertyStr(context, array, "indexOf");
JSValue index = JS_Call(context, index_of, array, 1, &handler);
int int_index = -1;
JS_ToInt32(context, &int_index, index);
if (int_index != -1)
{
JSValue splice = JS_GetPropertyStr(context, array, "splice");
JSValue splice_args[] = {
index,
JS_NewInt32(context, 1),
};
JSValue result = JS_Call(context, splice, array, 2, splice_args);
JS_FreeValue(context, result);
JS_FreeValue(context, splice);
}
JS_FreeValue(context, index);
JS_FreeValue(context, index_of);
if (tf_util_get_length(context, array) == 0)
{
JS_DeleteProperty(context, event_handlers, atom, 0);
}
}
JS_FreeValue(context, array);
JS_FreeAtom(context, atom);
JS_FreeValue(context, event_handlers);
return JS_UNDEFINED;
}
static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{ {
JSValue imports = argv[0]; JSValue imports = argv[0];
JSValue process = argv[1]; JSValue process = argv[1];
JSValue core = JS_GetPropertyStr(context, imports, "core"); JSValue core = JS_GetPropertyStr(context, imports, "core");
JS_SetPropertyStr(context, core, "apps", JS_NewCFunctionData(context, _tf_api_core_apps, 1, 0, 1, &process)); JS_SetPropertyStr(context, core, "apps", JS_NewCFunctionData(context, _tf_api_core_apps, 1, 0, 1, &process));
JS_SetPropertyStr(context, core, "register", JS_NewCFunctionData(context, _tf_api_core_register, 2, 0, 1, &process));
JS_SetPropertyStr(context, core, "unregister", JS_NewCFunctionData(context, _tf_api_core_unregister, 2, 0, 1, &process));
JS_FreeValue(context, core); JS_FreeValue(context, core);
return JS_UNDEFINED; return JS_UNDEFINED;
} }

View File

@@ -13,13 +13,13 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.2025.8</string> <string>0.2025.10</string>
<key>CFBundleSupportedPlatforms</key> <key>CFBundleSupportedPlatforms</key>
<array> <array>
<string>iPhoneOS</string> <string>iPhoneOS</string>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>16</string> <string>18</string>
<key>DTPlatformName</key> <key>DTPlatformName</key>
<string>iphoneos</string> <string>iphoneos</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>

View File

@@ -1578,28 +1578,28 @@ static void _shed_privileges()
if (setrlimit(RLIMIT_FSIZE, &zeroLimit) != 0) if (setrlimit(RLIMIT_FSIZE, &zeroLimit) != 0)
{ {
perror("setrlimit(RLIMIT_FSIZE, {0, 0})"); perror("setrlimit(RLIMIT_FSIZE, {0, 0})");
exit(-1); exit(EXIT_FAILURE);
} }
if (setrlimit(RLIMIT_NOFILE, &zeroLimit) != 0) if (setrlimit(RLIMIT_NOFILE, &zeroLimit) != 0)
{ {
perror("setrlimit(RLIMIT_NOFILE, {0, 0})"); perror("setrlimit(RLIMIT_NOFILE, {0, 0})");
exit(-1); exit(EXIT_FAILURE);
} }
if (setrlimit(RLIMIT_NPROC, &zeroLimit) != 0) if (setrlimit(RLIMIT_NPROC, &zeroLimit) != 0)
{ {
perror("setrlimit(RLIMIT_NPROC, {0, 0})"); perror("setrlimit(RLIMIT_NPROC, {0, 0})");
exit(-1); exit(EXIT_FAILURE);
} }
#if !defined(__MACH__) && !defined(__OpenBSD__) #if !defined(__MACH__) && !defined(__OpenBSD__)
if (setrlimit(RLIMIT_LOCKS, &zeroLimit) != 0) if (setrlimit(RLIMIT_LOCKS, &zeroLimit) != 0)
{ {
perror("setrlimit(RLIMIT_LOCKS, {0, 0})"); perror("setrlimit(RLIMIT_LOCKS, {0, 0})");
exit(-1); exit(EXIT_FAILURE);
} }
if (setrlimit(RLIMIT_MSGQUEUE, &zeroLimit) != 0) if (setrlimit(RLIMIT_MSGQUEUE, &zeroLimit) != 0)
{ {
perror("setrlimit(RLIMIT_MSGQUEUE, {0, 0})"); perror("setrlimit(RLIMIT_MSGQUEUE, {0, 0})");
exit(-1); exit(EXIT_FAILURE);
} }
#endif #endif
#endif #endif
@@ -1609,12 +1609,12 @@ static void _shed_privileges()
if (unveil("/dev/null", "r") || unveil(NULL, NULL)) if (unveil("/dev/null", "r") || unveil(NULL, NULL))
{ {
perror("unveil"); perror("unveil");
exit(-1); exit(EXIT_FAILURE);
} }
if (pledge("stdio unveil", NULL)) if (pledge("stdio unveil", NULL))
{ {
perror("pledge"); perror("pledge");
exit(-1); exit(EXIT_FAILURE);
} }
#endif #endif
} }
@@ -1831,7 +1831,7 @@ static void _error_handler(int sig)
const char* stack = tf_util_backtrace_string(); const char* stack = tf_util_backtrace_string();
tf_printf("ERROR:\n%s\n", stack); tf_printf("ERROR:\n%s\n", stack);
tf_free((void*)stack); tf_free((void*)stack);
_exit(1); _exit(EXIT_FAILURE);
} }
#if defined(_WIN32) #if defined(_WIN32)
@@ -1843,7 +1843,7 @@ static LONG WINAPI _win32_exception_handler(EXCEPTION_POINTERS* info)
const char* stack = tf_util_backtrace_string(); const char* stack = tf_util_backtrace_string();
tf_printf("ERROR:\n%s\n", stack); tf_printf("ERROR:\n%s\n", stack);
tf_free((void*)stack); tf_free((void*)stack);
_exit(1); _exit(EXIT_FAILURE);
} }
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }

View File

@@ -421,6 +421,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
_tf_ssb_db_exec(db, "COMMIT TRANSACTION"); _tf_ssb_db_exec(db, "COMMIT TRANSACTION");
tf_printf("Done.\n"); tf_printf("Done.\n");
} }
_tf_ssb_db_exec(db, "DELETE FROM blob_wants_cache WHERE blob_wants_cache.id IN (SELECT blobs.id FROM blobs)");
if (!_tf_ssb_db_has_rows(db, "SELECT * FROM sqlite_schema WHERE type = 'index' AND name = 'blob_wants_cache_source_id_unique_index'")) if (!_tf_ssb_db_has_rows(db, "SELECT * FROM sqlite_schema WHERE type = 'index' AND name = 'blob_wants_cache_source_id_unique_index'"))
{ {
tf_printf("Creating blob_wants_cache UNIQUE constraint.\n"); tf_printf("Creating blob_wants_cache UNIQUE constraint.\n");
@@ -436,8 +437,11 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
_tf_ssb_db_exec(db, _tf_ssb_db_exec(db,
"CREATE TRIGGER IF NOT EXISTS messages_ai_blob_wants_cache AFTER INSERT ON messages_refs BEGIN " "CREATE TRIGGER IF NOT EXISTS messages_ai_blob_wants_cache AFTER INSERT ON messages_refs BEGIN "
"INSERT INTO blob_wants_cache (source, id, timestamp) " "INSERT INTO blob_wants_cache (source, id, timestamp) "
"SELECT messages.id, new.ref, messages.timestamp FROM messages WHERE messages.id = new.message AND " "SELECT messages.id, new.ref, messages.timestamp FROM messages "
"LENGTH(new.ref) = 52 AND new.ref LIKE '&%.sha256' " "JOIN blobs ON new.ref = blobs.id "
"WHERE messages.id = new.message AND "
"LENGTH(new.ref) = 52 AND new.ref LIKE '&%.sha256' AND "
"blobs.content IS NULL "
"ON CONFLICT (source, id) DO NOTHING; END"); "ON CONFLICT (source, id) DO NOTHING; END");
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS blobs_refs_ai_blob_wants_cache"); _tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS blobs_refs_ai_blob_wants_cache");
_tf_ssb_db_exec(db, _tf_ssb_db_exec(db,
@@ -445,6 +449,8 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
"INSERT INTO blob_wants_cache (source, id, timestamp) " "INSERT INTO blob_wants_cache (source, id, timestamp) "
"SELECT messages.id, new.ref, messages.timestamp FROM messages " "SELECT messages.id, new.ref, messages.timestamp FROM messages "
"JOIN blob_wants_cache bwc ON bwc.source = messages.id AND bwc.id = new.blob " "JOIN blob_wants_cache bwc ON bwc.source = messages.id AND bwc.id = new.blob "
"JOIN blobs ON bwc.id = blobs.id "
"WHERE blobs.content IS NULL "
"ON CONFLICT (source, id) DO NOTHING; END"); "ON CONFLICT (source, id) DO NOTHING; END");
_tf_ssb_db_exec(db, _tf_ssb_db_exec(db,
"CREATE TRIGGER IF NOT EXISTS messages_ad_blob_wants_cache AFTER DELETE ON messages BEGIN " "CREATE TRIGGER IF NOT EXISTS messages_ad_blob_wants_cache AFTER DELETE ON messages BEGIN "
@@ -574,16 +580,15 @@ static int64_t _tf_ssb_db_store_message_raw(sqlite3* db, const char* id, const c
return last_row_id; return last_row_id;
} }
static char* _tf_ssb_db_get_message_blob_wants(tf_ssb_t* ssb, int64_t rowid) static char* _tf_ssb_db_get_message_blob_wants(sqlite3* db, int64_t rowid)
{ {
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
sqlite3_stmt* statement; sqlite3_stmt* statement;
char* result = NULL; char* result = NULL;
size_t size = 0; size_t size = 0;
if (sqlite3_prepare_v2(db, if (sqlite3_prepare_v2(db,
"SELECT DISTINCT json.value FROM messages, json_tree(messages.content) AS json LEFT OUTER JOIN blobs ON json.value = blobs.id WHERE messages.rowid = ?1 AND " "SELECT DISTINCT json.value FROM messages, json_tree(messages.content) AS json LEFT OUTER JOIN blobs ON json.value = blobs.id WHERE messages.rowid = ?1 AND "
"json.value LIKE '&%.sha256' AND length(json.value) = ?2 AND blobs.content IS NULL", "length(json.value) = ?2 AND json.value LIKE '&%.sha256' AND blobs.content IS NULL",
-1, &statement, NULL) == SQLITE_OK) -1, &statement, NULL) == SQLITE_OK)
{ {
if (sqlite3_bind_int64(statement, 1, rowid) == SQLITE_OK && sqlite3_bind_int(statement, 2, k_blob_id_len - 1) == SQLITE_OK) if (sqlite3_bind_int64(statement, 1, rowid) == SQLITE_OK && sqlite3_bind_int(statement, 2, k_blob_id_len - 1) == SQLITE_OK)
@@ -615,7 +620,6 @@ static char* _tf_ssb_db_get_message_blob_wants(tf_ssb_t* ssb, int64_t rowid)
result = tf_realloc(result, size + 1); result = tf_realloc(result, size + 1);
result[size] = '\0'; result[size] = '\0';
tf_ssb_release_db_reader(ssb, db);
return result; return result;
} }
@@ -653,7 +657,7 @@ static void _tf_ssb_db_store_message_work(tf_ssb_t* ssb, void* user_data)
if (last_row_id != -1) if (last_row_id != -1)
{ {
store->out_stored = true; store->out_stored = true;
store->out_blob_wants = _tf_ssb_db_get_message_blob_wants(ssb, last_row_id); store->out_blob_wants = _tf_ssb_db_get_message_blob_wants(db, last_row_id);
} }
store = store->next; store = store->next;
} }
@@ -972,7 +976,7 @@ static void _tf_ssb_db_blob_store_work(tf_ssb_t* ssb, void* user_data)
static void _tf_ssb_db_blob_store_after_work(tf_ssb_t* ssb, int status, void* user_data) static void _tf_ssb_db_blob_store_after_work(tf_ssb_t* ssb, int status, void* user_data)
{ {
blob_store_work_t* blob_work = user_data; blob_store_work_t* blob_work = user_data;
if (status == 0 && *blob_work->id) if (status == 0 && *blob_work->id && blob_work->is_new)
{ {
tf_ssb_notify_blob_stored(ssb, blob_work->id); tf_ssb_notify_blob_stored(ssb, blob_work->id);
} }

View File

@@ -1128,6 +1128,11 @@ void tf_ssb_test_replicate(const tf_test_options_t* options)
tf_printf("%s user %d = %s private=%s\n", added ? "added" : "failed", i, public[i], private[i]); tf_printf("%s user %d = %s private=%s\n", added ? "added" : "failed", i, public[i], private[i]);
} }
char blob_id[k_id_base64_len] = { 0 };
const char* k_blob = "Hello, new blob!";
b = tf_ssb_db_blob_store(ssb0, (const uint8_t*)k_blob, strlen(k_blob), blob_id, sizeof(blob_id), NULL);
assert(b);
JSContext* context0 = tf_ssb_get_context(ssb0); JSContext* context0 = tf_ssb_get_context(ssb0);
for (int i = 0; i < k_key_count - 1; i++) for (int i = 0; i < k_key_count - 1; i++)
{ {
@@ -1152,6 +1157,7 @@ void tf_ssb_test_replicate(const tf_test_options_t* options)
obj = JS_NewObject(context0); obj = JS_NewObject(context0);
JS_SetPropertyStr(context0, obj, "type", JS_NewString(context0, "post")); JS_SetPropertyStr(context0, obj, "type", JS_NewString(context0, "post"));
JS_SetPropertyStr(context0, obj, "text", JS_NewString(context0, "Hello, world!")); JS_SetPropertyStr(context0, obj, "text", JS_NewString(context0, "Hello, world!"));
JS_SetPropertyStr(context0, obj, "arbitrary_reference", JS_NewString(context0, blob_id));
stored = false; stored = false;
signed_message = tf_ssb_sign_message(ssb0, self, private_bin, obj, NULL, 0); signed_message = tf_ssb_sign_message(ssb0, self, private_bin, obj, NULL, 0);
tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored); tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored);
@@ -1213,6 +1219,15 @@ void tf_ssb_test_replicate(const tf_test_options_t* options)
tf_ssb_remove_message_added_callback(ssb1, _message_added, &count1); tf_ssb_remove_message_added_callback(ssb1, _message_added, &count1);
tf_printf("done\n"); tf_printf("done\n");
tf_printf("Waiting for blob.\n");
while (!tf_ssb_db_blob_get(ssb0, blob_id, NULL, NULL))
{
tf_ssb_set_main_thread(ssb1, true);
uv_run(&loop, UV_RUN_ONCE);
tf_ssb_set_main_thread(ssb1, false);
}
tf_printf("done\n");
tf_ssb_send_close(ssb1); tf_ssb_send_close(ssb1);
uv_close((uv_handle_t*)&idle0, NULL); uv_close((uv_handle_t*)&idle0, NULL);

View File

@@ -1101,7 +1101,7 @@ void tf_task_on_receive_packet(int packetType, const char* begin, size_t length,
} }
else else
{ {
exit(1); exit(EXIT_FAILURE);
} }
break; break;
case kSetImports: case kSetImports:
@@ -1699,7 +1699,7 @@ void tf_task_activate(tf_task_t* task)
else else
{ {
tf_printf("Assignment missing '=': %s.\n", assignment); tf_printf("Assignment missing '=': %s.\n", assignment);
exit(1); exit(EXIT_FAILURE);
} }
} }
tf_free(copy); tf_free(copy);

View File

@@ -1,2 +1,2 @@
#define VERSION_NUMBER "0.2025.8" #define VERSION_NUMBER "0.2025.10-wip"
#define VERSION_NAME "This program kills fascists." #define VERSION_NAME "This program kills fascists."