more conflict-resistant comment storage

Until now, comments were stored in filenames with an incrementing
counter. As a result, conflicts are easy; two people commenting on a
post around the same time would create the same filename. Then they'd
run into conflicts when merging.

Now comment filenames include a random slug.

A secondary consideration is filtering out comments from the file-picker
screen. It's only intended for top-level posts. I had a hacky way to do
this. My ideal approach would be a directory per post, but Lua+LÖVE
doesn't have a portable way to create directories.

Now you now have to manually create a single directory called
`comments/` in the directory of articles before using pothi.love.
This commit is contained in:
Kartik K. Agaram 2023-07-16 13:45:30 -07:00
parent e0d017867b
commit 8cfc7e3e10
10 changed files with 48 additions and 14 deletions

View File

@ -2,6 +2,8 @@ load_metadata = function(filename)
local mfile = metadata_file(filename)
local mpath = save_dir_path(mfile)
if not love.filesystem.getInfo(mpath) then
-- comments will always have metadata,
-- so we only get here for top-level posts with no parent
return {replies={}}
end
return json.decode(love.filesystem.read(mpath))

View File

@ -1,7 +1,13 @@
initialize_item = function(id, depth)
local path
if depth == 0 then
path = full_path(id)
else
path = comment_path(id)
end
local result = {
type='text',
id=id, filename=full_path(id),
id=id, filename=path,
width=Width, depth=depth,
metadata=load_metadata(id),
border=Border_color,

View File

@ -1,7 +1,7 @@
initialize_file_picker = function()
Files = love.filesystem.getDirectoryItems('data')
for i=#Files,1,-1 do
if (not Files[i]:match('%.md$')) or Files[i]:match('%-%d+.md$') then
if not Files[i]:match('%.md$') then
table.remove(Files, i)
end
end
@ -16,4 +16,4 @@ initialize_file_picker = function()
border={r=0.4, g=0.4, b=0.7}
})
end
end
end

View File

@ -1,14 +1,14 @@
new_comment = function(parent_id, depth)
local id = next_comment_id(parent_id)
local id = new_comment_id(parent_id)
print('creating', id)
local comment = {
type='text',
id=id,
filename=full_path(id),
filename=comment_path(id),
data={{data=''}},
width=Width,
depth=depth+1,
metadata={replies={}},
metadata={parent=parent_id, replies={}},
border=Border_color,
}
save_metadata(comment)
@ -20,4 +20,4 @@ new_comment = function(parent_id, depth)
table.insert(item_stuff.data,
reply_button(comment.id, depth+1))
return result, id
end
end

12
0144-new_comment_id Normal file
View File

@ -0,0 +1,12 @@
new_comment_id = function(id)
local corename = id:gsub('%.md$', '')
-- there'll be a collision on avarage every #Random_string_chars^(n/2) = 1296 comments
local n = 4
local result = ('%s-%s.md'):format(corename, random_string(4))
if file_exists(comment_path(result)) then
-- retry
-- This will infinite loop every #Random_string_chars^n = 1.6M, but it'll start taking a long time by halfway as long.
return new_comment_id(id)
end
return result
end

View File

@ -1,5 +0,0 @@
next_comment_id = function(id)
local num_replies = #load_metadata(id).replies
local corename = id:gsub('%.md$', '')
return ('%s-%d.md'):format(corename, num_replies)
end

View File

@ -1,6 +1,11 @@
save_metadata = function(node)
--print('saving metadata for', node.id)
local mfile = metadata_file(node.id)
local mpath = save_dir_path(mfile)
local mpath
if node.metadata.parent then
mpath = save_dir_path('comments/'..mfile)
else
mpath = save_dir_path(mfile)
end
love.filesystem.write(mpath, json.encode(node.metadata))
end
end

1
0149-Random_string_chars Normal file
View File

@ -0,0 +1 @@
Random_string_chars = 'abcdefghijklmnopqrstuvwxyz0123456789'

10
0150-random_string Normal file
View File

@ -0,0 +1,10 @@
random_string = function(n)
-- generate a random string of length n
local result = {}
local nchars = #Random_string_chars
for i=1,n do
local idx = math.random(1, nchars)
table.insert(result, Random_string_chars:sub(idx, idx))
end
return table.concat(result)
end

3
0151-comment_path Normal file
View File

@ -0,0 +1,3 @@
comment_path = function(id)
return full_path('comments/'..id)
end